A precise, cancellation-safe asyncio timer for Python that avoids drift
Project description
asynctimer
Deterministic, asyncio-native timer for predictable, repeated execution
Overview
asynctimer is a precise, cancellation-safe asyncio timer for Python that avoids drift and enforces fixed scheduling semantics. It is intentionally conservative and explicit, making it suitable for protecting external systems (APIs, databases, hardware) where bursts and ambiguity are unacceptable.
Why this library exists
Many timer utilities:
- implicitly drift over time
- allow bursts by default
- rely on best-effort timing
- do not clearly specify stop or overrun behavior
asynctimer takes a different approach:
Behavior is explicit, testable, and deterministic — even if that costs some throughput, though in case of timers that's virtually never a concern.
Why not just use asyncio.sleep()?
A common way to implement a repeating task is:
while True:
await asyncio.sleep(interval)
do_work()
This approach introduces timer drift because:
- execution time is added to the interval
- event loop scheduling latency compounds over time
- For long-running tasks, this drift becomes observable and problematic.
- No cancellation mechanism
Installation
pip install asynctimer
What it does
Implements a repeating, async-friendly timer that calls a user-provided callback at a fixed interval until stopped. Callbacks may be synchronous functions or async coroutines.
Timer Guarantees
- Callbacks never execute concurrently
- Calling stop() guarantees that no further callbacks will be scheduled or started after it returns.
- Drift behavior is explicitly configurable
- Overrun behavior is well-defined and tested
API
Timer(timeout: int | float, callback: Callback, schedule_policy: str = "FIXED_SCHEDULE")— create a timer with a repeat interval, callback, and drift policy.- timeout_ns: Tick interval in nanoseconds
- callback:
Callable[[], None | Awaitable[None]]- May be a synchronous function or an async coroutine
- Takes no arguments
- Returns nothing
- schedule_policy: Drift policy
"FIXED_SCHEDULE"(default)"FIXED_DELAY"
start()— start the timer loop (non-blocking; schedules repeated executions).stop()— stop the timer loop.
Drift / Schedule Policy
Timers can be scheduled in two fundamentally different ways.
Fixed Schedule (drift-free)
next_tick = initial_start + N × interval
- Anchored to the original schedule
- Prevents long-term drift
- Suitable for:
- heartbeats
- simulations
- clock-aligned tasks
from asynctimer import Timer
# callback can be async too
def callback():
print('tick')
timer = Timer(
2000_000_000,
callback,
schedule_policy="FIXED_SCHEDULE"
)
timer.start() # starts the repeating timer
# ... later: timer.stop()
Fixed Delay (completion-relative)
next_tick = callback_completion + interval
- Guarantees spacing between executions
- Suitable for:
- retries
- polling
- cooldowns
- background maintenance
from asynctimer import Timer
timer = Timer(
2000_000_000,
callback,
schedule_policy="FIXED_DELAY"
)
timer.start()
Overrun Behavior
If a callback takes longer than the interval:
- Missed ticks are not executed retroactively
- The timer resumes according to its original schedule policy
This avoids burst execution and keeps behavior predictable.
Stop Semantics
Calling stop() guarantees:
- No further callbacks will be scheduled or started after it returns.
Example
import asyncio
from asynctimer import Timer
# callback can be async too
def callback():
print('tick')
async def main():
timer = Timer(2000_000_000, callback)
timer.start() # starts the repeating timer
await asyncio.sleep(10)
timer.stop()
asyncio.run(main())
Working Examples
For concrete examples showing actual working code using these classes, see the example code in the examples/ directory:
- examples/BasicTimerExample.py — basic
Timerusage - examples/MultipleTimersExample.py — using multiple
Timerinstances - examples/SchedulePolicyTimerExample.py — using
SchedulePolicywithTimer
Build Package from Source
The project uses pyproject.toml. To build distribution archives install build and run:
pip install --upgrade build
python -m build
The above produces a dist/ folder with .whl and .tar.gz files. You can also install locally with:
pip install . # install from source
pip install -e . # editable install for development
Alternatively, this repository includes build_package.py; you can run it if you prefer (it wraps standard build steps):
python build_package.py
Run Tests
The tests are in the tests/ directory and use unittest's async test support. You can run them with unittest or with pytest.
Run with unittest (cross-platform):
python -m unittest discover -v
Run with pytest (if installed):
pip install pytest
pytest -q
Platform-specific helper scripts are provided:
- Windows:
run_tests.bat - Unix/macOS:
run_tests.sh
Notes & Troubleshooting
- The utilities depend only on Python's standard library (
asyncio,datetime, etc.). Tests useunittest.IsolatedAsyncioTestCasewhich requires Python 3.8+.
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 async_strict_timer-0.1.0.tar.gz.
File metadata
- Download URL: async_strict_timer-0.1.0.tar.gz
- Upload date:
- Size: 5.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51c75754c63d26e98d00606edf922ba0b4af538d4459d374f8c455e37ed25439
|
|
| MD5 |
7cf36f41f647e3aeefd786e19d003538
|
|
| BLAKE2b-256 |
1255ff6c5352b3a0adc70b77cb726ef9b7dc61675df574f06552c57a8745a95a
|
File details
Details for the file async_strict_timer-0.1.0-py3-none-any.whl.
File metadata
- Download URL: async_strict_timer-0.1.0-py3-none-any.whl
- Upload date:
- Size: 5.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70b38d34c73621bda33ba429de5328eda5a86cc60f3c31420dbe6489853aa7d3
|
|
| MD5 |
2263f4701faa2e950acb1e4ca3b757b5
|
|
| BLAKE2b-256 |
de86533af9e404511363050dd1ebf2f075488cbb39fd90eea611b1cd6dac2f83
|