Skip to main content

modern-di-arq

PyPI version Supported Python versions Downloads Coverage CI License GitHub stars uv Ruff ty

modern-di integration for arq.

Full guide: arq integration docs

Usage example: examples/

Installation

uv add modern-di-arq      # or: pip install modern-di-arq

Usage

setup_di seeds the root container into arq's ctx dict and wires four of arq's lifecycle hooks: on_startup/on_shutdown open and close the root container, on_job_start builds a Scope.REQUEST child container per job, and on_job_end closes it as a fallback.

Decorate a task with @inject to resolve its FromDI-marked parameters from that per-job child. The child is open from on_job_start until the last @inject-decorated call for the job exits, which closes it. Nested and concurrent (asyncio.gather) @inject calls over the same job share the one child through a reference count, so it closes exactly once, and teardown still happens if arq skips on_job_end.

import typing

from arq.connections import RedisSettings
from modern_di import Container, Group, Scope, providers
from modern_di_arq import FromDI, inject, setup_di


class Settings:
    def __init__(self) -> None:
        self.greeting = "hello"


class Greeter:
    def __init__(self, settings: Settings) -> None:  # auto-injected by type
        self._settings = settings

    def greet(self, name: str) -> str:
        return f"{self._settings.greeting}, {name}"


class AppGroup(Group):
    settings = providers.Factory(Settings, scope=Scope.APP, cache=True)
    greeter = providers.Factory(Greeter, scope=Scope.REQUEST)


@inject
async def greet(
    ctx: dict[str, typing.Any],  # arq passes its context dict as the first argument
    name: str,
    greeter: typing.Annotated[Greeter, FromDI(Greeter)],  # resolve by type
) -> str:
    return greeter.greet(name)


class WorkerSettings:
    functions = [greet]
    redis_settings = RedisSettings(host="localhost")


container = Container(groups=[AppGroup])
setup_di(WorkerSettings, container)
container.validate()  # optional fail-fast check of the provider graph

Run the worker as usual (arq mymodule.WorkerSettings) and enqueue jobs with only their real arguments, as in await pool.enqueue_job("greet", "world"). The FromDI parameters are resolved for you. A task must declare arq's ctx dict as its first parameter; injection is order-insensitive otherwise. arq's ctx is a plain dict (not a dedicated message type), so no context provider is registered. Read job metadata from ctx, and fetch_di_container(ctx) returns the root container.

Sharing a container

When one container serves several apps or workers, only one owner should open and close the root. Otherwise the first worker to shut down closes it for everyone else. Pass manage_lifespan=False and own the root yourself:

container = Container(groups=[AppGroup])
setup_di(WorkerSettings, container, manage_lifespan=False)

async with container:
    ...  # run the worker and the other apps here

The worker still builds and closes a child container per job, and your own on_startup and on_shutdown hooks still run. APP-scoped finalizers run once, when the async with block exits.

API

Symbol Description
setup_di(worker_settings, container, *, manage_lifespan=True) Seeds the root container into arq's ctx and wires root + per-job lifecycle onto on_startup/on_shutdown/on_job_start/on_job_end. With manage_lifespan=False, on_startup and on_shutdown leave the root container alone and you open and close it yourself. Accepts a WorkerSettings class/object or a settings dict; composes with existing hooks; returns the container. Raises TypeError if called twice on the same worker_settings
FromDI(dependency) Inert marker for Annotated[T, FromDI(...)] in task signatures; accepts a provider instance or a type
inject(task) Decorator that resolves FromDI-annotated parameters from the per-job Scope.REQUEST child. Order-insensitive; passthrough for tasks with no FromDI; raises TypeError at decoration if the task also declares *args/**kwargs. Raises RuntimeError naming setup_di when a job reaches it without the modern-di hooks installed
fetch_di_container(ctx) Returns the root container from an arq ctx dict

📦 PyPI

📝 License

Part of modern-python

Built on modern-di, a dependency-injection framework with an IoC container and scopes.

Browse the full list of templates and libraries in modern-python; the org profile has the categorized index.

Metadata

Release files for modern-di-arq 4.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modern-di-arq 4.1.0
File Size Uploaded
modern_di_arq-4.1.0.tar.gz 7.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for modern-di-arq 4.1.0
File Interpreter ABI Platform
modern_di_arq-4.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 14.6 kB

Release files / modern_di_arq-4.1.0.tar.gz

Download URL modern_di_arq-4.1.0.tar.gz
Size 7.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e04f0a7a595ed0a00227fef4d9bd15dca6da1693fa612ba950aafb897a7c366e
BLAKE2b-256 checksum
How to use checksums
222ca6770323f2201d64f3f909443f0cb2f1c367b316702486a9c5665025bc1c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / modern_di_arq-4.1.0-py3-none-any.whl

Download URL modern_di_arq-4.1.0-py3-none-any.whl
Size 7.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7ba914eeb36c7e5adb6f80eda091f35739d09b4fab743ef14da2681bc430874c
BLAKE2b-256 checksum
How to use checksums
f6b45be98cc6a32f73f4ce8317bfc6e3abb3e2f1f58e86f9527e0e469ef1cf32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

4.1.3

2 release files

4.1.2

2 release files

4.1.1

2 release files

This release

4.1.0 This release

2 release files

4.0.0

2 release files

3.2.0

2 release files

3.1.2

2 release files

3.1.1

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page