Python SDK for the Duroxide durable execution runtime
Project description
duroxide-python
Python SDK for the Duroxide durable execution runtime.
Write durable workflows as Python generators. The Rust runtime handles replay, persistence, and fault tolerance.
Features
- Generator-based orchestrations —
yieldtask descriptors, Rust handles DurableFutures - Activities — regular Python functions for side effects (I/O, network calls)
- Timers — durable delays that survive process restarts
- Events — wait for external signals
- Sub-orchestrations — compose workflows hierarchically
- Fan-out/Fan-in —
ctx.all()for parallel execution,ctx.race()for first-to-complete - Continue-as-new — long-running orchestrations with bounded history
- Deterministic replay — safe resume after crashes
- SQLite & PostgreSQL — pluggable storage providers
- Custom Status —
ctx.set_custom_status()/ctx.reset_custom_status()for orchestration progress reporting,client.wait_for_status_change()for efficient polling - KV Store — durable per-instance state via
ctx.set_kv_value()/ctx.get_kv_value()/ctx.get_kv_all_values()/ctx.get_kv_all_keys()/ctx.get_kv_length()/ctx.clear_kv_value()/ctx.clear_all_kv_values()/ctx.prune_kv_values_updated_before(), plusclient.get_kv_value()/client.wait_for_kv_value() - Event Queues —
ctx.dequeue_event(queue_name)for FIFO mailbox-style message passing,client.enqueue_event()to send messages - Retry on Session —
ctx.schedule_activity_with_retry_on_session()for retry with session affinity - Tag Routing — worker tags for activity affinity (
MAX_WORKER_TAGS=5,MAX_TAG_NAME_BYTES=256,MAX_KV_KEYS=150,MAX_KV_VALUE_BYTES=65536) - Admin APIs — instance management, metrics, pruning
- Activity client access —
ctx.get_client()lets activities start new orchestrations - Runtime metrics —
metrics_snapshot()for orchestration/activity counters
Installation
pip install duroxide
Prebuilt wheels are published for macOS arm64/x64, Linux manylinux x86_64 and aarch64, and Windows x86_64.
Quick Start
from duroxide import SqliteProvider, Client, Runtime
# Create provider and runtime
provider = SqliteProvider.in_memory()
runtime = Runtime(provider)
# Register an activity
@runtime.register_activity("greet")
def greet(ctx, input):
return f"Hello, {input['name']}!"
# Register an orchestration (generator function)
@runtime.register_orchestration("GreetWorkflow")
def greet_workflow(ctx, input):
result = yield ctx.schedule_activity("greet", input)
return result
# Start runtime and run orchestration
import threading
runtime.start()
client = Client(provider)
client.start_orchestration("greet-1", "GreetWorkflow", {"name": "World"})
status = client.wait_for_orchestration("greet-1", 10000)
print(status.output) # "Hello, World!"
runtime.shutdown()
Orchestrations
Orchestrations are Python generator functions. They must be deterministic — no I/O,
no randomness, no time.time(). Use only ctx.* methods for side effects.
@runtime.register_orchestration("MyWorkflow")
def my_workflow(ctx, input):
# Schedule activities
result = yield ctx.schedule_activity("DoWork", input)
# Fan-out / Fan-in
results = yield ctx.all([
ctx.schedule_activity("TaskA", {"id": 1}),
ctx.schedule_activity("TaskB", {"id": 2}),
])
# Timer
yield ctx.schedule_timer(5000) # 5 seconds
# Wait for external event
approval = yield ctx.wait_for_event("approval")
# Sub-orchestration
sub_result = yield ctx.schedule_sub_orchestration("SubWorkflow", input)
# Race (first to complete wins)
winner = yield ctx.race(
ctx.schedule_activity("Fast", None),
ctx.schedule_timer(10000),
)
# Custom status (fire-and-forget, no yield)
ctx.set_custom_status("processing complete")
# Dequeue from event queue (FIFO, blocks until message available)
msg = yield ctx.dequeue_event("inbox")
return {"result": result, "winner": winner}
Activities
Activities are regular Python functions that perform side effects. They run outside the replay engine and are safe for I/O operations.
@runtime.register_activity("SendEmail")
def send_email(ctx, input):
ctx.trace_info(f"Sending email to {input['to']}")
# ... actual email sending ...
return {"sent": True}
PostgreSQL Provider
from duroxide import PostgresProvider, Client, Runtime
provider = PostgresProvider.connect("postgresql://user:pass@localhost:5432/mydb")
# or with custom schema:
provider = PostgresProvider.connect_with_schema("postgresql://...", "duroxide_python")
runtime = Runtime(provider)
client = Client(provider)
Admin APIs
client = Client(provider)
# Metrics
metrics = client.get_system_metrics()
stats = client.get_orchestration_stats("instance-1")
depths = client.get_queue_depths()
# Instance management
instances = client.list_all_instances()
info = client.get_instance_info("instance-1")
tree = client.get_instance_tree("instance-1")
# Execution history with full event data
executions = client.list_executions("instance-1")
events = client.read_execution_history("instance-1", executions[0])
for event in events:
print(event.kind, event.data)
# event.kind: "OrchestrationStarted" | "ActivityCompleted" | ...
# event.data: JSON string with event-specific content (result, input, error, etc.)
# Cleanup
client.delete_instance("instance-1", force=True)
client.prune_executions("instance-1", PruneOptions(keep_last=5))
Custom Status
Report orchestration progress visible to external clients:
@runtime.register_orchestration("ProgressWorkflow")
def progress_workflow(ctx, input):
ctx.set_custom_status("step 1: validating")
yield ctx.schedule_activity("Validate", input)
ctx.set_custom_status("step 2: processing")
result = yield ctx.schedule_activity("Process", input)
ctx.reset_custom_status() # clear status
return result
# Poll for status changes from outside
status = client.wait_for_status_change("instance-1", 0, 50, 10000)
if status:
print(status.custom_status) # "step 1: validating"
print(status.custom_status_version) # monotonically increasing counter
KV Store
Durable per-instance key-value state for orchestration coordination and request/response patterns:
@runtime.register_orchestration("KvWorkflow")
def kv_workflow(ctx, input):
ctx.set_kv_value("status", "running")
result = yield ctx.schedule_activity("Compute", input)
ctx.set_kv_value("result", str(result))
snapshot = ctx.get_kv_all_values()
keys = ctx.get_kv_all_keys()
count = ctx.get_kv_length()
return {"result": result, "snapshot": snapshot, "keys": keys, "count": count}
# External reads
status = client.wait_for_kv_value("instance-1", "status", 10000)
result = client.get_kv_value("instance-1", "result")
KV entries are scoped to a single orchestration instance and remain readable after completion until the instance is deleted or pruned. Use ctx.prune_kv_values_updated_before(cutoff_ms) to deterministically clear stale keys from prior turns when you only want to retain newer state.
Event Queues
Persistent FIFO message passing between clients and orchestrations:
@runtime.register_orchestration("ChatBot")
def chat_bot(ctx, input):
msg_json = yield ctx.dequeue_event("inbox")
msg = json.loads(msg_json)
response = yield ctx.schedule_activity("Generate", msg["text"])
ctx.set_custom_status(json.dumps({"state": "replied", "response": response, "seq": msg["seq"]}))
if "bye" in msg["text"].lower():
return f"Done after {msg['seq']} msgs"
return (yield ctx.continue_as_new(""))
# Send messages from outside
client.enqueue_event(instance_id, "inbox", json.dumps({"seq": 1, "text": "Hello!"}))
status = client.wait_for_status_change(instance_id, 0, 50, 10000)
reply = json.loads(status.custom_status)
Development
# Create and activate a virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install build tools and test dependencies
pip install maturin pytest
# Build the native extension and install in development mode
maturin develop
# Run all 59 tests
pytest
# Run tests with verbose output
pytest -v
# Run a single test file
pytest tests/test_e2e.py -v
# Run a single test
pytest tests/test_e2e.py::test_hello_world
# Stop on first failure
pytest -v -x
# Build release wheel
maturin build --release
After Rust source changes (src/*.rs), re-run maturin develop to rebuild.
Python-only changes (python/duroxide/, tests/) take effect immediately.
Changelog
See CHANGELOG.md for release notes.
Documentation
- User Guide — orchestration patterns, activities, providers, tracing, determinism rules
- Architecture — PyO3 interop, GIL deadlock fix, generator driver, tracing internals
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file duroxide-0.1.24.tar.gz.
File metadata
- Download URL: duroxide-0.1.24.tar.gz
- Upload date:
- Size: 114.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e83f044ed5035a4fa074432f6792a2e45f1d07a0c0bcc69360e4b22b9842ff91
|
|
| MD5 |
f93034f7590ccf79f0b481dbe99bc56c
|
|
| BLAKE2b-256 |
7f0717ce87aa6ce6e872e2c8261c6f60eb62a29f1f6d83c77463badc10084e8f
|
Provenance
The following attestation bundles were made for duroxide-0.1.24.tar.gz:
Publisher:
publish.yml on microsoft/duroxide-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duroxide-0.1.24.tar.gz -
Subject digest:
e83f044ed5035a4fa074432f6792a2e45f1d07a0c0bcc69360e4b22b9842ff91 - Sigstore transparency entry: 1435997492
- Sigstore integration time:
-
Permalink:
microsoft/duroxide-python@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Branch / Tag:
refs/tags/v0.1.24 - Owner: https://github.com/microsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Trigger Event:
release
-
Statement type:
File details
Details for the file duroxide-0.1.24-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: duroxide-0.1.24-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 5.0 MB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
843ce9cae1bbb2e4005986d7865b939db5818a52cac5542d2376eecdfc4ba02f
|
|
| MD5 |
4441bf0bcb7e9db7417296f029ae4ef9
|
|
| BLAKE2b-256 |
47f7187f8eaa66f0fa200ec391c6f23773068c4be033e681bbad33ebb0a42693
|
Provenance
The following attestation bundles were made for duroxide-0.1.24-cp312-cp312-win_amd64.whl:
Publisher:
publish.yml on microsoft/duroxide-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duroxide-0.1.24-cp312-cp312-win_amd64.whl -
Subject digest:
843ce9cae1bbb2e4005986d7865b939db5818a52cac5542d2376eecdfc4ba02f - Sigstore transparency entry: 1435997509
- Sigstore integration time:
-
Permalink:
microsoft/duroxide-python@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Branch / Tag:
refs/tags/v0.1.24 - Owner: https://github.com/microsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Trigger Event:
release
-
Statement type:
File details
Details for the file duroxide-0.1.24-cp312-cp312-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: duroxide-0.1.24-cp312-cp312-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 6.8 MB
- Tags: CPython 3.12, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fc684fb1155799955f84ba081ea3315ac9bf6d9be544b8f8ae8c83dff4e67c5
|
|
| MD5 |
3f233830dcb50228316ee048e29122a1
|
|
| BLAKE2b-256 |
b94fa885372a791570b96016dab694b9a06164e6471216236b3c4dfff81908d6
|
Provenance
The following attestation bundles were made for duroxide-0.1.24-cp312-cp312-manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on microsoft/duroxide-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duroxide-0.1.24-cp312-cp312-manylinux_2_28_x86_64.whl -
Subject digest:
1fc684fb1155799955f84ba081ea3315ac9bf6d9be544b8f8ae8c83dff4e67c5 - Sigstore transparency entry: 1435997540
- Sigstore integration time:
-
Permalink:
microsoft/duroxide-python@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Branch / Tag:
refs/tags/v0.1.24 - Owner: https://github.com/microsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Trigger Event:
release
-
Statement type:
File details
Details for the file duroxide-0.1.24-cp312-cp312-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: duroxide-0.1.24-cp312-cp312-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 6.6 MB
- Tags: CPython 3.12, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0456b01778265b251af1273de431993182e3ba0c54e9d7d78305b4522e7f668
|
|
| MD5 |
efc612502164bb57d34cc6ac15e7a2d1
|
|
| BLAKE2b-256 |
e0d7815176f037128bb5b532dca1a7c61d26ee5d26f8f6843d5131d96047b204
|
Provenance
The following attestation bundles were made for duroxide-0.1.24-cp312-cp312-manylinux_2_28_aarch64.whl:
Publisher:
publish.yml on microsoft/duroxide-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duroxide-0.1.24-cp312-cp312-manylinux_2_28_aarch64.whl -
Subject digest:
d0456b01778265b251af1273de431993182e3ba0c54e9d7d78305b4522e7f668 - Sigstore transparency entry: 1435997556
- Sigstore integration time:
-
Permalink:
microsoft/duroxide-python@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Branch / Tag:
refs/tags/v0.1.24 - Owner: https://github.com/microsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Trigger Event:
release
-
Statement type:
File details
Details for the file duroxide-0.1.24-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: duroxide-0.1.24-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 4.7 MB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22be136ac05026c670de03d99193ba50b8d55641ee095a53a1fc7f9374f3e9b1
|
|
| MD5 |
5565033ae0d562defd689a20f771948f
|
|
| BLAKE2b-256 |
017ec3c9ab3507753c95a33c25b748ca5e9ee091fdbb1b54ca3b45c80c986416
|
Provenance
The following attestation bundles were made for duroxide-0.1.24-cp312-cp312-macosx_11_0_arm64.whl:
Publisher:
publish.yml on microsoft/duroxide-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duroxide-0.1.24-cp312-cp312-macosx_11_0_arm64.whl -
Subject digest:
22be136ac05026c670de03d99193ba50b8d55641ee095a53a1fc7f9374f3e9b1 - Sigstore transparency entry: 1435997526
- Sigstore integration time:
-
Permalink:
microsoft/duroxide-python@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Branch / Tag:
refs/tags/v0.1.24 - Owner: https://github.com/microsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Trigger Event:
release
-
Statement type:
File details
Details for the file duroxide-0.1.24-cp312-cp312-macosx_10_12_x86_64.whl.
File metadata
- Download URL: duroxide-0.1.24-cp312-cp312-macosx_10_12_x86_64.whl
- Upload date:
- Size: 5.0 MB
- Tags: CPython 3.12, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8184f34f4d9f802b4caf35564c7de69670aa4ea59605514ce5606f2bc3c3269f
|
|
| MD5 |
a60663dc2ee0faebd66bfa7239f43503
|
|
| BLAKE2b-256 |
616a3c40bafc54707a9488c6a7b8f8cff148a77586597449f4fac145689c83e6
|
Provenance
The following attestation bundles were made for duroxide-0.1.24-cp312-cp312-macosx_10_12_x86_64.whl:
Publisher:
publish.yml on microsoft/duroxide-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duroxide-0.1.24-cp312-cp312-macosx_10_12_x86_64.whl -
Subject digest:
8184f34f4d9f802b4caf35564c7de69670aa4ea59605514ce5606f2bc3c3269f - Sigstore transparency entry: 1435997575
- Sigstore integration time:
-
Permalink:
microsoft/duroxide-python@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Branch / Tag:
refs/tags/v0.1.24 - Owner: https://github.com/microsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5b068f630ddc2f9c4c4019364d49b4daaa2978ee -
Trigger Event:
release
-
Statement type: