Metadata-Version: 2.4
Name: durable-workflow
Version: 2.5.0
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.

## Cooperative cancellation release candidate

Use `request_cancellation()` for bounded, replayable workflow cleanup. Opt in
against a Server that advertises protocol 1.20 and the required capabilities:
set `DURABLE_WORKFLOW_WORKER_PROTOCOL_VERSION=1.20` and 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

```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_WORKER_PROTOCOL_VERSION=1.20` before starting
Compose and `DURABLE_WORKFLOW_COOPERATIVE_QUALIFICATION=1` for pytest. These cases
fail if the runtime does not discover the required capability. Ordinary CI
keeps protocol 1.19 and skips this unpublished feature's connected cases.

## License

[MIT](LICENSE)
