Metadata-Version: 2.4
Name: durable-workflow
Version: 2.5.13
Summary: Python client and worker SDK for Durable Workflow Cloud and self-hosted Server
Author: Durable Workflow Contributors
License-Expression: MIT
Project-URL: Homepage, https://python.durable-workflow.com/
Project-URL: Documentation, https://python.durable-workflow.com/
Project-URL: Repository, https://github.com/durable-workflow/sdk-python
Project-URL: Issues, https://github.com/durable-workflow/sdk-python/issues
Keywords: cloud,durable-execution,workflow,durable,orchestration,python,sdk,saga
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Requires-Dist: fastavro<2,>=1.12.2
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: playwright<2,>=1.48; extra == "dev"
Requires-Dist: PyYAML>=6.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "dev"
Provides-Extra: prometheus
Requires-Dist: prometheus-client>=0.20; extra == "prometheus"
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.25; extra == "docs"
Requires-Dist: playwright<2,>=1.48; extra == "docs"
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "docs"
Dynamic: license-file

# Durable Workflow Python SDK

[![CI](https://github.com/durable-workflow/sdk-python/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/durable-workflow/sdk-python/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/durable-workflow.svg)](https://pypi.org/project/durable-workflow/)
[![Python](https://img.shields.io/pypi/pyversions/durable-workflow.svg)](https://pypi.org/project/durable-workflow/)
[![License](https://img.shields.io/github/license/durable-workflow/sdk-python.svg)](LICENSE)

Build durable Python workflows and activities against [Durable Workflow
Cloud](https://cloud.durable-workflow.com/) or a
[self-hosted Server](https://github.com/durable-workflow/server). The SDK uses
the same language-neutral runtime protocol as the first-party PHP and Rust
SDKs.

## Install

```bash
pip install durable-workflow
```

Python 3.10 or newer is required.

## Quickstart

```python
import asyncio
from uuid import uuid4

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():
    workflow_id = f"greet-{uuid4().hex}"
    async with Client(
        "http://server:8080",
        token="dev-token-123",
        namespace="default",
    ) as client:
        worker = Worker(
            client,
            task_queue="python-workers",
            workflows=[GreeterWorkflow],
            activities=[greet],
        )
        handle = await client.start_workflow(
            workflow_type="greeter",
            workflow_id=workflow_id,
            task_queue="python-workers",
            input=["world"],
        )
        await worker.run_until(workflow_id=workflow_id, timeout=30.0)
        result = await client.get_result(handle)
        print(result)  # "hello, world"

if __name__ == "__main__":
    asyncio.run(main())
```

Pass the Server origin to `Client` without a trailing `/api`. For Cloud, pass
the complete namespace runtime URL exactly as provisioned. Cloud client and
worker processes use separate runtime credentials:

```python
client = Client(
    runtime_url,
    control_token=client_token,
    worker_token=worker_token,
    namespace=namespace,
)
```

Keep the client token in application processes and the worker token in worker
processes when deploying them separately.

## Capabilities

- Workflows, activities, child workflows, timers, and continue-as-new
- Signals, queries, validated updates, schedules, and message streams
- Activity retries, timeouts, cancellation, and heartbeats
- Deterministic parallel work, side effects, version markers, and sagas
- Replay verification and an in-process workflow test environment
- Avro payloads, external payload storage, metrics, and interceptors

See the [capability matrix](https://durable-workflow.com/docs/2.0/capabilities/)
for the complete cross-SDK contract.

## Documentation

- [Python SDK portal and API reference](https://python.durable-workflow.com/)
- [Python SDK guide](https://durable-workflow.com/docs/2.0/polyglot/python/)
- [Complete SDK reference](docs/sdk-reference.md)
- [Runnable examples](examples/)
- [Symmetric SDK playground](https://github.com/durable-workflow/sample-app#symmetric-sdk-playground)

## Runtime choices

Use [Durable Workflow Cloud](https://cloud.durable-workflow.com/early-access)
for a managed namespace, or run the published
[`durableworkflow/server`](https://hub.docker.com/r/durableworkflow/server)
image yourself. Workflow and activity type names, task queues, and payloads are
portable between both runtime choices.

## Compatibility

Stable `2.x` SDK releases follow semantic versioning and negotiate runtime
capabilities with Server at startup. Use stable `2.x` SDK and Server channels
for new applications. The [compatibility guide](https://durable-workflow.com/docs/2.0/compatibility/)
documents protocol and upgrade guarantees.

## Worker protocol and cooperative cancellation

Workers use protocol 1.20 by default. For a Server that supports only 1.19, set
`DURABLE_WORKFLOW_WORKER_PROTOCOL_VERSION=1.19`. This changes the request protocol
without enabling optional worker features.

Use `request_cancellation()` for bounded, replayable workflow cleanup. Opt in
against a Server that advertises protocol 1.20 and the required capabilities:
include `cooperative_cancellation` in the Worker's `capabilities`. Durable local callback
admission also needs `prepared_local_activities`, with
`prepared_local_activity_cancellation_policies` for explicit local policies.
Independently cancellable scopes remain disabled.

The cooperative worker supervises async and synchronous activity callbacks
independently of application heartbeats. Existing terminal cancellation remains
available. See the [cancellation guide](docs/cooperative-cancellation-design.md)
for immutable context, operation policies, shielded cleanup and recovery.

## Development

The installed `durable-workflow-replay-conformance` command runs an offline
Python replay shard. Its `sdk-python` artifact pin must match the installed
distribution version reported by `importlib.metadata.version("durable-workflow")`.
The report records that installed identity separately from the declared platform
tuple and fails if metadata is missing or the SDK pin differs. Replay success
does not prove installation or execution of the other declared artifacts; the
aggregate runner supplies its own verified installation receipts.

```bash
pip install -e '.[dev]'
ruff check src/ tests/
mypy src/durable_workflow/
pytest tests/ -m "not integration"
```

Integration tests use Docker:

```bash
export COMPOSE_PROJECT_NAME=sdk-python-local
docker compose -f docker-compose.test.yml up -d --build --wait
SERVER_PORT=$(docker compose -f docker-compose.test.yml port server 8080 | sed 's/.*://')
DURABLE_WORKFLOW_SERVER_URL="http://127.0.0.1:$SERVER_PORT" DURABLE_WORKFLOW_AUTH_TOKEN=test-token pytest tests/integration/ -v
docker compose -f docker-compose.test.yml down -v
```

Candidate cooperative cancellation qualification is explicit. In a manual CI
run, supply an exact public `server_commit` and set `cooperative_qualification`
to true. CI verifies that checkout, builds the candidate Server, enables protocol
1.20, runs the connected cases and retains JUnit, raw observations, image
authority and exact source provenance. An optional exact `native_commit` mounts
that public Native checkout read-only into the test stack. The image's published
Composer authority stays intact and the evidence identifies the source overlay.
These are source qualification runs. For a local candidate, set
`DURABLE_WORKFLOW_COOPERATIVE_QUALIFICATION=1` for pytest. These cases fail if the
runtime does not discover the required capability. Ordinary CI uses the SDK's
default protocol and skips the optional connected cooperative cases.

## License

[MIT](LICENSE)
