Skip to content

Commit 5986d68

Browse files
committed
feat: reorganize core
1 parent eb81a95 commit 5986d68

File tree

20 files changed

+511
-205
lines changed

20 files changed

+511
-205
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Duron is a Python library that makes async work _replayable_. You can pause, res
88
- 🧵 **Async-first** — Write `async def` functions and orchestrate them with familiar `await` syntax.
99
- 🔍 **Typed & traceable** — Decorators capture type hints so inputs and outputs serialize cleanly.
1010
- 🗄️ **Storage-agnostic** — Start with file-based logging or plug in your own backend to match your stack.
11-
- 🚀 **Drop-in ready** — Works in CLI tools, web backends, or long-lived agents—no special runtime required.
11+
- 🚀 **Drop-in ready** — Works in CLI tools, web backends, or long-lived agents—no special runtime or extra dependencies required.
1212

1313
## Install
1414

docs/index.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: Duron
3+
hide:
4+
- toc
5+
- navigation
6+
---
7+
8+
## Install
9+
10+
Duron requires **Python 3.10+**.
11+
12+
```bash
13+
pip install git+https://github.com/brian14708/duron.git
14+
```
15+
16+
## Quickstart
17+
18+
Duron defines two kinds of functions:
19+
20+
- `@duron.durable` — deterministic orchestration. It replays from logs, ensuring that control flow only advances when every prior step is known.
21+
- `@duron.effect` — side effects. Wrap anything that touches the outside world (APIs, databases, file I/O). Duron records its return value so it runs once per unique input.
22+
23+
```python
24+
import asyncio
25+
import random
26+
from pathlib import Path
27+
28+
import duron
29+
from duron.contrib.storage import FileLogStorage
30+
31+
32+
@duron.effect
33+
async def work(name: str) -> str:
34+
print("⚡ Preparing to greet...")
35+
await asyncio.sleep(2) # Simulate I/O
36+
print("⚡ Greeting...")
37+
return f"Hello, {name}!"
38+
39+
40+
@duron.effect
41+
async def generate_lucky_number() -> int:
42+
print("⚡ Generating lucky number...")
43+
await asyncio.sleep(1) # Simulate I/O
44+
return random.randint(1, 100)
45+
46+
47+
@duron.durable
48+
async def greeting_flow(ctx: duron.Context, name: str) -> str:
49+
message, lucky_number = await asyncio.gather(
50+
ctx.run(work, name), ctx.run(generate_lucky_number)
51+
)
52+
return f"{message} Your lucky number is {lucky_number}."
53+
54+
55+
async def main():
56+
async with greeting_flow.invoke(FileLogStorage(Path("log.jsonl"))) as job:
57+
await job.start("Alice")
58+
result = await job.wait()
59+
print(result)
60+
61+
62+
if __name__ == "__main__":
63+
asyncio.run(main())
64+
```

docs/reference/contrib.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# `duron.contrib` module
2+
3+
## ::: duron.contrib
4+
5+
options:
6+
show_submodules: true

examples/hello_world.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async def generate_lucky_number() -> int:
3030
return random.randint(1, 100)
3131

3232

33-
@duron.effect(checkpoint=True, initial=lambda: 0, reducer=int.__add__)
33+
@duron.effect(stateful=True, initial=lambda: 0, reducer=int.__add__)
3434
async def count_up(count: int, target: int) -> AsyncGenerator[int, int]:
3535
await asyncio.sleep(0.5)
3636
while count < target:

mkdocs.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ site_url: "https://brian14708.github.io/duron"
33
repo_url: "https://github.com/brian14708/duron"
44
repo_name: "brian14708/duron"
55
nav:
6-
- index.md
6+
- Home:
7+
- index.md
78
- Reference:
89
- reference/duron.md
910
- reference/log.md
1011
- reference/codec.md
1112
- reference/tracing.md
1213
- reference/typing.md
14+
- reference/contrib.md
1315
- Trace UI: https://brian14708.github.io/duron/trace-ui/
1416

1517
theme:
@@ -40,6 +42,7 @@ plugins:
4042
show_source: false
4143
show_submodules: false
4244
show_attribute_values: false
45+
separate_signature: true
4346
merge_init_into_class: true
4447
members: true
4548
inventories:

src/duron/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from duron._core.config import set_config as set_config
22
from duron._core.context import Context as Context
3+
from duron._core.invoke import DurableRun as DurableRun
34
from duron._core.signal import Signal as Signal
45
from duron._core.signal import SignalInterrupt as SignalInterrupt
56
from duron._core.signal import SignalWriter as SignalWriter
67
from duron._core.stream import Stream as Stream
78
from duron._core.stream import StreamClosed as StreamClosed
9+
from duron._core.stream import StreamOp as StreamOp
810
from duron._core.stream import StreamWriter as StreamWriter
911
from duron._decorator.durable import DurableFn as DurableFn
1012
from duron._decorator.durable import durable as durable

src/duron/_core/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,10 @@ def set_config(
2323
*,
2424
codec: Codec | None = None,
2525
) -> None:
26+
"""Set global configuration for Duron.
27+
28+
Args:
29+
codec: The codec to use for serializing and deserializing data.
30+
"""
2631
if codec is not None:
2732
config.codec = codec

0 commit comments

Comments
 (0)