Skip to main content

flagnetic

Python SDK for Flagnetic, self-hosted feature flags and experiments.

pip install flagnetic

It needs Python 3.11 or later and installs no dependency: the package is pure Python, its regular expression engine included, so it installs the same on Alpine and on free-threaded builds. Supported: the Python releases still supported upstream (a release reaching its end of life is dropped in a minor version) and, with the extra, openfeature-sdk 0.9 and later. The package speaks the SDK API /sdk/v1 and works with every Flagnetic server from 0.1.0 on (compatibility); docs/sdks/python.md is how it is deployed and tested.

Quick start

import flagnetic

client = flagnetic.Client("<server SDK key>", "https://flags.example.com")
client.ready(timeout=5)  # fresh, last_good or built_in; never raises

context = flagnetic.Context(units={"user": "u-1001"}, attributes={"plan": "pro"})
if client.get_bool(context, "new-checkout", False):
    ...

client.close()

The getters never block and never raise: they read the ruleset the client holds and serve the default you pass when they cannot serve the flag. One background thread fetches the ruleset with If-None-Match, holds the ruleset stream (a kill switch reaches the client within two seconds), polls every 30 seconds while the stream is down, and backs off with jitter. store="/var/lib/app/flagnetic.json" keeps the last good ruleset across restarts.

Experiments and events

The same thread reports an evaluation summary every minute (counts per flag, variation and reason, never a unit ID or a value) and sends exposures and aliases every two seconds, retrying with the same event IDs so the server drops a resend. An evaluation alone is not an exposure: call exposure where the unit actually meets the behaviour, or turn on automatic exposure for the flag.

price = client.get_string(context, "pricing", "Built-in price")
client.exposure(context, "pricing")  # reports the assignment just served
client.alias("anonymous", "d-8f1c", "user", "u-1001")  # after sign-up
client.flush(timeout=5)  # close() flushes too

Pre-forking servers and asyncio

Under gunicorn or uWSGI, build the client before the workers fork, or in each worker: a forked worker restarts the thread by itself, refetches and reports its own events. No hook is needed.

The getters never block, so they are safe in a coroutine. Start-up and shutdown wait on a worker thread:

stage = await client.aready(timeout=5)
await client.aflush()
await client.aclose()

Tests and local overrides

flagnetic.testing builds offline clients that make no request and start no thread:

from flagnetic import testing

client = testing.from_values({"new-checkout": True})  # or from_document(...), from_file(path)
client.set("new-checkout", False)  # change listeners fire
client.reload()  # back to what it was built from
client.recorder.exposures()  # what it would have reported

Client(..., allow_overrides=True) enables client.local_overrides.set(key, value), served with reason local_override; without the option set raises LocalOverridesNotAllowed, and a protected environment refuses and drops every local override.

OpenFeature

pip install 'flagnetic[openfeature]'

The extra installs openfeature-sdk 0.9 or later and the provider in flagnetic.openfeature. It takes the client's arguments and options and builds the client itself:

from openfeature import api
from openfeature.evaluation_context import EvaluationContext
from openfeature.track import TrackingEventDetails
from flagnetic.openfeature import Provider

provider = Provider("<server SDK key>", "https://flags.example.com", unit_type="user")
api.set_provider_and_wait(provider)  # api.set_provider before openfeature-sdk 0.10
client = api.get_client()

context = EvaluationContext("u-1001", {"plan": "pro", "units": {"account": "a-17"}})
price = client.get_string_value("pricing", "Built-in price", context)
client.track("exposure", context, TrackingEventDetails().add("flag", "pricing"))
client.track(
    "alias",
    tracking_event_details=TrackingEventDetails()
    .add("fromUnitType", "anonymous")
    .add("fromUnitId", "d-8f1c")
    .add("toUnitType", "user")
    .add("toUnitId", "u-1001"),
)

The targeting key is the unit ID of unit_type; the units attribute adds further units by unit type; every other attribute is an attribute of the context. Reasons map as in every Flagnetic provider:

Flagnetic OpenFeature
rule, prerequisite_failed, override, targeting_match TARGETING_MATCH
rollout, experiment SPLIT
fallthrough DEFAULT
off DISABLED
local_override STATIC
error ERROR
fallback ERROR, with FLAG_NOT_FOUND, TYPE_MISMATCH or GENERAL

The flag metadata carries the Flagnetic reason as flagnetic_reason, with stage, ruleset_version, variation, overridden and, for an experiment, its experiment_* fields. The provider emits PROVIDER_CONFIGURATION_CHANGED with the changed flag keys, PROVIDER_STALE while the client serves its last good ruleset, PROVIDER_READY once it is fresh again and PROVIDER_ERROR when the SDK key is revoked. Initialization waits up to ready_timeout seconds (default 10) for the first fetch and never fails. provider.client is the wrapped client, for local overrides, flush and the recorder of an offline provider (Provider("", "", offline=document)).

Development

uv sync --extra openfeature
uv run ruff check . && uv run ruff format --check . && uv run mypy
uv run pytest
uv run pytest e2e  # the smoke against two real replicas (below)

The smoke needs the end-to-end harness of e2e/ running: start flagnetic-e2e up with FLAGNETIC_E2E_SEED naming a file, then run uv run pytest e2e with the same FLAGNETIC_E2E_SEED and, unless flagnetic-e2e is on the PATH, FLAGNETIC_E2E_CMD naming it. It ends by revoking the seed's server key, so bring the harness up again before running it a second time.

The tests run the shared conformance suite in conformance/ against the evaluator, play the protocol scenarios and degradation cases against a mock server through the public API, and measure that evaluating one flag of a 1,000-flag ruleset stays under a millisecond at the 99th percentile.

Metadata

Release files for flagnetic 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flagnetic 0.1.0
File Size Uploaded Provenance Actions
flagnetic-0.1.0.tar.gz 185.0 kB With provenance Details for flagnetic-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for flagnetic 0.1.0
File Interpreter ABI Platform Actions
flagnetic-0.1.0-py3-none-any.whl Python 3 none any Details for flagnetic-0.1.0-py3-none-any.whl

Total release size: 317.8 kB

Release files / flagnetic-0.1.0.tar.gz

Download URL flagnetic-0.1.0.tar.gz
Size 185.0 kB
Tags Source
SHA-256 checksum
How to use checksums
85164ff63360db8bce18aec77d24b2114b7d807ab62106b4079cadef0d5441e6
BLAKE2b-256 checksum
How to use checksums
ffb62aab1b5dd6d614c7ac92744a84778f599a97b5e7f325d4c83bd51b3d7c44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / flagnetic-0.1.0-py3-none-any.whl

Download URL flagnetic-0.1.0-py3-none-any.whl
Size 132.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3c02fb5c621abb9cc01c1562a1a808de22eef239865529111dee397fc50d2ae
BLAKE2b-256 checksum
How to use checksums
3d2d59a62dc45fc4a9385fa2c97e3bb6ac23709e813a50a25534f8c48d0de5ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for flagnetic 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for flagnetic-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page