Skip to content

Commit d868f87

Browse files
committed
Update example to a new, full app
1 parent a9feed0 commit d868f87

File tree

9 files changed

+622
-101
lines changed

9 files changed

+622
-101
lines changed

examples/in_memory_backend_example.py

Lines changed: 0 additions & 101 deletions
This file was deleted.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# In-Memory Backend Example
2+
3+
This example demonstrates how to structure a Durable Task Python project
4+
so that orchestrators and activities are defined separately from
5+
infrastructure code, enabling fast, fully in-process unit tests using the
6+
in-memory backend.
7+
8+
You can copy this entire folder into a new directory and run it as a
9+
standalone project.
10+
11+
## Folder Structure
12+
13+
```text
14+
in_memory_backend_example/
15+
├── README.md
16+
├── requirements.txt
17+
├── src/
18+
│ ├── __init__.py
19+
│ ├── workflows.py # Orchestrators & activities (pure logic, no infra)
20+
│ └── app.py # Runs workflows against a real DTS backend
21+
└── test/
22+
├── __init__.py
23+
└── test_workflows.py # Unit tests using the in-memory backend
24+
```
25+
26+
### Key ideas
27+
28+
- **[src/workflows.py](src/workflows.py)** — All orchestrator and activity
29+
definitions live here. They import only from `durabletask` and have no
30+
dependency on a particular backend, making them portable and testable.
31+
32+
- **[src/app.py](src/app.py)** — The entry point that wires up the real
33+
Durable Task Scheduler (or emulator) client and worker, registers the
34+
workflow functions, and schedules an orchestration.
35+
36+
- **[test/test_workflows.py](test/test_workflows.py)** — Pytest tests that
37+
exercise every workflow path using `create_test_backend()`. No sidecar,
38+
emulator, or Azure resources are needed.
39+
40+
## Getting Started
41+
42+
1. Copy this folder to a new location and `cd` into it:
43+
44+
```bash
45+
cd in_memory_backend_example
46+
```
47+
48+
1. Create and activate a virtual environment:
49+
50+
Bash:
51+
52+
```bash
53+
python -m venv .venv
54+
source .venv/bin/activate
55+
```
56+
57+
PowerShell:
58+
59+
```powershell
60+
python -m venv .venv
61+
.\.venv\Scripts\Activate.ps1
62+
```
63+
64+
1. Install dependencies:
65+
66+
```bash
67+
pip install -r requirements.txt
68+
```
69+
70+
## Running the Tests
71+
72+
From the `in_memory_backend_example/` directory:
73+
74+
```bash
75+
pytest test/
76+
```
77+
78+
## Running the App Against the Emulator
79+
80+
Start the DTS emulator, then from the `in_memory_backend_example/`
81+
directory:
82+
83+
```bash
84+
python -m src.app
85+
```
86+
87+
## Patterns Demonstrated
88+
89+
| Pattern | Where |
90+
|---|---|
91+
| Activity chaining | `process_order` — validate → calculate → pay |
92+
| Fan-out / fan-in | `process_order` — ship all items in parallel |
93+
| Sub-orchestration | `order_with_approval` calls `process_order` |
94+
| Human interaction (external event + timer) | `order_with_approval` — wait for approval or timeout |
95+
| Error handling | Tests verify validation failures propagate correctly |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
durabletask
2+
durabletask-azuremanaged
3+
azure-identity
4+
pytest
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Microsoft Corporation.
3+
# Licensed under the MIT License.
4+
5+
"""
6+
Run the order-processing workflow against a real Durable Task Scheduler backend.
7+
8+
Usage (emulator — no env vars needed):
9+
python -m src.app
10+
11+
Usage (Azure):
12+
export ENDPOINT=https://<scheduler>.durabletask.io
13+
export TASKHUB=<taskhub>
14+
python -m src.app
15+
"""
16+
17+
import os
18+
19+
from azure.identity import DefaultAzureCredential
20+
21+
from durabletask import client
22+
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
23+
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
24+
25+
from src.workflows import (
26+
Order,
27+
OrderItem,
28+
calculate_total,
29+
order_with_approval,
30+
process_order,
31+
process_payment,
32+
send_confirmation,
33+
ship_item,
34+
validate_order,
35+
)
36+
37+
38+
def main():
39+
# Use environment variables if provided, otherwise default to the emulator.
40+
taskhub_name = os.getenv("TASKHUB", "default")
41+
endpoint = os.getenv("ENDPOINT", "http://localhost:8080")
42+
43+
print(f"Using taskhub: {taskhub_name}")
44+
print(f"Using endpoint: {endpoint}")
45+
46+
secure_channel = endpoint.startswith("https://")
47+
credential = DefaultAzureCredential() if secure_channel else None
48+
49+
with DurableTaskSchedulerWorker(
50+
host_address=endpoint,
51+
secure_channel=secure_channel,
52+
taskhub=taskhub_name,
53+
token_credential=credential,
54+
) as w:
55+
# Register all orchestrators and activities
56+
w.add_orchestrator(process_order)
57+
w.add_orchestrator(order_with_approval)
58+
w.add_activity(validate_order)
59+
w.add_activity(calculate_total)
60+
w.add_activity(process_payment)
61+
w.add_activity(send_confirmation)
62+
w.add_activity(ship_item)
63+
w.start()
64+
65+
c = DurableTaskSchedulerClient(
66+
host_address=endpoint,
67+
secure_channel=secure_channel,
68+
taskhub=taskhub_name,
69+
token_credential=credential,
70+
)
71+
72+
# Build a sample order
73+
order = Order(
74+
customer="Contoso",
75+
items=[
76+
OrderItem(name="Widget", quantity=3, unit_price=25.00),
77+
OrderItem(name="Gadget", quantity=1, unit_price=99.99),
78+
],
79+
)
80+
81+
# Schedule and run the orchestration
82+
instance_id = c.schedule_new_orchestration(process_order, input=order)
83+
print(f"Orchestration scheduled with ID: {instance_id}")
84+
85+
state = c.wait_for_orchestration_completion(instance_id, timeout=60)
86+
if state and state.runtime_status == client.OrchestrationStatus.COMPLETED:
87+
print(f"Orchestration completed! Result: {state.serialized_output}")
88+
elif state:
89+
print(f"Orchestration failed: {state.failure_details}")
90+
else:
91+
print("Orchestration timed out.")
92+
93+
94+
if __name__ == "__main__":
95+
main()

0 commit comments

Comments
 (0)