Skip to main content

Python SDK

The Cuttlegate Python SDK evaluates feature flags in your Python services. It requires Python 3.11 or later.

Install

pip install cuttlegate

Quick start

import os
from cuttlegate import CuttlegateClient, CuttlegateConfig, EvalContext

client = CuttlegateClient(CuttlegateConfig(
api_key=os.environ["CUTTLEGATE_API_KEY"],
server_url="https://flags.example.com",
project="my-project",
environment="production",
))

ctx = EvalContext(user_id="user-123", attributes={"plan": "pro"})

# Boolean flag
enabled = client.bool("dark-mode", ctx)
print("dark-mode enabled:", enabled)

# String variant
result = client.evaluate("banner-text", ctx)
print("variant:", result.variant) # e.g. "holiday"
print("reason:", result.reason) # e.g. "rule_match"

CuttlegateClient validates config immediately and raises ConfigError on any missing field. No network call is made at construction time.

Evaluation methods

MethodReturnsNotes
bool(key, ctx)boolTrue if variant is "true"
string(key, ctx)strReturns the variant string
evaluate(key, ctx)EvalResultFull result with variant, reason, enabled
evaluate_all(ctx)dict[str, EvalResult]All flags in one HTTP round trip

evaluate_all is the most efficient option when you need multiple flags — one HTTP request regardless of how many flags exist.

EvalResult fields

FieldTypeNotes
keystrFlag key
enabledboolWhether the flag is enabled for this context
variantstrPrimary field. "true" or "false" for bool flags; variant key for all others.
reasonstr"rule_match", "default", "disabled", or "percentage_rollout"
evaluated_atstrISO 8601 evaluation timestamp
valuestrDeprecated. Use variant instead.

Real-time streaming

connect_stream opens a background SSE connection for live flag changes:

from cuttlegate import connect_stream, CuttlegateConfig, FlagChangeEvent

def on_change(event: FlagChangeEvent) -> None:
print(f"flag {event.flag_key} changed: enabled={event.enabled}")

stream = connect_stream(config, on_change=on_change, on_error=print)

# ... application runs ...

stream.close()

The SDK reconnects automatically on transient failures (5xx, network errors) with exponential backoff. Auth errors (401/403) are terminal.

Production use — CachedClient

For hot request paths, use CachedClient. It seeds an in-memory cache on construction and keeps it fresh via a background SSE connection:

from cuttlegate import CachedClient, CuttlegateConfig, EvalContext

cache = CachedClient(config)

ctx = EvalContext(user_id="user-123")
enabled = cache.bool("dark-mode", ctx) # cache hit — no HTTP call

Offline persistence (FlagStore)

By default, the cache lives only in memory. If the server is down at construction time, the constructor raises. To survive restarts, provide a FlagStore:

from cuttlegate import CachedClient, CuttlegateConfig, EvalResult, FlagStore

class RedisFlagStore:
"""Example FlagStore backed by Redis."""

def save(self, flags: dict[str, EvalResult]) -> None:
redis.set("cg-flags", serialize(flags))

def load(self) -> dict[str, EvalResult]:
raw = redis.get("cg-flags")
return deserialize(raw) if raw else {}

cache = CachedClient(config, store=RedisFlagStore())

When store is set:

  • save is called after successful bootstrap and on every SSE update.
  • load is called when bootstrap fails (server unreachable). If it returns a non-empty dict, the cache is seeded from it and the SSE thread starts normally.
  • Auth errors never fall back to the store.

The SDK ships with NoopFlagStore (the default — no persistence).

Testing

Use MockCuttlegateClient for unit tests without a live server:

from cuttlegate import MockCuttlegateClient, EvalContext

mock = MockCuttlegateClient(flags={"dark-mode": True})
ctx = EvalContext(user_id="u1")
assert mock.bool("dark-mode", ctx) is True
mock.assert_evaluated("dark-mode")

Error handling

All errors are typed — catch specific classes:

from cuttlegate import AuthError, FlagNotFoundError, ServerError, SDKError

try:
result = client.evaluate("my-flag", ctx)
except FlagNotFoundError as exc:
print(f"flag {exc.key!r} not found")
except AuthError as exc:
print(f"auth failed: HTTP {exc.status_code}")
except SDKError as exc:
print(f"sdk error: {exc}")
ErrorWhen raised
ConfigErrorMissing or invalid config field
AuthError401 or 403 from server
FlagNotFoundErrorKey absent from response
ServerError5xx from server
InvalidResponseErrorMalformed JSON or SSE event

Configuration

FieldTypeRequiredDefaultDescription
api_keystryesAPI key (cg_...)
server_urlstryesCuttlegate server URL
projectstryesProject slug
environmentstryesEnvironment slug
timeout_msintno10000Request timeout in milliseconds

For the full API reference and additional examples (Flask integration, async client, OpenFeature provider), see the SDK README.