Skip to main content

Tick-based task runner with shared timestamps for Chumicro applications.

Project description

chumicro-runner

A tick-based service pattern for Chumicro libraries.

Components implement a check(now_ms) -> bool check that gates when a handler fires. A Runner captures time once per tick, checks each service, and batch-fires all due handlers — replacing ad-hoc polling loops with a single standard contract.

Installation

# CPython (pip)
pip install chumicro-runner

# CircuitPython (circup)
circup bundle-add ChuMicro/chumicro-bundle
circup install chumicro-runner

# MicroPython (mip)
mpremote mip install github:ChuMicro/chumicro-bundle/chumicro_runner

For experimental (pre-release) versions from the develop branch:

pip install chumicro-runner-experimental
circup install chumicro-runner-experimental
mpremote mip install github:ChuMicro/chumicro-bundle/chumicro_runner_experimental

Quick example

from chumicro_runner import Runner


class TemperatureSensor:
    """Alert when temperature exceeds a threshold."""

    def __init__(self, threshold=30.0):
        self._threshold = threshold
        self._last_reading = 0.0

    def read_temperature(self):
        """Read from hardware — fast I2C or ADC operation."""
        # On a real board: return self._i2c_device.temperature
        return self._last_reading

    def check(self, now_ms):
        self._last_reading = self.read_temperature()
        return self._last_reading > self._threshold

    def handle(self, now_ms):
        print(f"ALERT: {self._last_reading}°C exceeds {self._threshold}°C")


runner = Runner()
sensor = TemperatureSensor(threshold=30.0)
runner.add(sensor, period_ms=5000)  # check every 5 seconds

while True:
    runner.tick()

For simple periodic tasks, no service class is needed:

from chumicro_runner import Runner

runner = Runner()
runner.add_periodic(lambda now_ms: print("blink!"), period_ms=500)

while True:
    runner.tick()

What's included

Core

Symbol Description
Runner(ticks=None) Tick-based service loop with shared timestamps
Runner.add(task, handler=None, period_ms=None, start_after_ms=None, run_count=None) Register a task; returns a TaskHandle
Runner.add_periodic(handler, period_ms, start_after_ms=None, run_count=None) Register a periodic handler; returns a TaskHandle
Runner.tick() Capture time, check services, batch-fire handlers; returns now_ms
TaskHandle Opaque handle for runtime mutation of a registered service
TaskHandle.set_period(period_ms) Add, change, or remove the period (None to remove)
TaskHandle.remove() Remove this service from the runner
TaskHandle.period_ms Read-only: the service period, or None
TaskHandle.run_count Read-only: remaining run count, or None if unlimited
TaskHandle.active Read-only: whether the service is still registered

Testing

Symbol Description
CallRecorder() Callable that records handler invocations for test assertions
CallRecorder.calls Direct access to the list of recorded now_ms values

Registration patterns

Object-based (with .check() and .handle())

Pass an object that has check(now_ms) -> bool and handle(now_ms) methods. The runner calls .check(); if it returns True, .handle() is queued:

class MotionDetector:
    def __init__(self):
        # On a real board: self._pin = digitalio.DigitalInOut(board.D5)
        pass

    def detect_motion(self):
        """Read PIR sensor pin — fast digital read."""
        # On a real board: return self._pin.value
        return False

    def check(self, now_ms):
        return self.detect_motion()

    def handle(self, now_ms):
        print("Motion!")

runner.add(MotionDetector())

Callable-based (check function + handler)

Pass a callable check function and a handler. Both can be lambdas, bound methods, or regular functions:

runner.add(
    lambda now_ms: sensor.ready(),
    handler=lambda now_ms: process(sensor.read()),
)

Handler-only (no check, fires every tick)

Pass just a handler with no service check:

runner.add(handler=lambda now_ms: poll_buttons(now_ms))

Periodic (fires every N milliseconds)

No service check needed — the handler fires on schedule:

handle = runner.add_periodic(toggle_led, period_ms=500)
handle.set_period(1000)  # change rate at runtime

Runtime mutation

add() and add_periodic() return a TaskHandle for runtime changes:

handle = runner.add(sensor, period_ms=5000)

# Speed up.
handle.set_period(1000)

# Remove the period — service runs every tick.
handle.set_period(None)

# Remove entirely.
handle.remove()

Testing your components

The chumicro_runner.testing module provides CallRecorder for verifying that handlers fire at the right times:

from chumicro_runner.testing import CallRecorder
from chumicro_timing.testing import FakeTicks

fake = FakeTicks()
recorder = CallRecorder()
runner = Runner(ticks=fake)
runner.add_periodic(recorder, period_ms=100)

runner.tick()
assert len(recorder) == 0  # not due yet

fake.advance(100)
runner.tick()
assert recorder.calls == [100]

Platform support

All classes use only basic Python features. Works identically on CPython, MicroPython, and CircuitPython.

Memory notes

  • _TaskEntry and TaskHandle use __slots__ to minimise per-instance memory.
  • Handlers are collected into a pre-allocated list and batch-fired, avoiding per-tick allocation.

Docs

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

chumicro_runner-0.1.0.tar.gz (12.4 kB view details)

Uploaded Source

Built Distribution

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

chumicro_runner-0.1.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file chumicro_runner-0.1.0.tar.gz.

File metadata

  • Download URL: chumicro_runner-0.1.0.tar.gz
  • Upload date:
  • Size: 12.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for chumicro_runner-0.1.0.tar.gz
Algorithm Hash digest
SHA256 86f35784ac3b80152b761b7ebfc278a225d68e51c44a8c624ecbac74454d5568
MD5 2cab401681a85d6871381d1d03f31405
BLAKE2b-256 ea6b6ee7c859389e4e313b6ba3c35b2941f6603c0da1dc6074e34688d12fac36

See more details on using hashes here.

Provenance

The following attestation bundles were made for chumicro_runner-0.1.0.tar.gz:

Publisher: release.yml on ChuMicro/ChuMicro

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for chumicro_runner-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 94d39d43ef6b649c82b0858ce3c9bf64e4f476b2368d9dceb482b186e3d9532e
MD5 4e31197288e2a85b39dc0b826ca7b3c3
BLAKE2b-256 d0cb7b7f72c855899566dc1f104d025a30d88f5df5cbd9a6c6b2db21557c242e

See more details on using hashes here.

Provenance

The following attestation bundles were made for chumicro_runner-0.1.0-py3-none-any.whl:

Publisher: release.yml on ChuMicro/ChuMicro

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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