Skip to main content

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:


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 use unittest.IsolatedAsyncioTestCase which requires Python 3.8+.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_strict_timer-0.1.0.tar.gz (5.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_strict_timer-0.1.0-py3-none-any.whl (5.4 kB view details)

Uploaded Python 3

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

Hashes for async_strict_timer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 51c75754c63d26e98d00606edf922ba0b4af538d4459d374f8c455e37ed25439
MD5 7cf36f41f647e3aeefd786e19d003538
BLAKE2b-256 1255ff6c5352b3a0adc70b77cb726ef9b7dc61675df574f06552c57a8745a95a

See more details on using hashes here.

File details

Details for the file async_strict_timer-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for async_strict_timer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 70b38d34c73621bda33ba429de5328eda5a86cc60f3c31420dbe6489853aa7d3
MD5 2263f4701faa2e950acb1e4ca3b757b5
BLAKE2b-256 de86533af9e404511363050dd1ebf2f075488cbb39fd90eea611b1cd6dac2f83

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page