Skip to main content

modern-di-celery

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

modern-di integration for Celery 5.x.

Full guide: Celery integration docs

Usage example: examples/

Installation

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

Usage

setup_di registers the container for the app and connects handlers that open and close it on worker_process_init/worker_process_shutdown (once per forked process under the prefork and solo pools) and on worker_init/worker_shutdown (once in the main worker process, the only signals the threads, gevent, and eventlet pools send). The close runs after every other receiver of the shutdown signal that was connected before the worker or child process started, including ones connected after setup_di, so your shutdown code can still resolve Scope.APP dependencies and their finalizers run last. The plugin moves its close to the end when the process starts, so a receiver connected after that, for example from a worker_init receiver, runs after the close. @inject builds a Scope.REQUEST child container per task call and resolves the FromDI-marked parameters from it; alternatively set task_cls=DITask to inject every task without a per-task decorator.

import typing

from celery import Celery
from modern_di import Container, Group, Scope, providers
from modern_di_celery 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)


app = Celery("myapp", broker="redis://localhost")
container = Container(groups=[AppGroup])
setup_di(app, container)
container.validate()  # optional fail-fast check of the provider graph


@app.task
@inject
def greet(
    name: str,
    greeter: typing.Annotated[Greeter, FromDI(Greeter)],  # resolve by type
) -> str:
    return greeter.greet(name)

These worker signals fire only when a real celery worker starts and stops, so a script or test that runs tasks eagerly (task_always_eager = True) must drive the container lifecycle itself, either by sending those signals or by opening and closing the container directly. Celery tasks are sync, so REQUEST-scoped finalizers run via close_sync(); there is no framework request/message object, so no context provider is registered.

Prefork with spawn

Celery runs its prefork pool on billiard, its own copy of multiprocessing, which starts children with the fork method by default on macOS and Linux. With spawn (Windows, or billiard.set_start_method("spawn", force=True)), each child unpickles the app and re-imports your task modules, so setup_di runs again in the child. The worker_process_init handler then registers the child's container for the app the child runs. It matches apps by main name, so give each app in a process its own name. Under spawn, Celery's prefork pool also needs FORKED_BY_MULTIPROCESSING=1 in the environment. Without it tasks fail inside Celery's tracer, with or without modern-di.

Sharing a container

To share one container between the Celery app and other code in the same process, such as another app built on modern-di, pass manage_lifespan=False and open and close the root container yourself. The worker signals then leave it alone, and each task call still gets its own child container:

from modern_di_celery import setup_di


setup_di(app, container, manage_lifespan=False)


async def main() -> None:
    async with container:
        ...  # run the apps that share the container

Under the prefork pool each forked child inherits the open container. With manage_lifespan=False nothing closes it in the child, so close it yourself if its Scope.APP finalizers must run there, for example from your own worker_process_shutdown handler that calls container.close_sync().

API

Symbol Description
setup_di(app, container, *, manage_lifespan=True) Registers the root container (Scope.APP) for the app, outside app.conf, and opens/closes it on worker_process_init/worker_process_shutdown and worker_init/worker_shutdown. With manage_lifespan=False it never opens or closes the root container, and @inject still builds and closes a child container per task call. Returns the container
FromDI(dependency) Inert marker for Annotated[T, FromDI(...)] in task signatures; accepts a provider instance or a type
inject(task) Decorator that builds a Scope.REQUEST child per call, resolves the FromDI-annotated parameters, and closes the child with close_sync() afterwards. Raises TypeError at decoration if the task also declares *args/**kwargs. Raises RuntimeError naming setup_di when a task reaches it without setup_di called
DITask Task subclass that applies @inject to a task's run automatically; pass task_cls=DITask to Celery(...) or base=DITask to @app.task(...)
fetch_di_container(app) Returns the root container (Scope.APP) registered with the Celery app. Raises RuntimeError naming setup_di when called on an app without setup_di called

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-celery 4.2.3

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-celery 4.2.3
File Size Uploaded Provenance
modern_di_celery-4.2.3.tar.gz 6.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for modern-di-celery 4.2.3
File Interpreter ABI Platform
modern_di_celery-4.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 13.8 kB

Release files / modern_di_celery-4.2.3.tar.gz

Download URL modern_di_celery-4.2.3.tar.gz
Size 6.9 kB
Tags Source
SHA-256 checksum
How to use checksums
91b7ee1f5f6dffd64d160a552ea950404713ec39fe8a0901ed16885a7588da9f
BLAKE2b-256 checksum
How to use checksums
a4fba082a2a3f1c99299966d491eb89f53f75808b5d6deefdf11974cd17200c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","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_celery-4.2.3-py3-none-any.whl

Download URL modern_di_celery-4.2.3-py3-none-any.whl
Size 6.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b20c98b45bab25cc0bded2e7cb2840eb5bd1af55867f7190edfd7a93693a7477
BLAKE2b-256 checksum
How to use checksums
2434b3e0ce7344b364cb3462f1c28e00bdbb7ef1e3a19bb88270d577d773109c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","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}

Security for modern-di-celery 4.2.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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