Lightweight, production-ready feature flags SDK for Python (3.10+).
- O(1) local evaluation — thread-safe reads from an in-memory snapshot
- Non-blocking startup with persistent disk cache (no dependency on a live API at init)
- Real-time updates via SSE (Server-Sent Events)
- Persistent disk cache for instant cold starts
- Graceful offline behavior and resilient reconnect
- Zero runtime dependencies
Most feature flag platforms are built for enterprises. SOASAP focuses on:
- low-latency local evaluation
- minimal dependencies
- zero startup impact
- resilient offline behavior
- simple integration with Python applications
pip install soasapFor local development:
pip install -e ".[dev]"import os
from soasap import create_soasap_client
flags = create_soasap_client(
api_key=os.environ["SOASAP_API_KEY"],
preload=True,
)
# Sync reads — never throw, never hit the network
if flags.get_bool("new-checkout"):
print("New checkout enabled")SOASAP uses a non-blocking background architecture so feature flagging never blocks your application startup.
flags = create_soasap_client(
api_key=os.environ["SOASAP_API_KEY"],
preload=True,
)- Cold start: loads the last snapshot from disk; background SSE connects without blocking init.
- Non-blocking: network I/O runs in a daemon thread; your server keeps booting even if the API is down.
flags = create_soasap_client(api_key=os.environ["SOASAP_API_KEY"])Background synchronization starts on the first flag read. Until then you get disk cache or defaults.
flags.get_bool("feature-x")
flags.get_number("rate-limit", 100)
flags.get_string("ui-theme", "light")
flags.get_json("checkout-config", {"enableUpsells": False, "maxItems": 10})JSON keys from the dashboard are returned as stored (typically camelCase). Map to your types accordingly.
- Immutable snapshots: readers never see partial updates.
- Memory cap: SSE parser enforces a 5 MB payload limit.
- Payload validation: root must be a JSON object
{}. - Disk debounce: writes coalesced to at most once every ~2.5 seconds.
| Scenario | Behavior |
|---|---|
| API unavailable | Uses stale cached flags |
| SSE disconnected | Keeps last known snapshot |
| First startup without cache | Returns default values |
| Invalid payload | Payload ignored |
| Disk cache failure | In-memory mode continues |
| Persistent network issues | Automatic reconnect with backoff |
Getters never throw.
| Platform | Location |
|---|---|
| Windows | %LOCALAPPDATA%\soasap\cache |
| Linux / macOS | ~/.local/share/soasap/cache |
Override:
create_soasap_client(
api_key="...",
cache_directory="/custom/path",
)from soasap import SoasapErrorSource, create_soasap_client
create_soasap_client(
api_key="...",
on_error=lambda ctx: print(
f"[{ctx.source.value}] transient={ctx.is_transient} {ctx.exception}"
),
)Sources: SoasapErrorSource.NETWORK, .DISK, .PARSER.
flags.close()Cancels SSE, flushes disk (up to 5s), and releases resources.
[Read path] get_bool() → current_snapshot ref → O(1) lookup
↑
| (snapshot reference swap)
[Background] SSE → SseEventParser (5MB cap) → DiskWriteCoalescer → disk
Use one client instance per process. For multi-process servers (e.g. Gunicorn), create one client per process.
import os
from flask import Flask, g
from soasap import create_soasap_client
flags = create_soasap_client(
api_key=os.environ["SOASAP_API_KEY"],
preload=True,
)
app = Flask(__name__)
@app.before_request
def attach_flags():
g.soasap = flags
@app.get("/")
def index():
if g.soasap.get_bool("maintenance-mode"):
return "Maintenance", 503
return "OK"
import atexit
atexit.register(flags.close)- Python 3.10, 3.11, 3.12, 3.13+
- Never block application startup
- Never crash the host process
- Prefer stale data over failure
- Keep the hot path fast
- Make network failures invisible to request handlers
- Keep the API minimal and predictable
MIT