Releases: modern-python/modern-di-celery
Release list
2.0.1
modern-di-celery 2.0.1 — @inject variadic fail-fast
A patch release fixing silent argument corruption in @inject.
Fix
-
@injectnow rejects*args/**kwargsat decoration time. Since 2.0.0,
@injectbinds a task's arguments to its visible signature by name (which is
what makes injection parameter-order-insensitive). That by-name call could not
faithfully forward variadic parameters:inspect.Signature.bindstores
*args/**kwargsvalues under the literal names"args"/"kwargs", so a
task declaring*args/**kwargsalongside aFromDIparameter had its
real payload silently misrouted into a keyword argument (e.g. a caller's
(1, 2, foo="bar")arrived inside the task asargs=(), kwargs={"args": (1, 2), "kwargs": {"foo": "bar"}}) instead of raising.@injectnow raises a clearTypeErrorat decoration time when a task
declares aVAR_POSITIONALorVAR_KEYWORDparameter together with aFromDI
parameter, naming the offending parameter and pointing at explicit named
parameters as the fix. A task with noFromDIparameter is returned
unchanged and may use*args/**kwargsfreely.
Downstream
- Potentially breaking, intentionally. A task that previously combined
@inject/DITaskwith*args/**kwargsand aFromDIparameter was already
broken at runtime (silently corrupted arguments); it now fails loudly at import
time. Replace the variadic parameters with explicit named parameters.
Internals
- 100% line coverage;
ruff,ty, andeof-fixerclean. Two new tests cover
theVAR_POSITIONALandVAR_KEYWORDrejection paths.
2.0.0
modern-di-celery 2.0.0 — first release
First release of the Celery integration for modern-di. It wires a
modern-di container into a
Celery 5.x app and resolves dependencies into task
handlers, scoped per task. The version starts at 2.0.0 to track the modern-di
2.x ecosystem alongside the other official integrations.
A Celery task is a plain callable with no Depends-style seam, so this
integration follows modern-di's decorator path (like modern-di-typer),
fully synchronous — a natural fit, since Celery workers are synchronous.
Feature
setup_di(app, container). Stores the root container onapp.conf(read
back withfetch_di_container(app)), and wires the container's lifecycle to
Celery's worker-process signals — reopening it onworker_process_init(each
forked worker process gets its own open container) and closing it on
worker_process_shutdown. Returns the container.- Per-task child containers.
injectopens oneScope.REQUESTchild
container per task, resolves the marked dependencies, and closes it
(close_sync) when the task finishes — including on the error path. FromDI+@inject. Mark a task parameter with
Annotated[T, FromDI(provider_or_type)]and decorate the task with@inject;
the decorator resolves each marked parameter and rewrites the task signature
so Celery binds only the caller's real arguments. Injection is
parameter-order-insensitive —FromDIparams may appear anywhere in the
signature.DITaskbase class. SetCelery(task_cls=DITask)or@app.task(base=DITask)
to inject every task without a per-task@inject;DITaskappliesinject
to the task'srunand resets the task header so Celery still binds only real
args.
Packaging
- Requires
celery>=5,<6andmodern-di>=2.25,<3; Python 3.10–3.14. - Ships
py.typed; zero runtime dependencies beyond Celery and modern-di. - Resolution and finalizers are synchronous; there is no connection object (a
Celery task carries no framework request object).
Downstream
No action needed — this is a new package.
Internals
- 100% line coverage;
ruff,ty, andeof-fixerclean across Python
3.10–3.14. Tests use Celery's eager execution (task_always_eager). - Uses the portable planning convention (
planning/) with anarchitecture/
truth home; releases are tag-driven.