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.
Metadata
Release files for chrono-daemon 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chrono_daemon-0.1.1.tar.gz | 88.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chrono_daemon-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 126.0 kB
Release files / chrono_daemon-0.1.1.tar.gz
| Download URL | chrono_daemon-0.1.1.tar.gz |
|---|---|
| Size | 88.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bd79c8bc3f28252e5d987d2f86b467074aa9bad2ae3f6c742aef25155ca061f7
|
|
BLAKE2b-256 checksum How to use checksums |
6e3a05b75fb572ff6fe849a94ce0de0b9a45c4ed95a6a18b23c54a170868e891
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / chrono_daemon-0.1.1-py3-none-any.whl
| Download URL | chrono_daemon-0.1.1-py3-none-any.whl |
|---|---|
| Size | 37.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
49060d43c0f982269fea7af3866bb5ed5b19a71d8de8dd4db98ca3865b2606f3
|
|
BLAKE2b-256 checksum How to use checksums |
fe5e39521bff055860357e02bb7bcc17ae3ddc01950bd86734e6c1dfd28b880e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|