Skip to content

Task

grelmicro.task

Task.

Each name loads its submodule on first access.

TaskError

Bases: GrelmicroError

Base grelmicro Task error.

Tasks

Tasks(
    *,
    auto_start: bool = True,
    tasks: list[Task] | None = None,
    timezone: str | None = None,
    shutdown_timeout: float | None = None,
    env_prefix: str | None = None,
    env_load: bool | None = None,
)

Bases: TaskRouter, Reconfigurable[TasksConfig]

Tasks.

Tasks class, the main entrypoint to manage scheduled tasks.

Supports live reconfiguration of shutdown_timeout via reconfigure(new_config). A swap applies to the next shutdown, not to a drain already under way. timezone is startup-only: changing the zone under a running cron task would move which wall-clock fire counts as due, and the durable last-fire state would either replay a fire or swallow one. See Live reconfiguration.

Initialize Tasks.

PARAMETER DESCRIPTION
auto_start

Automatically start all tasks.

TYPE: bool DEFAULT: True

tasks

A list of tasks to be started.

TYPE: list[Task] | None DEFAULT: None

timezone

The IANA timezone name every cron task uses.

A cron task that passes its own timezone=, and a TaskRouter that declares one, keep theirs.

Default: "UTC". When unset and env reads are enabled (see env_load and GREL_ENV_LOAD), resolves from GREL_TASK_TIMEZONE, then from the app-wide GREL_TIMEZONE, before falling back to the default.

TYPE: str | None DEFAULT: None

shutdown_timeout

Seconds to let running tasks finish their current unit of work on shutdown before they are force-cancelled. On exit a stop signal is raised so tasks unwind as soon as their in-flight work completes; this only bounds how long a task stuck mid-work delays shutdown.

Defaults to 30.0, matching Kubernetes' terminationGracePeriodSeconds. Keep it at or below the pod grace period so draining finishes before SIGKILL. Set to 0 to cancel immediately without draining.

When unset and env reads are enabled, resolves from GREL_TASK_SHUTDOWN_TIMEOUT.

TYPE: float | None DEFAULT: None

env_prefix

Override the auto-derived environment variable prefix.

Default: GREL_TASK_. Tasks takes no registration name, so two instances in one process read the same variables. Pass a prefix here to separate them.

TYPE: str | None DEFAULT: None

env_load

Whether to read environment variables.

When None (the default), follow the process-wide GREL_ENV_LOAD flag. Pass True or False to override the flag for this construction.

TYPE: bool | None DEFAULT: None

RAISES DESCRIPTION
SettingsValidationError

If a setting fails validation.

timezone property

timezone: str

The IANA timezone name cron tasks use unless they set their own.

from_config classmethod

from_config(
    config: TasksConfig,
    *,
    auto_start: bool = True,
    tasks: list[Task] | None = None,
) -> Self

Construct a Tasks from a pre-built TasksConfig.

PARAMETER DESCRIPTION
config

The pre-built tasks configuration.

Use this path when the configuration is assembled at startup from a settings tree (for example YAML, Vault, or a pydantic-settings aggregator). The environment path is bypassed and the config is used as-is.

TYPE: TasksConfig

auto_start

Automatically start all tasks.

TYPE: bool DEFAULT: True

tasks

A list of tasks to be started.

TYPE: list[Task] | None DEFAULT: None

start async

start() -> None

Start all tasks manually.

TasksConfig

Bases: BaseModel

Tasks Config.

timezone class-attribute instance-attribute

timezone: TimeZoneName = TimeZoneName(UTC_NAME)

IANA timezone every cron task uses unless it, or the TaskRouter holding it, sets its own.

shutdown_timeout class-attribute instance-attribute

shutdown_timeout: NonNegativeFloat = 30.0

Seconds to let running tasks finish their current unit of work on shutdown before they are force-cancelled.

TaskRouter

TaskRouter(
    *,
    tasks: list[Task] | None = None,
    timezone: str | None = None,
)

Task Router.

TaskRouter class, used to group task schedules, for example to structure an app in multiple files. It would then be included in the Tasks, or in another TaskRouter.

