Skip to main content

ConfigDirector Python SDK

CI PyPI

Python server SDK for ConfigDirector, remote config and feature flags with typed values, JSON Schema validation, and safe renames of live flags. Start free, no card required.

Install

pip install configdirector-server-sdk

Retrieve a value

from configdirector import create_client

# The server SDK key is a secret. Do not commit it to your source code.
client = create_client("YOUR-SERVER-SDK-KEY")
client.initialize()

new_checkout = client.get_value("new-checkout", False)

Full details are in the official documentation.

Test your code

configdirector.testing creates a test client: the SDK's real client connected to an in-memory server that your test controls, with no network connection, no telemetry, and no thread. Seed values, change them mid-test so watches and configs_updated handlers fire, and hold or fail initialization to test loading states. It ships in the package, so there is nothing else to install.

from configdirector.testing import create_test_client

with create_test_client({"new-checkout": True}) as test_client:
    service = CheckoutService(test_client.client)
    test_client.client.initialize()

    assert service.is_new_checkout_enabled("user-123")

    test_client.set_value("new-checkout", False)
    assert not service.is_new_checkout_enabled("user-123")

test_client.client is a ConfigDirectorClient, so it goes anywhere your code accepts one. It starts uninitialized, like a production client, because the code under test usually owns the call to initialize; with no hold or failure armed, initialize completes at once with the stored values. The test client is a context manager that closes its client on exit.

Values

create_test_client, set_value, and replace_values take native values, and the config type follows from each value's type: a bool; an int (an integer config); a float (a float config); a str; or a dict with str keys or a list (a JSON config). A str is always a string config, so JSON text meant as a JSON config is given as json.loads(text). Every value is served as an unconditional config, so every context receives the same value; a test that needs different values per context is written as one test per value. None, an unsupported type such as a tuple or a set, or a dict with a non-str key raise ConfigDirectorTypeError; a blank key, a non-finite number, or an integer with too many digits to write out raise ConfigDirectorValidationError. In every case nothing changes.

Reads behave as they do in production: set_value("k", True) read as a string returns the in-code default value with the type-mismatch reason, set_value("k", 2.5) read with an int default returns the default with the invalid-number reason, and set_value("k", "") serves the in-code default value with the value-missing reason. Reasons arrive on the config_evaluated event. JSON objects keep their insertion order.

Controls

Control Effect
set_value(key, value) Stores the value and, once the client is connected, delivers an update carrying only key. Watches of key, configs_updated handlers, and reads see it before the call returns.
remove_value(key) Removes the value and delivers a full update without it. Reads return the in-code default value with the config-state-missing reason, watches of key receive the default, and configs_updated lists key in removed_keys.
replace_values(values) Replaces every stored value, disarms any armed hold or failure, and delivers a full update. Use it to reset a test client shared across tests.
hold_initialization() The next initialize waits until complete_initialization() or fail_initialization(), or until the client's timeout elapses. initialize blocks, so a test calls it on another thread.
complete_initialization() Delivers the stored values to the held initialize, on the calling thread, so the client is ready when the call returns. Called while a hold is armed but not picked up, it disarms the hold.
fail_initialization() Fails the held initialize the way an invalid SDK key does: it completes promptly, the client is not ready, and the error is logged. Called with no initialize held, it arms the next one to fail.

After a failed attempt, the next initialize succeeds and delivers the values stored in the meantime.

Options

create_test_client(values, timeout=..., logger=...) takes timeout (the client's connection timeout in seconds, which bounds how long a held initialize waits; defaults to the SDK's production timeout, and a timeout given to initialize itself takes precedence) and logger (defaults to the standard library logger named configdirector, so pytest's caplog captures it).

Module-level clients

An application that creates and initializes its client when a module loads runs the real client before any test can intervene, which opens a connection and starts threads. A monkeypatch also has to reach every module that bound the name: from configdirector_client import client binds its own client, so patching configdirector_client.client does not change it. Structure the application around a lazy getter instead, and replace that in tests:

_client: ConfigDirectorClient | None = None
_client_lock = threading.Lock()


def get_client() -> ConfigDirectorClient:
    global _client
    with _client_lock:
        if _client is None:
            _client = create_client(os.environ["CONFIGDIRECTOR_SERVER_SDK_KEY"])
            _client.initialize()
        return _client

The lock matters on a threaded server: without it, two first requests arriving together each build and initialize a client.

def test_new_checkout(monkeypatch: pytest.MonkeyPatch) -> None:
    with create_test_client({"new-checkout": True}) as test_client:
        test_client.client.initialize()
        monkeypatch.setattr(app, "get_client", lambda: test_client.client)
        ...

What to expect

  • The first update is delivered while initialize connects, so watches registered before initialize run before it returns, and configs_updated fires before client_ready.
  • Watches and handlers run on the thread that calls set_value, remove_value, replace_values, complete_initialization, or initialize, not on a transport thread.
  • A held initialize that times out, or that is interrupted by close, leaves the client not ready until the next initialize, and logs the SDK's timeout warning; production streaming would keep retrying in the background. Readiness is sticky: once the client is ready, a later held, failed, or timed-out attempt leaves it ready with its previous values, and completing such an attempt fires no client_ready.
  • Watches fire on every update carrying their key, whether or not the value changed, as in production. remove_value and replace_values deliver a full update, so they also fire the watches of every remaining key.
  • An exception raised by a watch or handler is logged and does not propagate, as with the production transports.
  • After close, is_ready is false, get_value serves the last values, get_all_configs returns an empty dict, initialize raises ConfigDirectorValidationError, every watch and event handler has been removed, the test client's controls are silent no-ops, and no SDK thread is left running.

Documentation

Refer to the official documentation for the Python SDK.

There is also a quickstart guide for ConfigDirector and any of our SDKs.

Sample apps

samples/configdirector-server-sdk/ holds small, runnable applications built on this SDK, one per web framework. Start with flask:

cd samples/configdirector-server-sdk/flask
uv run flask --app app run --port 3600

OpenFeature

To read ConfigDirector flags through the OpenFeature API, use configdirector-openfeature-server-provider, which is built on this SDK.

Getting Help

Metadata

Release files for configdirector-server-sdk 1.7.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 configdirector-server-sdk 1.7.0
File Size Uploaded
configdirector_server_sdk-1.7.0.tar.gz 55.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for configdirector-server-sdk 1.7.0
File Interpreter ABI Platform
configdirector_server_sdk-1.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 132.1 kB

Release files / configdirector_server_sdk-1.7.0.tar.gz

Download URL configdirector_server_sdk-1.7.0.tar.gz
Size 55.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6979e27e30d2cd13ca0257e7e2f39be99706af8351eb6347efc561a1ecdd517f
BLAKE2b-256 checksum
How to use checksums
3082ab01c244a00bc839c1dd01673fb00c8b2660fc0bfb1c46c5d6958e14a5ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 15, 2026.

Transparency log

Release files / configdirector_server_sdk-1.7.0-py3-none-any.whl

Download URL configdirector_server_sdk-1.7.0-py3-none-any.whl
Size 76.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b0cee3797940a37119369e2567f9d0fcfc6b4bec65dbdc75c4c2bb3cb073f6e6
BLAKE2b-256 checksum
How to use checksums
97e39f6c249dd55ae9595d020ce303d5ef48a0006cb71a1067e524ea4d9926f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.7.0 This release

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

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