Python SDK for the Durable Workflow server (language-neutral HTTP protocol)
Project description
Durable Workflow (Python SDK)
A Python SDK for the Durable Workflow server. Speaks the server's language-neutral HTTP/JSON worker protocol — no PHP runtime required.
Status: Alpha. Core features implemented: workflows, activities, schedules, signals, timers, child workflows, continue-as-new, side effects, version markers, and worker-applied accepted updates. Client calls for queries and updates exist; Python workflow-side query receiver metadata is available, while server-routed Python query execution and pre-accept update validator routing are still in progress. Full language-neutral protocol support for cross-PHP/Python orchestration is the release goal.
Install
pip install durable-workflow
Or for development:
pip install -e '.[dev]'
Quickstart
from durable_workflow import Client, Worker, workflow, activity
@activity.defn(name="greet")
def greet(name: str) -> str:
return f"hello, {name}"
@workflow.defn(name="greeter")
class GreeterWorkflow:
def run(self, ctx, name):
result = yield ctx.schedule_activity("greet", [name])
return result
async def main():
client = Client("http://server:8080", token="dev-token-123", namespace="default")
worker = Worker(
client,
task_queue="python-workers",
workflows=[GreeterWorkflow],
activities=[greet],
)
handle = await client.start_workflow(
workflow_type="greeter",
workflow_id="greet-1",
task_queue="python-workers",
input=["world"],
)
await worker.run_until(workflow_id="greet-1", timeout=30.0)
result = await client.get_result(handle)
print(result) # "hello, world"
For a fuller deployable example, see
examples/order_processing, which runs a
multi-activity order workflow against a local server with Docker Compose.
Activity retries and timeouts
Configure per-call activity retries and deadlines from workflow code:
from durable_workflow import ActivityRetryPolicy
result = yield ctx.schedule_activity(
"charge-card",
[order],
retry_policy=ActivityRetryPolicy(
max_attempts=4,
initial_interval_seconds=1,
backoff_coefficient=2,
maximum_interval_seconds=30,
non_retryable_error_types=["ValidationError"],
),
start_to_close_timeout=120,
schedule_to_close_timeout=300,
heartbeat_timeout=15,
)
Child workflow starts use the same retry policy shape and workflow-level execution/run timeout names:
from durable_workflow import ChildWorkflowRetryPolicy
receipt = yield ctx.start_child_workflow(
"payment.child",
[order],
retry_policy=ChildWorkflowRetryPolicy(
max_attempts=3,
initial_interval_seconds=2,
backoff_coefficient=2,
non_retryable_error_types=["ValidationError"],
),
execution_timeout_seconds=600,
run_timeout_seconds=120,
)
Workflow signals, queries, and updates
Signals mutate workflow state during replay:
@workflow.defn(name="approval")
class ApprovalWorkflow:
def __init__(self) -> None:
self.approved = False
@workflow.signal("approve")
def approve(self, by: str) -> None:
self.approved = True
@workflow.query("status")
def status(self) -> dict:
return {"approved": self.approved}
@workflow.update("set_approval")
def set_approval(self, approved: bool) -> dict:
self.approved = approved
return {"approved": self.approved}
@set_approval.validator
def validate_set_approval(self, approved: bool) -> None:
if not isinstance(approved, bool):
raise ValueError("approved must be boolean")
The Python SDK records query and update receiver metadata on workflow classes,
exposes a query-state replay helper, and applies accepted updates on Python
workflow tasks by emitting complete_update or fail_update back to the
server. Query routing and synchronous pre-accept update validator execution are
still server-side follow-ups; use those paths only with deployments that
advertise support for the target workflow type.
Features
- Async-first: Built on
httpxandasyncio - Type-safe: Full type hints, passes
mypy --strict - Polyglot: Works alongside PHP workers on the same task queue
- HTTP/JSON protocol: No gRPC, no protobuf dependencies
- Codec envelopes: Avro payloads by default, with JSON decode compatibility for existing history
- Metrics hooks: Pluggable counters and histograms, with an optional Prometheus adapter
Authentication
For local servers that use one shared bearer token, pass token=:
client = Client("http://server:8080", token="shared-token", namespace="default")
For production servers with role-scoped tokens, pass separate credentials for control-plane calls and worker-plane polling:
client = Client(
"https://workflow.example.internal",
control_token="operator-token",
worker_token="worker-token",
namespace="orders",
)
Create one client per namespace when your deployment issues namespace-scoped
tokens. The SDK sends the configured token as Authorization: Bearer ... and
the namespace as X-Namespace on every request.
Metrics
Pass a recorder to Client(metrics=...) or Worker(metrics=...) to collect request, poll, and task metrics. The SDK ships a no-op default, an InMemoryMetrics recorder for tests or custom exporter loops, and PrometheusMetrics for deployments that install the optional extra:
pip install 'durable-workflow[prometheus]'
from durable_workflow import Client, PrometheusMetrics
metrics = PrometheusMetrics()
client = Client("http://server:8080", token="dev-token-123", metrics=metrics)
Custom recorders implement increment(name, value=1.0, tags=None) and record(name, value, tags=None).
Documentation
Full documentation is available at durable-workflow.github.io/docs/2.0/polyglot/python:
Requirements
- Python ≥ 3.10
- A running Durable Workflow server
Compatibility
SDK version 0.2.x is compatible with servers that advertise these protocol
manifests from GET /api/cluster/info:
control_plane.version: "2"control_plane.request_contract.schema: durable-workflow.v2.control-plane-request.contractversion1worker_protocol.version: "1.0"
The top-level server version is build identity only. The worker checks these
protocol manifests at startup and fails closed when compatibility is missing,
unknown, or undiscoverable.
Development
# Install dev dependencies
pip install -e '.[dev]'
# Run tests
pytest
# Run integration tests (requires Docker)
pytest -m integration
# Type check
mypy src/durable_workflow/
# Lint
ruff check src/ tests/
# Preview the API reference site locally
pip install -e '.[docs]'
mkdocs serve
The API reference is published to python.durable-workflow.com and rebuilt automatically on push to main.
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 Distribution
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 durable_workflow-0.3.1.tar.gz.
File metadata
- Download URL: durable_workflow-0.3.1.tar.gz
- Upload date:
- Size: 76.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57ecc0bc18c6c7c8aad8ec4287f15165e48bb7ce651753f9ea0c91c100f92b58
|
|
| MD5 |
9d0f5b14b94ec5954a81a393e8df0ec1
|
|
| BLAKE2b-256 |
a8e1d98fa7962b40d8f586ca0bf225eded880f1278972dbddbf65e8b752be3da
|
File details
Details for the file durable_workflow-0.3.1-py3-none-any.whl.
File metadata
- Download URL: durable_workflow-0.3.1-py3-none-any.whl
- Upload date:
- Size: 48.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e82668cb2e4dcc5558430aea9f491aa77076f362857d81937a33d6e959464d8c
|
|
| MD5 |
9930119cea9458f4f599e7edcdf03f89
|
|
| BLAKE2b-256 |
e6671f1e06303efb49152651e4e76d5d6d8998bf69bf116541b9fb5219a43f43
|