pymediate
Guide

Dependency injection

Wiring handlers through a dependency-injector container via the optional di extra.

PyMediate ships optional support for dependency-injector, so handlers get their dependencies from a DI container instead of being constructed by hand.

pip install 'pymediate[di]'

See installation for other package managers, and troubleshooting if the import fails.

Basic setup

DependencyInjectorServiceProvider wraps a dependency-injector container and implements PyMediate's ServiceProvider protocol, so it drops in wherever Services.provider() would go. It resolves handlers by their concrete type using type inspection — provider names in the container are irrelevant:

from dependency_injector import containers, providers
from pymediate import Handler, Mediator
from pymediate.providers import DependencyInjectorServiceProvider

class AppContainer(containers.DeclarativeContainer):
    database = providers.Singleton(Database)

    # The provider name can be anything — PyMediate matches by type, not by name
    user_service = providers.Factory(CreateUserHandler, database=database)
container = AppContainer()
provider = DependencyInjectorServiceProvider(container)
mediator = Mediator(provider)

response = mediator.send(CreateUserRequest(username="alice", email="alice@example.com"))

Build the provider outside the container

Construct DependencyInjectorServiceProvider from a container you've already built — never declare it as one of that container's own providers (for example via providers.Self()). Doing so makes the container resolve itself while still scanning itself, which recurses until Python's recursion limit is hit.

Factory vs. Singleton providers

Both provider types work — pick the one matching the handler's intended lifetime:

class AppContainer(containers.DeclarativeContainer):
    database = providers.Singleton(Database)

    # Factory: a new handler instance on every resolution
    create_user_handler = providers.Factory(CreateUserHandler, database=database)

    # Singleton: one shared handler instance for the whole application
    metrics_handler = providers.Singleton(RecordMetricsHandler, database=database)

Most handlers should be Factory unless they're genuinely stateless and safe to share. Pipeline behaviors registered through the container respect their scopes the same way, including ContextLocalSingleton for per-request lifetimes.

Testing with a container

Override providers exactly as you would in any dependency-injector test:

def test_create_user_with_container():
    container = AppContainer()
    container.database.override(providers.Singleton(InMemoryDatabase))

    mediator = Mediator(DependencyInjectorServiceProvider(container))
    response = mediator.send(CreateUserRequest(username="alice", email="alice@example.com"))

    assert response.username == "alice"

If a test doesn't need the container at all, construct the handler directly — see testing without frameworks.

See also

On this page