modern-di-arq 2.0.0 — first release
First release of the arq integration for
modern-di. It wires a modern-di
container into an arq worker and resolves dependencies into job handlers,
scoped per job. The version starts at 2.0.0 to track the modern-di 2.x
ecosystem alongside the other official integrations.
An arq task is a plain coroutine with no Depends-style seam, so this
integration follows modern-di's decorator path (like modern-di-celery
and modern-di-typer), and is fully async — a natural fit, since arq
workers are async end to end.
Feature
setup_di(worker_settings, container). Seeds the root container into
arq'sctxdict (read back withfetch_di_container(ctx)) and wraps all
four of arq's lifecycle hooks:on_startup/on_shutdownopen and close the
root (container.open()reopens idempotently across a worker restart or
test re-entry with noContainerClosedWarning;close_async()runs
APP-scoped finalizers on shutdown), andon_job_start/on_job_endbuild
and close oneScope.REQUESTchild per job — closed even on the task's
error path, since arq always fireson_job_end. Any hook the caller already
set onworker_settingsstill runs, composed around the wrapped step.
Accepts both a class/objectworker_settings(attribute access) and a
dict(item access). Returns the container.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 from the job's
Scope.REQUESTchild. Injection is parameter-order-insensitive —FromDI
params may appear anywhere in the signature. Unlike the Celery/aiogram
integrations,injectdoes not rewrite the task's signature: arq never
binds a task's signature, sofunctools.wrapsis used as-is, and the
wrapper only needs to stayasync defto satisfy arq's
inspect.iscoroutinefunctioncheck.- Fail-fast on
*args/**kwargs. A task combining aFromDIparameter
with*args/**kwargsraisesTypeErrorat decoration time instead of
misrouting arguments at call time —inspect.Signature.bindwould otherwise
store those catch-alls under the literal keys"args"/"kwargs", which the
wrapper's keyword-unpacking call to the task would silently break. Use
explicit named parameters instead.
Packaging
- Requires
arq>=0.25,<1(the floor is pinned-tested: the full suite passes
against arq 0.25.0 on Python 3.10 with a real Redis) and
modern-di>=2.25,<3; Python 3.10–3.14. - Ships
py.typed; zero runtime dependencies beyond arq and modern-di.
Downstream
No action needed — this is a new package.
Internals
- No
@inject-only auto-inject path: arq'sWorkerSettings.functionslist is
heterogeneous (plain coroutines,arq.func(...)wrappers, import strings),
so blanket wrapping is out of scope for v1. - No connection
ContextProvider: arq's per-jobctxis a baredict, not
an injectable connection object, matching the Celery and Typer
integrations. - Tests run against a real Redis (
docker-compose, aredis:8service) and
drive a real burstWorker, not direct coroutine calls, so hook
composition,ctxmerging, and task dispatch are exercised end to end. - 100% line coverage;
ruff,ty, andeof-fixerclean across Python
3.10–3.14. - Uses the portable planning convention (
planning/) with anarchitecture/
truth home; releases are tag-driven.