Skip to content

Releases: modern-python/modern-di-arq

Release list

2.0.0

Choose a tag to compare

@github-actions github-actions released this 11 Jul 09:33
c03af70

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's ctx dict (read back with fetch_di_container(ctx)) and wraps all
    four of arq's lifecycle hooks: on_startup/on_shutdown open and close the
    root (container.open() reopens idempotently across a worker restart or
    test re-entry with no ContainerClosedWarning; close_async() runs
    APP-scoped finalizers on shutdown), and on_job_start/on_job_end build
    and close one Scope.REQUEST child per job — closed even on the task's
    error path, since arq always fires on_job_end. Any hook the caller already
    set on worker_settings still runs, composed around the wrapped step.
    Accepts both a class/object worker_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.REQUEST child. Injection is parameter-order-insensitive — FromDI
    params may appear anywhere in the signature. Unlike the Celery/aiogram
    integrations, inject does not rewrite the task's signature: arq never
    binds a task's signature, so functools.wraps is used as-is, and the
    wrapper only needs to stay async def to satisfy arq's
    inspect.iscoroutinefunction check.
  • Fail-fast on *args/**kwargs. A task combining a FromDI parameter
    with *args/**kwargs raises TypeError at decoration time instead of
    misrouting arguments at call time — inspect.Signature.bind would 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's WorkerSettings.functions list is
    heterogeneous (plain coroutines, arq.func(...) wrappers, import strings),
    so blanket wrapping is out of scope for v1.
  • No connection ContextProvider: arq's per-job ctx is a bare dict, not
    an injectable connection object, matching the Celery and Typer
    integrations.
  • Tests run against a real Redis (docker-compose, a redis:8 service) and
    drive a real burst Worker, not direct coroutine calls, so hook
    composition, ctx merging, and task dispatch are exercised end to end.
  • 100% line coverage; ruff, ty, and eof-fixer clean across Python
    3.10–3.14.
  • Uses the portable planning convention (planning/) with an architecture/
    truth home; releases are tag-driven.