Initialize the task router.

PARAMETER DESCRIPTION
tasks

A list of tasks to be scheduled.

TYPE: list[Task] | None DEFAULT: None

timezone

The IANA timezone name every cron task in this router uses.

A cron task that passes its own timezone= keeps it. When None (the default), the router takes the timezone of the Tasks or TaskRouter that includes it, and falls back to "UTC" when nothing sets one.

TYPE: str | None DEFAULT: None

RAISES DESCRIPTION
TimezoneError

If no timezone of that name can be loaded.

tasks property

tasks: list[Task]

List of scheduled tasks.

timezone property

timezone: str | None

The timezone this router declares, or None when it declares none.

A router reports only what it was given. The timezone its tasks end up using is resolved by the owning Tasks when it starts, and a router does not read the value it would inherit.

add_task

add_task(task: Task) -> None

Add a task to the scheduler.

Marks the function a task exposes as function, so a decorator applied below the one that registered it is refused instead of wrapping calls the schedule will never make. IntervalTask and CronTask both expose it. A Task of your own is marked when it does the same, and left alone when it does not.

every

every(
    *,
    interval: int | timedelta,
    name: str | None = None,
    gate: Literal["claim"]
    | TaskLock
    | LeaderElection
    | None = None,
    sync: LockPrimitive | None = None,
) -> Callable[
    [Callable[..., Any | Awaitable[Any]]],
    Callable[..., Any | Awaitable[Any]],
]

Decorate a function to run it on a fixed interval.

Every worker runs it by default. Pass gate="claim" to run it on one worker per interval, or a LeaderElection to run it on the elected worker.

PARAMETER DESCRIPTION
interval

The duration between each task run.

Takes whole seconds or a timedelta, up to 100 years. A float is refused. Write timedelta(milliseconds=500) for an interval under a second.

Accuracy is not guaranteed and may vary with system load. Consider the execution time of the task when setting the interval.

TYPE: int | timedelta

name

The name of the task.

If None, a name will be generated automatically from the function. A gate locks under the task name, unless it is a TaskLock with a name of its own, so a name passed here must then be a valid lock name, like Lock, that does not start with task-. A generated name that is not one, such as __main__:job, locks under task-__main__:job.

TYPE: str | None DEFAULT: None

gate

Which workers run each interval.

  • None (the default): every worker runs every interval.
  • "claim": one worker claims each interval and runs it. The claim is held for the whole interval, with a lease of two intervals.
  • A TaskLock: one worker claims each interval, with the lock's own lease_duration, backend and worker. Its min_hold_duration must be at least interval, now and on every later reconfigure. A lock without a name takes the task name.
  • A LeaderElection: only the elected worker runs the task. It claims each interval as with "claim", so a leader handover never runs one interval twice.

A gated task renews its claim every third of the lease from the moment it holds it until the body ends, so a body, and a wait for a sync lock before it, may take as long as needed. A worker that crashes frees its claim within the lease. A claim lost anyway, to a backend outage or a stalled event loop, logs a warning and lets the body finish.

TYPE: Literal['claim'] | TaskLock | LeaderElection | None DEFAULT: None

sync

Optional resource-level synchronization primitive.

Wraps the body once the gate lets this worker through. Use a Lock to serialise execution against a shared resource. Which workers run the task is set by gate, not this parameter.

TYPE: LockPrimitive | None DEFAULT: None

RAISES DESCRIPTION
FunctionTypeError

If the task name generation fails.

ValueError

If interval is not whole seconds or a timedelta, is not greater than zero, or is over 100 years.

ValueError

If the gate TaskLock already gates another task.

SettingsValidationError

If the gate TaskLock holds a claim for less than interval, or the gate locks under a name that is not a valid lock name or starts with task-.

TypeError

If gate is not a supported value, or sync is a leader election.

cron

