Source code for schedium.triggers.sugar.tick
from __future__ import annotations
from datetime import datetime
from schedium.triggers.base import BaseTrigger
from schedium.types.granularity import (
UNIT_TO_GRANULARITY_MAP,
Granularity,
GranularityUnit,
)
from schedium.types.time_window import TimeWindow
[docs]
class Tick(BaseTrigger):
"""
A trigger that always matches, but defines the dedup bucket.
Tick(...) doesn’t control when; it controls how often at most it can run (once per bucket)
when other parts of the trigger tree make it match.
Even though both ``Every(unit=..., interval=1)`` and ``Tick(...)`` match for
all ``now``, they differ in their notion of “next time”. For example, for
``after=10:00:30``:
- ``Tick("minute").next_window(after)`` starts at ``10:00:30``.
- ``Every("minute", interval=1).next_window(after)`` starts at the next
epoch-aligned boundary (typically ``10:01:00``).
Why this is useful:
- When composing constraints with AND, using `Every(unit="week", interval=1)`
can force alignment to week boundaries (e.g. Monday 00:00), which makes
intersection search for schedules like "Monday at 09:30" less direct.
- `Tick(Granularity.WEEK)` keeps the schedule driven by the constraints
(weekday/time), while still guaranteeing the job won't run more than once
per WEEK bucket.
`Tick` is intended mainly for "sugar" helpers (like `Weekly(...)`) and
advanced compositions.
Parameters
----------
granularity : schedium.schemas.granularity.Granularity | schedium.schemas.granularity.GranularityUnit
Deduplication bucket size. If given as a string, it is converted via
:data:`~schedium.schemas.granularity.UNIT_TO_GRANULARITY_MAP`.
"""
def __init__(self, granularity: Granularity | GranularityUnit) -> None:
if isinstance(granularity, str):
granularity = UNIT_TO_GRANULARITY_MAP[granularity]
self.granularity = granularity
[docs]
def required_granularity(self) -> Granularity:
return self.granularity
[docs]
def fallback_granularity(self) -> Granularity:
return self.granularity
[docs]
def matches(self, now: datetime) -> bool:
return True
[docs]
def next_window(
self,
after: datetime,
*,
max_iterations: int = 100_000,
) -> TimeWindow:
return TimeWindow(start=after, end=None)