From fd27520ab6af1c6ac83cdc01bf20f28c4df3292a Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sun, 12 Jul 2026 13:03:28 +0300 Subject: [PATCH] docs: full-parity README and concrete docs link Expand the README to the standard integration template (badges, install, usage example, API table, footer) and point the pyproject Documentation URL at the concrete integrations/arq/ docs page instead of the site root. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++--------- pyproject.toml | 2 +- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0643d5a..1a11e27 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,34 @@ # modern-di-arq +[![PyPI version](https://img.shields.io/pypi/v/modern-di-arq.svg)](https://pypi.org/project/modern-di-arq/) +[![Supported Python versions](https://img.shields.io/pypi/pyversions/modern-di-arq.svg)](https://pypi.org/project/modern-di-arq/) +[![Downloads](https://static.pepy.tech/badge/modern-di-arq/month)](https://pepy.tech/projects/modern-di-arq) +[![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/modern-python/modern-di-arq/actions/workflows/ci.yml) +[![CI](https://github.com/modern-python/modern-di-arq/actions/workflows/ci.yml/badge.svg)](https://github.com/modern-python/modern-di-arq/actions/workflows/ci.yml) +[![License](https://img.shields.io/github/license/modern-python/modern-di-arq.svg)](https://github.com/modern-python/modern-di-arq/blob/main/LICENSE) +[![GitHub stars](https://img.shields.io/github/stars/modern-python/modern-di-arq)](https://github.com/modern-python/modern-di-arq/stargazers) +[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +[![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty) + [Modern-DI](https://github.com/modern-python/modern-di) integration for [arq](https://arq-docs.helpmanual.io). -## Install +Full guide: [arq integration docs](https://modern-di.modern-python.org/integrations/arq/) + +## Installation ```bash -pip install modern-di-arq +uv add modern-di-arq # or: pip install modern-di-arq ``` -## Quickstart +## 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, and `on_job_start`/`on_job_end` build and close a `Scope.REQUEST` child container around each job. Decorate a task with `@inject` to resolve its `FromDI`-marked parameters from that per-job child. ```python import typing +from arq.connections import RedisSettings from modern_di import Container, Group, Scope, providers from modern_di_arq import FromDI, inject, setup_di @@ -22,24 +38,54 @@ class Settings: self.greeting = "hello" -class Dependencies(Group): - settings = providers.Factory(scope=Scope.APP, creator=Settings) +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, name: str, settings: typing.Annotated[Settings, FromDI(Dependencies.settings)]) -> str: - return f"{settings.greeting}, {name}" +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") -setup_di(WorkerSettings, Container(groups=[Dependencies], validate=True)) +setup_di(WorkerSettings, Container(groups=[AppGroup], validate=True)) ``` -`setup_di` wires the root container's lifecycle to arq's `on_startup`/`on_shutdown` -and seeds it into arq's `ctx` dict; a per-job `Scope.REQUEST` child is built in -`on_job_start` and closed in `on_job_end`. Decorate individual tasks with -`@inject` to resolve `FromDI` markers from that child. See the -[documentation](https://modern-di.modern-python.org). +Run the worker as usual (`arq mymodule.WorkerSettings`) and enqueue jobs with only their real arguments — `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. + +## API + +| Symbol | Description | +|---|---| +| `setup_di(worker_settings, container)` | 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`. 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` | +| `fetch_di_container(ctx)` | Returns the root container from an arq `ctx` dict | + +## 📦 [PyPI](https://pypi.org/project/modern-di-arq) + +## 📝 [License](LICENSE) + +## Part of `modern-python` + +Built on [`modern-di`](https://github.com/modern-python/modern-di), a dependency-injection framework with IoC container and scopes. + +Browse the full list of templates and libraries in +[`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index. diff --git a/pyproject.toml b/pyproject.toml index fe1ec87..64ac331 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ version = "0" [project.urls] Homepage = "https://modern-di.modern-python.org" -Documentation = "https://modern-di.modern-python.org" +Documentation = "https://modern-di.modern-python.org/integrations/arq/" Repository = "https://github.com/modern-python/modern-di-arq" Issues = "https://github.com/modern-python/modern-di-arq/issues" Changelog = "https://github.com/modern-python/modern-di-arq/releases"