Source code for action0.celery_sched.tasks

"""
Check that every scheduled task is actually registered with the Celery app.

Beat sends tasks by *name*, and a misspelled name is not noticed until the
first run is due — possibly hours after a deploy — and then only as an
error on the worker. :py:func:`check_tasks` catches it at startup.

The app only knows the tasks whose modules were imported. Beat imports them
(``imports``/``include`` and autodiscovery) right before it sends the
``beat_init`` signal, which makes that the place to check:

.. code-block:: python

    from celery.signals import beat_init


    @beat_init.connect
    def check_schedule(sender, **kwargs):
        check_tasks(sender.app)
"""

from collections.abc import Mapping
from typing import Any

from celery import Celery

from action0.celery_sched.errors import UnknownTaskError


[docs] def check_tasks(app: Celery, schedule: Mapping[str, Mapping[str, Any]] | None = None) -> None: """ Raise if a schedule entry refers to a task the app hasn't registered. >>> app = Celery("example") >>> @app.task(name="myapp.tasks.poll") ... def poll() -> None: ... >>> check_tasks(app, {"Poll feed": {"task": "myapp.tasks.poll"}}) >>> check_tasks(app, {"Report": {"task": "myapp.tasks.report"}}) Traceback (most recent call last): ... action0.celery_sched.errors.UnknownTaskError: unregistered tasks: 'Report' -> myapp.tasks.report :param app: the Celery app whose task registry to check against :param schedule: the entries to check, in ``beat_schedule`` shape (default: the app's own ``beat_schedule`` setting, so entries configured some other way are checked too) :raises UnknownTaskError: listing every entry whose task is unregistered """ # the setting is untyped (Any), so give the result a declared type of its own entries: Mapping[str, Mapping[str, Any]] = ( (app.conf.beat_schedule or {}) if schedule is None else schedule ) registered = app.tasks missing = { name: entry["task"] for name, entry in entries.items() if entry["task"] not in registered } if missing: raise UnknownTaskError(missing)