Releases: modern-python/modern-di-celery
Release list
2.1.0
modern-di-celery 2.1.0 — adopt the modern-di integration kit
Maintenance release. No public API change — FromDI, inject,
DITask, setup_di, and fetch_di_container keep their signatures and
behavior. Swaps this package's hand-rolled FromDI marker, its
parse/resolve pair, and the __modern_di_injected__ double-wrap guard for
the shared primitives in
modern_di.integrations,
shipped in
modern-di 2.28.0.
Ninth of 13 planned adapter conversions across the modern-di ecosystem
(after
modern-di-starlette 2.2.0,
modern-di-fastapi 2.10.0,
modern-di-litestar 2.13.0,
modern-di-aiohttp 2.2.0,
modern-di-flask 2.1.0,
modern-di-faststream 2.10.0,
modern-di-typer 2.3.0,
and
modern-di-grpc 2.1.0).
Like typer, Celery has no connection/context object — a task invocation
carries no framework request — so this is a Layer-2-only conversion.
Unlike typer, this repo uses is_injected/mark_injected: DITask's
existing double-wrap guard maps directly onto the kit's formalized
primitives.
Internal refactors
_FromDIis gone;FromDI = integrations.from_di.injectnow callsintegrations.parse_markersdirectly —
_parse_inject_paramsis deleted entirely, its body having been an exact
duplicate.- Resolution goes through
integrations.resolve_markers; the double-wrap
guard usesintegrations.mark_injected/integrations.is_injected
(DITask.__init__and both ofinject's marking points). - The task-specific mechanism — the
*args/**kwargsdecoration-time
TypeErrorguard, by-name signature binding, and the deliberate
non-use offunctools.wraps— is preserved exactly unchanged; none of
it is part of the kit. - Dead imports removed:
dataclasses,providers,T_co. architecture/dependency-injection.md's "Resolution" and "DITask"
sections promoted to describe the new internals.
Packaging
- Bumps the
modern-difloor to>=2.28,<3.
Downstream
No action needed — the public API (FromDI, inject, DITask,
setup_di, fetch_di_container) is unchanged. Upgrading only requires
modern-di>=2.28.0.
Internals
- 100% line coverage;
ruff,tyclean across Python 3.10–3.14. - Built via
subagent-driven-development: 4 planned tasks, each with an
independent spec+quality review; a whole-branch review before merge.
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.