Python SDK
The same SDK, in Python. Standard library only, thread-safe, and it passes the same conformance file as the JavaScript SDK line for line.
Install
$ pip install flagrship-sdkPython 3.9 or later. No dependencies: urllib for HTTP and threading for the poll loop.
Usage
1import os2from flagrship import Flagrship3 4flags = Flagrship(5 api_key=os.environ["FLAGRSHIP_KEY"],6 defaults={"new-checkout": False},7)8 9# Blocks until the first sync attempt finishes. Never raises.10flags.ready()11 12if flags.is_enabled("new-checkout", user.id):13 show_new_checkout()Or as a context manager, which waits for ready() on entry and stops polling on exit:
with Flagrship(api_key=key) as flags: if flags.is_enabled("new-checkout", user.id): show_new_checkout()Constructor arguments
api_key is positional; everything else is keyword-only.
| Argument | Type | Default | What it does |
|---|---|---|---|
| api_key | str | required | A read-scoped key for one environment. |
| api_url | str | https://api.flagrship.dev | Override for self-hosted or local APIs. |
| poll_interval | float | 30.0 | Seconds between polls. 0 disables polling. |
| defaults | Mapping[str, bool] | {} | What is_enabled returns for unknown flags. |
| on_update | Callable[[dict], None] | None | Called after every successful config change. |
| on_error | Callable[[Exception], None] | None | Called when a sync fails. Last config keeps serving. |
| transport | Callable | urllib | Injectable for tests. See the source for the signature. |
Methods
ready(timeout=None)
Blocks until the first sync attempt finishes. Returns False only if timeout elapsed first. Never raises. The first sync runs on a background thread, so construction itself never blocks.
is_enabled(flag_key, user_id=None, default=None)
Same five-step evaluation as the JavaScript SDK:
- Unknown flag →
default, thendefaults[flag_key], thenFalse - Disabled →
False - Rollout 100 →
True - No user ID and rollout below 100 →
False - Otherwise
bucket(flag_key, user_id) < rollout_percentage
get_flag(flag_key)
A frozen FlagConfig dataclass, or None. Fields: key, enabled, rollout_percentage, targeting_rules.
all_flags()
A snapshot dict. Safe to iterate while polling continues.
refresh()
Sync now. Returns True if config changed. Never raises.
close()
Stop the poll thread. The last config stays in memory.
Threads
Evaluation is lock-free. Each successful sync replaces the whole config dict in a single assignment, which is atomic in CPython, so a reader on any thread sees either the old snapshot or the new one and never a partially written mix. A lock serialises writers only (the poll thread versus an explicit refresh()).
Both background threads are daemon threads. Your process exits when your code finishes; the SDK never keeps it alive.
Bucketing
from flagrship import bucket, murmurhash3_x86_32 bucket("new-checkout", "alice") # 40murmurhash3_x86_32("hello") # 613153351These return exactly what the JavaScript SDK returns for the same input. Both implementations are checked against the same fixture file on every commit.