Advanced
For logged-out traffic you need a stable unit so a fractional rollout buckets the same on the server and in the browser. The middleware mints a first-party…
Generated from the SDK's own /docs/ — also served raw at
https://shipeasy-ai.github.io/sdk-python/pages/advanced.md.
Anonymous-id bucketing + middleware
For logged-out traffic you need a stable unit so a fractional rollout buckets
the same on the server and in the browser. The middleware mints a first-party
__se_anon_id cookie (shared with every Shipeasy SDK) for any request without
one; evaluations then default to it as anonymous_id, so get_flag on an
anonymous request just works — no per-call wiring.
# WSGI (Flask, Django, ...)
from shipeasy.middleware import AnonIdMiddleware
app.wsgi_app = AnonIdMiddleware(app.wsgi_app)
# ASGI (FastAPI, Starlette)
from shipeasy.middleware import AnonIdASGIMiddleware
app.add_middleware(AnonIdASGIMiddleware)# logged-out request → buckets on the __se_anon_id cookie automatically
client = shipeasy.Client({})
client.get_flag("new_checkout")An explicit user_id/anonymous_id always wins. The id is also on the request
(environ["shipeasy.anon_id"]). The cookie is non-HttpOnly by design so the
browser SDK buckets identically; a request with no unit still resolves a
fully-rolled (100%) gate as on. Cookie name + format are a cross-SDK contract.
Private attributes
Pass private_attributes to configure() to strip the named
keys from every outbound event properties bag before it POSTs to /collect
(LD/Statsig privateAttributes). The server evaluates locally, so private attrs
still drive targeting — they just never leave the process on the telemetry
path:
shipeasy.configure(api_key="sdk_server_...", private_attributes=["email", "ssn"])Sticky bucketing
Pass a sticky_store to configure() to pin a user's experiment assignment
across allocation changes. InMemoryStickyStore is built in; implement the
StickyBucketStore protocol (get(unit) / set(unit, exp, entry)) for a
durable backend:
from shipeasy import InMemoryStickyStore
shipeasy.configure(api_key="sdk_server_...", sticky_store=InMemoryStickyStore())Absent a store, bucketing is deterministic (MurmurHash3 over the unit).
Bucketing unit (bucketBy)
The bucketing unit per experiment is server-driven: an experiment can be
configured to bucket on a non-default attribute (e.g. company_id) in the
dashboard, and the SDK reads it from the experiment definition
(exp.bucketBy) — falling back to user_id then anonymous_id. Make sure that
attribute is present in the user map you pass.
Manual exposure — log_exposure
The server is stateless and never auto-logs exposures. Call log_exposure at the
real decision point (when you actually present the treatment) for parity with the
browser's auto-exposure. It re-evaluates the experiment and, if the user is
enrolled, POSTs a single exposure event — on the same bound Client, no user
argument:
# construct once per callsite (cheap; binds the user)
client = shipeasy.Client(current_user)
client.log_exposure("checkout_button")No-op in test mode or when the user isn't enrolled.
Server-side rendering (SSR)
Emit the request's evaluated flags as a declarative <script> tag so the
browser SDK has them on first paint. shipeasy.bootstrap_script_tag carries the
payload in data-* attributes (no key); the static se-bootstrap.js loader
hydrates window.__SE_BOOTSTRAP and writes the __se_anon_id cookie so the
browser buckets identically to the server. Both helpers delegate to the engine
configured via configure() — you never touch it directly.
import shipeasy
user = {"user_id": "u_123"}
# Two tags for the document <head>. The PUBLIC client key (not the server key)
# goes on the i18n loader tag.
head = shipeasy.bootstrap_script_tag(user, anon_id=anon_id) \
+ shipeasy.i18n_script_tag(client_key, "en:prod")bootstrap_script_tag also accepts i18n_profile= and base_url=.
OpenFeature provider
The Python SDK ships an OpenFeature server provider, shipeasy.openfeature.ShipeasyProvider, so apps standardised on the CNCF OpenFeature API can plug…
Admin API client (optional) — `shipeasy.admin`
The base SDK evaluates flags, configs, and experiments (configure() + shipeasy.Client(user)). The Admin API client is a separate, optional surface for…