Tiny anyio-based concurrency primitives: Channel, Clock (wall + sim), Daemon, Supervisor.
Project description
chrono-daemon
chrono-daemon is a small Python library for long-running async components whose time can be replayed deterministically. It wraps anyio with four primitives:
Channel[T]: typed single-producer / single-consumer queues.Clock: real time withWallClock, virtual time withSimClock.Daemon: a lifecycle unit withon_start,run, andon_stop.Supervisor: a structured-concurrency root for hosting daemons.
The practical payoff is simple: production daemons sleep on ctx.clock; tests
swap in SimClock and advance seconds of work without waiting for wall time.
Quick Example
from chrono_daemon import Channel, Context, SimClock, Supervisor, daemon, open_channel
@daemon
async def producer(ctx: Context, out: Channel[int]) -> None:
for i in range(10):
await ctx.clock.sleep(0.1)
await out.send.send(i)
await out.send.aclose()
@daemon
async def consumer(ctx: Context, src: Channel[int]) -> None:
async for item in src.recv:
ctx.logger.info("got %d", item)
async def main() -> None:
ch: Channel[int] = open_channel(maxsize=4)
clock = SimClock()
async with Supervisor(clock=clock) as sup:
sup.add(producer(ch))
sup.add(consumer(ch))
await clock.advance(1.5) # replay 1.5 s of work immediately
Why Use It
- Time-dependent async code is testable without sleeps, polling, or fake task schedulers.
- Wiring is explicit. Every edge is a named SPSC channel, so ownership and backpressure stay visible.
- Lifecycle behavior is structured. Daemons get startup, shutdown, logging, cancellation, and error policy in one place.
- The core runtime is small: pure Python,
anyiounderneath, and no runtime dependency beyondanyio.
Core API
| What it is | |
|---|---|
Channel[T] |
typed bounded SPSC queue, the only inter-daemon communication primitive |
Clock |
WallClock (real time) or SimClock (deterministic, burst advance(dt) / advance_to(t)) |
Daemon |
long-running async unit; on_start / run / on_stop hooks. Use a subclass or the @daemon decorator |
Supervisor |
async with Supervisor(...) structured-concurrency root; hosts daemons, dispatches errors (shutdown / restart / ignore) |
See docs/concepts.md for details.
Scope
chrono-daemon is for in-process async systems where explicit wiring and deterministic time matter: evaluation loops, agent pipelines, robotics-style control mocks, and testable service internals.
It is intentionally not a topic broker, service registry, RPC framework, parameter server, CLI launcher, network runtime, or continuous-time numerical simulator.
Install / dev
uv sync --dev --extra zmq
make check # ruff + pyrefly
make test # pytest on asyncio + trio
make all # format + check + test
Python 3.11+. Only runtime dependency is anyio>=4; --extra zmq installs
the optional transport dependencies needed for the full test suite.
Remote ZMQ channels are optional:
pip install "chrono-daemon[zmq]"
Where to look next
docs/concepts.md: what each primitive is and the invariants the test suite pins.docs/adr/: why each decision looks the way it does (Topic-less, on-error-shutdown by default, anyio-only).docs/recipes.md: patterns kept off the core surface but importable underchrono_daemon.recipes.*, grouped as routing, coordination, state, buffering, and hosting helpers undersrc/chrono_daemon/recipes/.examples/: runnable System 2/1/0 demos and notes.
Status
Early. In-process channels are the default and keep zero runtime dependencies
beyond anyio. ZMQ remote channels are available as an optional extra
(chrono-daemon[zmq]) for asyncio-backed deployments.
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 chrono_daemon-0.1.1.tar.gz.
File metadata
- Download URL: chrono_daemon-0.1.1.tar.gz
- Upload date:
- Size: 88.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd79c8bc3f28252e5d987d2f86b467074aa9bad2ae3f6c742aef25155ca061f7
|
|
| MD5 |
172880c05bac6e937bdb4ba349458d28
|
|
| BLAKE2b-256 |
6e3a05b75fb572ff6fe849a94ce0de0b9a45c4ed95a6a18b23c54a170868e891
|
File details
Details for the file chrono_daemon-0.1.1-py3-none-any.whl.
File metadata
- Download URL: chrono_daemon-0.1.1-py3-none-any.whl
- Upload date:
- Size: 37.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49060d43c0f982269fea7af3866bb5ed5b19a71d8de8dd4db98ca3865b2606f3
|
|
| MD5 |
f6076fb3b38bab4807401433c1556bef
|
|
| BLAKE2b-256 |
fe5e39521bff055860357e02bb7bcc17ae3ddc01950bd86734e6c1dfd28b880e
|