Distributed Durable Functions in Python
Project description
Stent
Distributed durable functions for Python. Write reliable, stateful workflows using async/await.
pip install stent
Quick Example
import asyncio
from stent import Stent
@Stent.durable
async def process_order(order_id: str) -> dict:
await asyncio.sleep(1) # Simulate work
return {"order_id": order_id, "status": "processed"}
@Stent.durable
async def order_workflow(order_ids: list[str]) -> list:
results = []
for order_id in order_ids:
result = await process_order(order_id)
results.append(result)
return results
async def main():
backend = Stent.backends.SQLiteBackend("workflow.db")
async with Stent.bootstrap(backend, serve=True) as executor:
result = await order_workflow(["ORD-001", "ORD-002"])
print(result)
asyncio.run(main())
Why Stent?
| Feature | Temporal | Celery | Prefect | Airflow | Stent |
|---|---|---|---|---|---|
| Durable Execution | Yes | No | Partial | No | Yes |
| Setup Complexity | High | Medium | Medium | High | Very Low |
| Infrastructure | Server cluster | Broker | Server | Multi-component | SQLite/Postgres |
| Native Async | Yes | No | Yes | Limited | Yes |
Stent fills the gap between simple task queues (Celery) and enterprise platforms (Temporal):
- vs Temporal: Same durability guarantees, fraction of the infrastructure
- vs Celery/Dramatiq: True workflow durability, not just task retries
- vs Prefect/Airflow: Application workflows, not batch data pipelines
See full comparison for details.
Features
- Durable Execution - Workflow state survives crashes and restarts
- Auto-dispatch - Durable functions work like normal
awaitcalls viaStent.use()orStent.bootstrap() - Automatic Retries - Configurable retry policies with exponential backoff
- Distributed Workers - Scale horizontally across multiple processes
- Parallel Execution - Fan-out/fan-in with
fn.map(),fn.starmap(), andasyncio.gather - Rate Limiting - Control concurrent executions per function
- External Signals - Coordinate workflows with external events
- Dead Letter Queue - Inspect and replay failed tasks
- Cancellation - Cancel running or pending executions with
executor.cancel() - Idempotency & Caching - Prevent duplicate work
- Multiple Backends - SQLite (dev) or PostgreSQL (production)
- Testing Fixtures - Built-in pytest fixtures via
stent.testing - OpenTelemetry - Distributed tracing support
Key Concepts
from stent import Stent, RetryPolicy
# Decorator works with or without parens
@Stent.durable
async def my_activity(data: dict) -> dict:
...
# Configurable options
@Stent.durable(
retry_policy=RetryPolicy(max_attempts=5, initial_delay=1.0),
queue="high_priority",
max_concurrent=10,
idempotent=True,
)
async def my_configured_activity(data: dict) -> dict:
...
# Durable sleep (doesn't block workers) — asyncio.sleep >= 1s auto-promotes
await asyncio.sleep(30) # becomes durable inside execution context
# Parallel execution via .map()
results = await my_activity.map([item1, item2, item3])
# External signals
payload = await Stent.wait_for_signal("approval")
await executor.send_signal(exec_id, "approval", {"approved": True})
# Cancellation
await executor.cancel(exec_id)
Setup
# One-liner with bootstrap (recommended)
async with Stent.bootstrap(backend, serve=True) as executor:
result = await my_workflow()
# Or manual setup
backend = Stent.backends.SQLiteBackend("stent.db")
await backend.init_db()
executor = Stent.use(backend=backend)
worker = asyncio.create_task(executor.serve())
await executor.wait_until_ready()
Backends
# SQLite (development)
backend = Stent.backends.SQLiteBackend("stent.db")
# PostgreSQL (production)
backend = Stent.backends.PostgresBackend("postgresql://user:pass@host/db")
# Optional: Redis for low-latency notifications
executor = Stent(
backend=backend,
notification_backend=Stent.notifications.RedisBackend("redis://localhost")
)
Result Type
from stent import Result
result = Result.Ok(42)
result.unwrap() # 42
result.unwrap_or(0) # 42
result.map(str) # Result.Ok("42")
err = Result.Error("oops")
err.unwrap_or(0) # 0
bool(err) # False
Testing
# conftest.py
pytest_plugins = ["stent.testing"]
# test_my_workflow.py
async def test_workflow(stent_worker):
result = await my_durable_fn(42)
assert result == 84
CLI
stent list # List executions
stent show <exec_id> # Show execution details
stent dlq list # List dead-lettered tasks
stent dlq replay <task_id> # Replay failed task
Documentation
Full documentation available in docs/:
- Getting Started | Core Concepts | Comparison
- Guides: Durable Functions | Orchestration | Error Handling | Parallel Execution | Signals | Workers | Monitoring
- Patterns: Saga | Batch Processing
- Reference: API | Configuration | Deployment
Examples
See examples/ for complete workflows:
simple_flow.py- Basic workflowsaga_trip_booking.py- Saga pattern with compensationbatch_processing.py- Fan-out/fan-inmedia_pipeline.py- Complex multi-stage pipeline
Requirements
- Python 3.12+
aiosqliteorasyncpg(backend)redis(optional, for notifications)
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 stent-1.1.2.tar.gz.
File metadata
- Download URL: stent-1.1.2.tar.gz
- Upload date:
- Size: 87.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86995fb46041acc4320c528c3ad97113f75b029699b4eaa343735c1cd08f4756
|
|
| MD5 |
b65007c1e2bbbdc495a6c556edd96ea2
|
|
| BLAKE2b-256 |
1a2b2ecf5149858a12cc8f1a1b755828392abfc73f80f0d15a8fdb015bbfed6e
|
File details
Details for the file stent-1.1.2-py3-none-any.whl.
File metadata
- Download URL: stent-1.1.2-py3-none-any.whl
- Upload date:
- Size: 107.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10769f4c846127b18e04b1e884f375f1780efa4c8900c98b184531e437a249e7
|
|
| MD5 |
95ef6833303e028ef63387a5b5f653ba
|
|
| BLAKE2b-256 |
6d02251a8cf6948584c6a988ecde37c02401f92dbfd4f0966ac6ebe0f54bfaab
|