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). @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

📦 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-celery 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-celery 4.1.0
File Size Uploaded
modern_di_celery-4.1.0.tar.gz 6.6 kB Details

Built distribution (wheel)

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

Total release size: 13.2 kB

Release files / modern_di_celery-4.1.0.tar.gz

Download URL modern_di_celery-4.1.0.tar.gz
Size 6.6 kB
Tags Source
SHA-256 checksum
How to use checksums
10ba4a1943aedc871d7d6e762e753363ba541057aec04751ada36a775023850f
BLAKE2b-256 checksum
How to use checksums
d96d207add564c46ddf5cfda58f1f12cd375e886a8a013909660e7d0f778a488
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_celery-4.1.0-py3-none-any.whl

Download URL modern_di_celery-4.1.0-py3-none-any.whl
Size 6.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
471e9a1b8a934b054278ff0234d780917ecd7ddcfe437db91b2d76e6236cf946
BLAKE2b-256 checksum
How to use checksums
1e7423218ad6de6dd344e19323ea6d32663e21afb83a4c2353aad2d72f9d30ef
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.2.2

2 release files

4.2.1

2 release files

4.2.0

2 release files

This release

4.1.0 This release

2 release files

4.0.2

2 release files

4.0.1

2 release files

4.0.0

2 release files

3.1.0

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.1.0

2 release files

2.0.1

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