Skip to content

API Reference

Auto-generated from source docstrings.

Clients

ConfigClient

opendecree.ConfigClient(target, *, subject=None, role='superadmin', tenant_id=None, token=None, insecure=True, credentials=None, timeout=10.0, retry=None, check_version=False, interceptors=None, otel=False)

Synchronous client for reading and writing OpenDecree configuration values.

Use as a context manager for clean channel lifecycle::

with ConfigClient("localhost:9090", subject="myapp") as client:
    val = client.get("tenant-id", "payments.fee")
    retries = client.get("tenant-id", "payments.retries", int)

Create a new ConfigClient.

Parameters:

Name Type Description Default
target str

gRPC server address (e.g., "localhost:9090").

required
subject str | None

Identity for x-subject metadata header.

None
role str

Role for x-role metadata header. Defaults to "superadmin".

'superadmin'
tenant_id str | None

Default tenant for x-tenant-id metadata header.

None
token str | None

Bearer token. When set, metadata headers are not sent. On a TLS channel the token is embedded via composite_channel_credentials and protected by the TLS layer. On an insecure channel it travels in cleartext and a UserWarning is raised — prefer TLS in production.

None
insecure bool

Use plaintext (no TLS). Defaults to True for local dev. Do not combine with token in production.

True
credentials ChannelCredentials | None

TLS channel credentials. Overrides insecure.

None
timeout float

Default per-RPC timeout in seconds. Defaults to 10.

10.0
retry RetryConfig | None

Retry configuration. Defaults to RetryConfig(). Pass None to disable retry.

None
check_version bool

When True, run :meth:check_compatibility lazily on the first RPC call. Raises :exc:IncompatibleServerError if the server version is outside the supported range.

False
interceptors list[Any] | None

