RealtimeClock¶
Module: chronopype.clocks.realtime
Inherits: BaseClock
A clock implementation that synchronizes with wall-clock time. It handles drift compensation automatically.
Constructor¶
RealtimeClock(
config: ClockConfig,
error_callback: Callable[[TickProcessor, Exception], None] | None = None
)
Raises: ClockError if config.clock_mode is not ClockMode.REALTIME.
Methods¶
run()¶
Run the clock indefinitely. Each tick fires at tick_size intervals. The clock compensates for drift --- if a tick takes longer than the tick interval, the next tick fires immediately.
Cancel via asyncio.Task.cancel() or by exiting the context manager.
run_til(target_time)¶
Run the clock until target_time is reached. The target is in the clock's timestamp domain; internally the clock calculates the wall-clock duration as target_time - current_tick and runs for that duration.
Raises: ClockError if the clock is already running or not in a context.
Example¶
import asyncio
import time
from chronopype import ClockConfig, ClockMode
from chronopype.clocks import RealtimeClock
from chronopype.processors import TickProcessor
class HeartbeatProcessor(TickProcessor):
async def async_tick(self, timestamp: float) -> None:
print(f"Heartbeat at {timestamp:.2f}")
async def main():
config = ClockConfig(
clock_mode=ClockMode.REALTIME,
start_time=time.time(),
tick_size=1.0,
)
async with RealtimeClock(config) as clock:
clock.add_processor(HeartbeatProcessor())
# Run for 30 seconds
await clock.run_til(config.start_time + 30)
asyncio.run(main())
Drift Handling¶
When a processor takes longer than tick_size, the clock:
- Detects the drift (elapsed time - tick_size)
- Fires the next tick immediately (no sleep)
- Continues compensating until caught up
This ensures the total number of ticks over time remains accurate, even if individual ticks are delayed.