Shipeasy
ReferencePython

Agent skill

Installable LLM skill for the Python SDK (configure() + Client(user), evaluate, experiment + track, testing).

An installable agent skill for the Python SDK. Fetch it with shipeasy docs skill --sdk python (--install writes it to your agent skills dir), or copy it below — the YAML frontmatter installs with it.

---
name: shipeasy-python
description: Use Shipeasy (feature flags, configs, kill switches, A/B experiments, i18n) from Python. Covers configure() + Client(user), get_flag/get_config/get_experiment, track, testing, OpenFeature.
---

# Shipeasy Python SDK

Server SDK (`pip install shipeasy`, `import shipeasy`). Server-key only — never
embed in a browser. Requires Python 3.9+.

Two things only: **`configure()`** once at startup, then **`shipeasy.Client(user)`**
per request.

> **Pulling deeper docs.** Each section below links its full reference page and
> copy-paste snippets — fetch any of them as raw Markdown when you need more than
> this summary. Discover the whole tree from the manifest:
> `https://shipeasy-ai.github.io/sdk-python/manifest.json` (lists every
> `pages/<key>.md` and `snippets/<group>/<leaf>.md`). All URLs below are
> `https://shipeasy-ai.github.io/sdk-python/…`.

## Configure once

```python
import shipeasy

shipeasy.configure(
    api_key="sdk_server_...",
    attributes=lambda u: {"user_id": u.id, "country": u.country, "plan": u.plan},
)
```

Omit `attributes` if your user object is already the attribute map. For a
long-running server pass `poll=True` to keep the blob fresh in the background.

**Django:** add `"shipeasy.django"` to `INSTALLED_APPS`, run
`python manage.py shipeasy_install` (idempotently wires the anon-id middleware +
a `SHIPEASY = {...}` settings block + `.env`), then set `SHIPEASY["SERVER_KEY"]`.
The app's AppConfig calls `configure()` from that dict at boot — no manual
`configure()` call needed.

→ More: `pages/installation.md` (per-framework setup, incl. the Django app),
`pages/configuration.md` (every option).

## Evaluate (bound `Client(user)`)

Bind the user once per request, then call without re-passing it — `track` and
`log_exposure` are on the bound client too, so experiments are end-to-end here:

```python
client = shipeasy.Client(current_user)

client.get_flag("new_checkout")                 # bool; default= only on un-evaluable
client.get_config("billing_copy", default={})   # typed JSON; default= on absent
client.get_killswitch("payments_breaker")       # bool kill switch

result = client.get_experiment("checkout_button", default_params={"color": "blue"})
result.in_experiment, result.group, result.params

client.log_exposure("checkout_button")          # at the decision point
client.track("purchase", {"amount": 49})        # conversion / metric event
```

`get_flag_detail` returns `FlagDetail(value, reason)` (reasons: `RULE_MATCH`,
`DEFAULT`, `OFF`, `OVERRIDE`, `FLAG_NOT_FOUND`, `CLIENT_NOT_READY`).

→ More: pages `pages/flags.md` · `pages/configs.md` · `pages/killswitches.md`
(incl. named switches) · `pages/experiments.md`. Snippets
`snippets/release/{flags,configs,killswitches,experiments}.md` and
`snippets/metrics/track.md` (event tracking).

## Testing (no network)

Use the `configure()` siblings — seed overrides, read through the same `Client`:

```python
shipeasy.configure_for_testing(
    flags={"new_checkout": True},
    configs={"billing_copy": {"title": "Welcome"}},
    experiments={"checkout_button": ("treatment", {"color": "green"})},
)
assert shipeasy.Client({"user_id": "u_123"}).get_flag("new_checkout") is True

# flip a value on the spot, mid-test:
shipeasy.override_flag("new_checkout", False)
shipeasy.clear_overrides()
```

Offline (real rules from a snapshot / file):

```python
shipeasy.configure_for_offline(path="snapshot.json")
# or snapshot={"flags": {...}, "experiments": {...}}, plus optional overrides
```

→ More: `pages/testing.md` (override helpers + a working example
`shipeasy-snapshot.json`).

## OpenFeature

```python
import shipeasy                                   # pip install "shipeasy[openfeature]"
from openfeature import api
from shipeasy.openfeature import ShipeasyProvider

shipeasy.configure(api_key="sdk_server_...", poll=True)
api.set_provider(ShipeasyProvider())             # uses the configured global
```

Boolean → gate; string/int/float/object → config.

→ More: `pages/openfeature.md` (reason mapping, type routing).

## Error reporting — see()

```python
from shipeasy import see
try:
    charge(order)
except PaymentError as e:
    see(e).causes_the("checkout").to("use the backup processor")
```

→ More: `pages/error-reporting.md` · snippets `snippets/ops/see.md`
(`.extras()`, violations, control-flow exceptions).

## Other surfaces

- Anon bucketing: `AnonIdMiddleware` (WSGI) / `AnonIdASGIMiddleware` (ASGI) mint
  the shared `__se_anon_id` cookie; anonymous `get_flag` then just works.
- `configure(private_attributes=[...])` strips keys from outbound events.
- `configure(sticky_store=InMemoryStickyStore())` pins experiment assignment.
- `client.log_exposure(exp_name)` for manual exposure.
- SSR: `shipeasy.bootstrap_script_tag(user)` + `shipeasy.i18n_script_tag(client_key, "en:prod")`.

→ More: `pages/advanced.md`.

## i18n

No server-side `t()`. The browser's Shipeasy **client** SDK renders labels; this
server SDK only emits the loader tag (public client key) during SSR.

→ More: `pages/i18n.md` · snippets `snippets/i18n/setup.md`.
Was this page helpful?
✎ Edit this page