Optional list of :class:grpc.UnaryUnaryClientInterceptor / :class:grpc.UnaryStreamClientInterceptor instances to inject (e.g., for logging, tracing, or metrics). User-supplied interceptors are applied outermost (before the SDK's internal auth interceptor).

None
otel bool

When True, wire an OpenTelemetry gRPC client interceptor so get/set/watch RPCs appear in your application traces. Requires pip install 'opendecree[otel]'. The OTel interceptor is outermost, wrapping all other interceptors.

False

server_version property

Deprecated. Use :meth:get_server_version instead.

check_compatibility()

Check that the server version is compatible with this SDK.

Fetches the server version (cached) and compares it against opendecree.SUPPORTED_SERVER_VERSION.

Raises:

Type Description
IncompatibleServerError

If the server version is outside the supported range.

UnavailableError

If the server is unreachable.

close()

Close the underlying gRPC channel.

get(tenant_id, field_path, value_type=None, *, nullable=False)

get(tenant_id: str, field_path: str) -> str
get(tenant_id: str, field_path: str, value_type: type[bool]) -> bool
get(tenant_id: str, field_path: str, value_type: type[int]) -> int
get(tenant_id: str, field_path: str, value_type: type[float]) -> float
get(tenant_id: str, field_path: str, value_type: type[timedelta]) -> timedelta
get(tenant_id: str, field_path: str, value_type: type[str], *, nullable: bool) -> str | None

Get a config value, optionally converting to a specific type.

Without a type argument, returns the raw string value. With a type argument, converts and returns the typed value.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
field_path str

Dot-separated field path (e.g., "payments.fee").

required
value_type type | None

Target type (str, int, float, bool, timedelta). Defaults to str.

None
nullable bool

If True, return None for null/unset values instead of raising.

False

Returns:

Type Description
object

The config value, converted to the requested type.

Raises:

Type Description
NotFoundError

If the field has no value (and nullable is False).

TypeMismatchError

If the value cannot be converted to the requested type.

get_all(tenant_id)

Get all config values for a tenant.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required

Returns:

Type Description
dict[str, str]

A dict mapping field paths to their string values.

Raises:

Type Description
NotFoundError

If the tenant does not exist.

get_server_version()

Fetch the server's version, cached after first call.

Returns:

Type Description
ServerVersion

ServerVersion with version and commit strings.

Raises:

Type Description
UnavailableError

If the server is unreachable.

set(tenant_id, field_path, value, *, description=None, value_description=None, expected_checksum=None, idempotency_key=None)

Set a config value.

value is a native Python value — str, int, float, bool, datetime, timedelta, dict, list, or URL (for url-typed fields; a plain str targets string-typed fields). The SDK picks the wire representation matching value's runtime type, which the server then validates against the field's declared schema type — passing the wrong Python type raises InvalidArgumentError.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
field_path str

Dot-separated field path (e.g., "payments.fee").

required
value ConfigValue

The value, as a Python type matching the field's schema type.

required
description str | None

Optional version-level description for the audit log.

None
value_description str | None

Optional description stored with this specific value.

None
expected_checksum str | None

When set, the server rejects the write if the current value's checksum does not match (optimistic concurrency).

None
idempotency_key str | None

When provided, the request is retried on DEADLINE_EXCEEDED in addition to UNAVAILABLE. Use only when the write is safe to apply more than once (e.g., the value is a known constant and a duplicate apply is harmless). Without this key, writes are only retried on UNAVAILABLE to avoid double-applying after a server-side timeout.

None

Raises:

Type Description
NotFoundError

If the field does not exist in the schema.

LockedError

If the field is locked.

InvalidArgumentError

If the value fails validation.

ChecksumMismatchError

If expected_checksum is set and does not match.

set_many(tenant_id, updates, *, description=None, idempotency_key=None)

Atomically set multiple config values.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
updates list[FieldUpdate]

List of :class:FieldUpdate objects, each carrying a field path, value, and optional per-field metadata.

required
description str | None

Optional version-level description for the audit log.

None
idempotency_key str | None

When provided, the request is retried on DEADLINE_EXCEEDED in addition to UNAVAILABLE. See set() for details on retry semantics.

None

Raises:

Type Description
NotFoundError

If a field does not exist in the schema.

LockedError

If any field is locked.

InvalidArgumentError

If any value fails validation.

ChecksumMismatchError

If any expected_checksum does not match.

set_null(tenant_id, field_path, *, idempotency_key=None)

Set a config field to null.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
field_path str

Dot-separated field path.

required
idempotency_key str | None

When provided, the request is retried on DEADLINE_EXCEEDED in addition to UNAVAILABLE. See set() for details on retry semantics.

None

Raises:

Type Description
NotFoundError

If the field does not exist in the schema.

LockedError

If the field is locked.

watch(tenant_id)

Create a config watcher for a tenant.

Use as a context manager — auto-starts on enter, auto-stops on exit::

with client.watch("tenant-id") as watcher:
    fee = watcher.field("payments.fee", float, default=0.01)
    print(fee.value)

The watcher uses the client's gRPC channel and auth settings.

AsyncConfigClient

opendecree.AsyncConfigClient(target, *, subject=None, role='superadmin', tenant_id=None, token=None, insecure=True, credentials=None, timeout=10.0, retry=None, check_version=False, interceptors=None, otel=False)

Asynchronous client for reading and writing OpenDecree configuration values.

Use as an async context manager::

async with AsyncConfigClient("localhost:9090", subject="myapp") as client:
    val = await client.get("tenant-id", "payments.fee")
    retries = await client.get("tenant-id", "payments.retries", int)

Create a new AsyncConfigClient.

Parameters:

Name Type Description Default
target str

gRPC server address (e.g., "localhost:9090").

required
subject str | None

Identity for x-subject metadata header.

None
role str

Role for x-role metadata header. Defaults to "superadmin".

'superadmin'
tenant_id str | None

Default tenant for x-tenant-id metadata header.

None
token str | None

Bearer token. When set, metadata headers are not sent. On a TLS channel the token is embedded via composite_channel_credentials and protected by the TLS layer. On an insecure channel it travels in cleartext and a UserWarning is raised — prefer TLS in production.

None
insecure bool

Use plaintext (no TLS). Defaults to True for local dev. Do not combine with token in production.

True
credentials ChannelCredentials | None

TLS channel credentials. Overrides insecure.

None
timeout float

Default per-RPC timeout in seconds. Defaults to 10.

10.0
retry RetryConfig | None

Retry configuration. Defaults to RetryConfig(). Pass None to disable retry.

None
check_version bool

When True, run :meth:check_compatibility lazily on the first RPC call. Raises :exc:IncompatibleServerError if the server version is outside the supported range.

False
interceptors list[Any] | None

Optional list of :class:grpc.aio.ClientInterceptor instances to inject (e.g., for logging, tracing, or metrics). Passed directly to the grpc.aio channel.

None
otel bool

When True, wire an OpenTelemetry gRPC client interceptor so get/set/watch RPCs appear in your application traces. Requires pip install 'opendecree[otel]'. The OTel interceptor is outermost, wrapping all other interceptors.

False

check_compatibility() async

Check that the server version is compatible with this SDK.

Fetches the server version (cached) and compares it against opendecree.SUPPORTED_SERVER_VERSION.

Raises:

Type Description
IncompatibleServerError

If the server version is outside the supported range.

UnavailableError

If the server is unreachable.

close() async

Close the underlying gRPC channel.

get(tenant_id, field_path, value_type=None, *, nullable=False) async

get(tenant_id: str, field_path: str) -> str
get(tenant_id: str, field_path: str, value_type: type[bool]) -> bool
get(tenant_id: str, field_path: str, value_type: type[int]) -> int
get(tenant_id: str, field_path: str, value_type: type[float]) -> float
get(tenant_id: str, field_path: str, value_type: type[timedelta]) -> timedelta
get(tenant_id: str, field_path: str, value_type: type[str], *, nullable: bool) -> str | None

Get a config value, optionally converting to a specific type.

Without a type argument, returns the raw string value. With a type argument, converts and returns the typed value.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
field_path str

Dot-separated field path (e.g., "payments.fee").

required
value_type type | None

Target type (str, int, float, bool, timedelta). Defaults to str.

None
nullable bool

If True, return None for null/unset values instead of raising.

False

Returns:

Type Description
object

The config value, converted to the requested type.

Raises:

Type Description
NotFoundError

If the field has no value (and nullable is False).

TypeMismatchError

If the value cannot be converted to the requested type.

get_all(tenant_id) async

Get all config values for a tenant.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required

Returns:

Type Description
dict[str, str]

A dict mapping field paths to their string values.

Raises:

Type Description
NotFoundError

If the tenant does not exist.

get_server_version() async

Fetch the server's version, cached after first call.

Returns:

Type Description
ServerVersion

ServerVersion with version and commit strings.

Raises:

Type Description
UnavailableError

If the server is unreachable.

set(tenant_id, field_path, value, *, description=None, value_description=None, expected_checksum=None, idempotency_key=None) async

Set a config value.

value is a native Python value — str, int, float, bool, datetime, timedelta, dict, list, or URL (for url-typed fields; a plain str targets string-typed fields). The SDK picks the wire representation matching value's runtime type, which the server then validates against the field's declared schema type — passing the wrong Python type raises InvalidArgumentError.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
field_path str

Dot-separated field path (e.g., "payments.fee").

required
value ConfigValue

The value, as a Python type matching the field's schema type.

required
description str | None

Optional version-level description for the audit log.

None
value_description str | None

Optional description stored with this specific value.

None
expected_checksum str | None

When set, the server rejects the write if the current value's checksum does not match (optimistic concurrency).

None
idempotency_key str | None

When provided, the request is retried on DEADLINE_EXCEEDED in addition to UNAVAILABLE. Use only when the write is safe to apply more than once (e.g., the value is a known constant and a duplicate apply is harmless). Without this key, writes are only retried on UNAVAILABLE to avoid double-applying after a server-side timeout.

None

Raises:

Type Description
NotFoundError

If the field does not exist in the schema.

LockedError

If the field is locked.

InvalidArgumentError

If the value fails validation.

ChecksumMismatchError

If expected_checksum is set and does not match.

set_many(tenant_id, updates, *, description=None, idempotency_key=None) async

Atomically set multiple config values.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
updates list[FieldUpdate]

List of :class:FieldUpdate objects, each carrying a field path, value, and optional per-field metadata.

required
description str | None

Optional version-level description for the audit log.

None
idempotency_key str | None

When provided, the request is retried on DEADLINE_EXCEEDED in addition to UNAVAILABLE. See set() for details on retry semantics.

None

Raises:

Type Description
NotFoundError

If a field does not exist in the schema.

LockedError

If any field is locked.

InvalidArgumentError

If any value fails validation.

ChecksumMismatchError

If any expected_checksum does not match.

set_null(tenant_id, field_path, *, idempotency_key=None) async

Set a config field to null.

Parameters:

Name Type Description Default
tenant_id str

Tenant UUID.

required
field_path str

Dot-separated field path.

required
idempotency_key str | None

When provided, the request is retried on DEADLINE_EXCEEDED in addition to UNAVAILABLE. See set() for details on retry semantics.

None

Raises:

Type Description
NotFoundError

If the field does not exist in the schema.

LockedError

If the field is locked.

watch(tenant_id)

Create an async config watcher for a tenant.

Use as an async context manager — auto-starts on enter, auto-stops on exit::

async with client.watch("tenant-id") as watcher:
    fee = watcher.field("payments.fee", float, default=0.01)
    print(fee.value)

The watcher uses the client's gRPC channel and auth settings.

Watchers

ConfigWatcher

opendecree.ConfigWatcher(stub, pb2, tenant_id, timeout)

Watches a tenant's configuration for live changes.

Created via client.watch(). Use as a context manager — auto-starts on enter, auto-stops on exit.

field(path, type_, *, default, on_callback_error=None, max_queue_size=_DEFAULT_MAX_QUEUE_SIZE)

Register a field to watch.

Must be called before the watcher is started (before enter).

Parameters:

Name Type Description Default
path str

Dot-separated field path (e.g., "payments.fee").

required
type_ type[T]

Python type to convert values to (str, int, float, bool, timedelta).

required
default T

Default value when the field is null or not set.

required
on_callback_error Callable[[Exception], None] | None

Optional hook called with the exception when an on_change callback raises. If not set, the exception is logged. The hook may re-raise to terminate the watcher's background loop.

None
max_queue_size int

Maximum number of unread changes buffered. When the queue is full, the oldest entry is dropped and dropped_changes is incremented. Default: 1024.

_DEFAULT_MAX_QUEUE_SIZE

Returns:

Type Description
WatchedField[T]

A WatchedField that tracks the live value.

start()

Start watching — loads initial snapshot and subscribes to changes.

stop()

Stop watching and clean up the background thread.

WatchedField

opendecree.WatchedField(path, type_, default, *, on_callback_error=None, max_queue_size=_DEFAULT_MAX_QUEUE_SIZE)

Bases: _WatchedFieldBase[T]

A live, thread-safe configuration field with a typed value.

Attributes are updated automatically by the watcher's background thread.

dropped_changes property

Number of changes dropped because the queue was full.

value property

The current value — always fresh, thread-safe.

changes()

Blocking iterator that yields Change events for this field.

Blocks until a change arrives or the watcher is stopped. Yields Change objects with old_value and new_value as strings.

AsyncConfigWatcher

opendecree.AsyncConfigWatcher(stub, pb2, tenant_id, timeout, metadata=None)

Watches a tenant's configuration for live changes (async variant).

Created via async_client.watch(). Use as an async context manager — auto-starts on enter, auto-stops on exit.

field(path, type_, *, default, on_callback_error=None, max_queue_size=_DEFAULT_MAX_QUEUE_SIZE)

Register a field to watch.

Must be called before the watcher is started (before aenter).

Parameters:

Name Type Description Default
path str

Dot-separated field path (e.g., "payments.fee").

required
type_ type[T]

Python type to convert values to (str, int, float, bool, timedelta).

required
default T

Default value when the field is null or not set.

required
on_callback_error Callable[[Exception], None] | None

Optional hook called with the exception when an on_change callback raises. If not set, the exception is logged. The hook may re-raise to terminate the watcher's background task.

None
max_queue_size int

Maximum number of unread changes buffered. When the queue is full, the oldest entry is dropped and dropped_changes is incremented. Default: 1024.

_DEFAULT_MAX_QUEUE_SIZE

Returns:

Type Description
AsyncWatchedField[T]

An AsyncWatchedField that tracks the live value.

start() async

Start watching — loads initial snapshot and subscribes to changes.

stop() async

Stop watching and cancel the background task.

AsyncWatchedField

opendecree.AsyncWatchedField(path, type_, default, *, on_callback_error=None, max_queue_size=_DEFAULT_MAX_QUEUE_SIZE)

Bases: _WatchedFieldBase[T]

A live configuration field with a typed value (async variant).

Updated automatically by the watcher's asyncio task.

dropped_changes property

Number of changes dropped because the queue was full.

value property

The current value — always fresh.

changes() async

Async iterator that yields Change events for this field.

Yields Change objects until the watcher is stopped.

Data Types

Change

opendecree.Change(field_path, old_value, new_value, version, changed_by='') dataclass

A configuration change event from a subscription.

Attributes:

Name Type Description
field_path str

Dot-separated field path that changed.

old_value str | None

Previous value as a string, or None if newly created.

new_value str | None

New value as a string, or None if set to null.

version int

Config version number after this change.

changed_by str

Identity of who made the change.

FieldUpdate

opendecree.FieldUpdate(field_path, value, expected_checksum=None, value_description=None) dataclass

A single field update for use with :meth:ConfigClient.set_many.

Attributes:

Name Type Description
field_path str

Dot-separated field path (e.g., "payments.fee").

value ConfigValue

The value as a native Python type matching the field's schema type — str, int, float, bool, datetime, timedelta, dict, list, or URL (for url-typed fields). The SDK converts it to the matching wire representation.

expected_checksum str | None

When set, the server rejects the write if the current value's checksum does not match (optimistic concurrency).

value_description str | None

Optional description stored with this specific value.

ServerVersion

opendecree.ServerVersion(version, commit) dataclass

Server version information from the ServerService.

Attributes:

Name Type Description
version str

Semantic version string (e.g., "0.3.1").

commit str

Git commit hash of the server build.

RetryConfig

opendecree.RetryConfig(max_attempts=3, initial_backoff=0.1, max_backoff=5.0, multiplier=2.0, retryable_codes=(grpc.StatusCode.UNAVAILABLE, grpc.StatusCode.DEADLINE_EXCEEDED, grpc.StatusCode.RESOURCE_EXHAUSTED), total_timeout=None) dataclass

Configuration for retry behavior.

Attributes:

Name Type Description
max_attempts int

Maximum number of attempts (including the first).

initial_backoff float

Initial backoff duration in seconds.

max_backoff float

Maximum backoff duration in seconds.

multiplier float

Backoff multiplier between attempts.

retryable_codes tuple[StatusCode, ...]

gRPC status codes that trigger a retry.

total_timeout float | None

Overall wall-clock budget in seconds shared across all attempts. When set, backoff sleeps are clipped to the remaining budget and no further attempt is made once the budget is exhausted. None means no global limit (original behavior).

Exceptions

DecreeError

opendecree.DecreeError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: Exception

Base exception for all OpenDecree SDK errors.

NotFoundError

opendecree.NotFoundError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when a requested resource does not exist.

AlreadyExistsError

opendecree.AlreadyExistsError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when attempting to create a resource that already exists.

InvalidArgumentError

opendecree.InvalidArgumentError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when a request contains invalid arguments.

LockedError

opendecree.LockedError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when a field is locked and cannot be modified.

ChecksumMismatchError

opendecree.ChecksumMismatchError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when an optimistic concurrency check fails.

PermissionDeniedError

opendecree.PermissionDeniedError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when the caller lacks permission for the operation.

UnavailableError

opendecree.UnavailableError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when the server is unavailable.

TypeMismatchError

opendecree.TypeMismatchError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when a typed getter receives a value of the wrong type.

IncompatibleServerError

opendecree.IncompatibleServerError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when the server version is incompatible with this SDK.

TimeoutError

opendecree.TimeoutError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when the operation deadline was exceeded.

ResourceExhaustedError

opendecree.ResourceExhaustedError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when a resource quota or rate limit is exceeded.

CancelledError

opendecree.CancelledError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when the operation was cancelled.

UnimplementedError

opendecree.UnimplementedError(message, code=None, *, trailing_metadata=None, retry_after=None)

Bases: DecreeError

Raised when the server has not implemented the operation.