cron(
    expr: str,
    *,
    timezone: str | None = None,
    name: str | None = None,
    misfire_grace: int | timedelta | None = None,
    backend: ScheduleBackend | None = None,
    gate: Literal["claim"]
    | LeaderElection
    | Unset
    | None = UNSET,
    sync: LockPrimitive | None = None,
) -> Callable[
    [Callable[..., Any | Awaitable[Any]]],
    Callable[..., Any | Awaitable[Any]],
]

Decorate function to add it as a cron task.

Runs the task whenever the wall-clock time matches the cron expression in the given timezone.

Every worker runs each fire by default. Left out beside a schedule backend, gate is reported as ungated-cron each time the task starts, except in development and test. With gate="claim" or a LeaderElection, each fire is claimed against a durable last-fire state, so the task runs at most once across every worker per fire. A fire missed while every worker was down replays once on restart, bounded by misfire_grace, and only the most recent missed fire runs.

The claim guarantee is at-most-once. A worker that claims a fire and then crashes mid-run does not retry it, because the last-fire state already advanced. Make the body idempotent, or wrap it with @retry, when correctness depends on completion.

PARAMETER DESCRIPTION
expr

The 5-field cron expression: minute hour day-of-month month day-of-week.

Each field supports *, */step, a-b, a-b/step, a comma list, and a bare integer. Day of week is 0-6 with 0 = Sunday (7 also means Sunday). When both day-of-month and day-of-week are restricted, a day matches if it matches either.

TYPE: str

timezone

The IANA timezone name used to compute fire times.

When None (the default), the task takes the timezone configured on the Tasks or TaskRouter it belongs to, and falls back to "UTC" when nothing sets one. Pass a name here to pin one task to a different timezone than the rest.

TYPE: str | None DEFAULT: None

name

The name of the task.

If None, a name will be generated automatically from the function. Also used as the schedule name for the durable last-fire state.

TYPE: str | None DEFAULT: None

misfire_grace

How late a missed fire may run when a worker comes back.

Takes whole seconds or a timedelta, up to 100 years. A float is refused.

Read only by a gated task. A fire missed while every worker was down replays once on restart only when now is within this duration of the fire. Past the budget, the fire is dropped. None (default) sets no budget, so any missed fire replays once, however late. Only the most recent missed fire ever runs, never a backlog.

TYPE: int | timedelta | None DEFAULT: None

backend

The durable schedule backend.

Read only by a gated task. By default, resolves through the active Grelmicro app's Coordination component.

TYPE: ScheduleBackend | None DEFAULT: None

gate

Which workers run each fire.

  • None: every worker runs every fire. Left out, it is the same, and the task reports ungated-cron each time it starts beside a schedule backend, except in development and test. Pass None to say every worker on purpose.
  • "claim": one worker claims each fire against the durable schedule backend and runs it. A fire missed while every worker was down replays once on restart.
  • A LeaderElection: only the elected worker runs the task. It claims each fire as with "claim", so a leader handover never runs one fire twice. A fire missed while no worker led replays once when a worker becomes the leader.

A gated task with no schedule backend in scope reports a coordination error on every fire and runs nothing.

TYPE: Literal['claim'] | LeaderElection | Unset | None DEFAULT: UNSET

sync

Optional resource-level synchronization primitive.

Wraps the body once the gate lets this worker through. Use a Lock to serialise execution against a shared resource. Which workers run the task is set by gate, not this parameter.

TYPE: LockPrimitive | None DEFAULT: None

RAISES DESCRIPTION
FunctionTypeError

If the task name generation fails.

CronError

If the cron expression is invalid.

TimezoneError

If the timezone is not an IANA timezone name.

TypeError

If gate is not a supported value, or sync is a leader election.

ValueError

If backend is passed without a gate.

ValueError

If misfire_grace is not whole seconds or a timedelta, is not greater than zero, or is over 100 years.

include_router

include_router(router: TaskRouter) -> None

Include another router in this router.

RAISES DESCRIPTION
TaskAddOperationError

If the tasks have already started.

started

started() -> bool

Check if the task manager has started.

do_mark_as_started

do_mark_as_started() -> None

Mark the task manager as started.

Do not call this method directly. It is called by the task manager when the task manager is started.