Wiring

Wiring feature provides a way to inject container providers into the functions and methods.

To use wiring you need:

  • Place @inject decorator. Decorator @inject injects the dependencies.

  • Place markers. Wiring marker specifies what dependency to inject, e.g. Provide[Container.bar]. This helps container to find the injections.

  • Wire the container with the markers in the code. Call container.wire() specifying modules and packages you would like to wire it with.

  • Use functions and classes as you normally do. Framework will provide specified injections.

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def main(service: Service = Provide[Container.service]) -> None:
    ...


if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])

    main()

Markers

Wiring feature uses markers to make injections. Injection marker is specified as a default value of a function or method argument:

from dependency_injector.wiring import inject, Provide


@inject
def foo(bar: Bar = Provide[Container.bar]):
    ...

Specifying an annotation is optional.

To inject the provider itself use Provide[foo.provider]:

from dependency_injector.providers import Factory
from dependency_injector.wiring import inject, Provide


@inject
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
    bar = bar_provider(argument="baz")
    ...

You can also use Provider[foo] for injecting the provider itself:

from dependency_injector.providers import Factory
from dependency_injector.wiring import inject, Provider


@inject
def foo(bar_provider: Factory[Bar] = Provider[Container.bar]):
    bar = bar_provider(argument="baz")
    ...

You can use configuration, provided instance and sub-container providers as you normally do.

@inject
def foo(token: str = Provide[Container.config.api_token]):
    ...


@inject
def foo(timeout: int = Provide[Container.config.timeout.as_(int)]):
    ...


@inject
def foo(baz: Baz = Provide[Container.bar.provided.baz]):
    ...


@inject
def foo(bar: Bar = Provide[Container.subcontainer.bar]):
    ...

You can compound wiring and Resource provider to implement per-function execution scope. See Resources, wiring and per-function execution scope for details.

Also you can use Provide marker to inject a container.

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def main(container: Container = Provide[Container]):
    service = container.service()
    ...


if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])

    main()

String identifiers

You can use wiring with string identifiers. String identifier should match provider name in the container:

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def main(service: Service = Provide["service"]) -> None:
    ...


if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])

    main()

With string identifiers you don’t need to use a container to specify an injection.

To specify an injection from a nested container use point . as a separator:

@inject
def foo(service: UserService = Provide["services.user"]) -> None:
    ...

You can also use injection modifiers:

from dependency_injector.wiring import (
    inject,
    Provide,
    as_int,
    as_float,
    as_,
    required,
    invariant,
    provided,
)


@inject
def foo(value: int = Provide["config.option", as_int()]) -> None:
    ...


@inject
def foo(value: float = Provide["config.option", as_float()]) -> None:
    ...


@inject
def foo(value: Decimal = Provide["config.option", as_(Decimal)]) -> None:
    ...

@inject
def foo(value: str = Provide["config.option", required()]) -> None:
    ...

@inject
def foo(value: int = Provide["config.option", required().as_int()]) -> None:
    ...


@inject
def foo(value: int = Provide["config.option", invariant("config.switch")]) -> None:
    ...

@inject
def foo(value: int = Provide["service", provided().foo["bar"].call()]) -> None:
    ...

To inject a container use special identifier <container>:

@inject
def foo(container: Container = Provide["<container>"]) -> None:
    ...

Making injections into modules and class attributes

You can use wiring to make injections into modules and class attributes.

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


service: Service = Provide[Container.service]


class Main:

    service: Service = Provide[Container.service]


if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])

    assert isinstance(service, Service)
    assert isinstance(Main.service, Service)

You could also use string identifiers to avoid a dependency on a container:

service: Service = Provide["service"]


class Main:

    service: Service = Provide["service"]

Wiring with modules and packages

To wire a container with the modules you need to call container.wire() method:

container.wire(
    modules=[
        "yourapp.module1",
        "yourapp.module2",
    ],
)

Method container.wire() can resolve relative imports:

# In module "yourapp.main":

container.wire(
    modules=[
        ".module1",  # Resolved to: "yourapp.module1"
        ".module2",  # Resolved to: "yourapp.module2"
    ],
)

You can also manually specify a base package for resolving relative imports with the from_package argument:

# In module "yourapp.main":

container.wire(
    modules=[
        ".module1",  # Resolved to: "anotherapp.module1"
        ".module2",  # Resolved to: "anotherapp.module2"
    ],
    from_package="anotherapp",
)

Argument modules can also take already imported modules:

from yourapp import module1, module2


container = Container()
container.wire(modules=[module1, module2])

You can wire container with a package. Container walks recursively over the package modules:

container.wire(
    packages=[
        "yourapp.package1",
        "yourapp.package2",
    ],
)

Arguments modules and packages can be used together.

When wiring is done functions and methods with the markers are patched to provide injections when called.

@inject
def foo(bar: Bar = Provide[Container.bar]):
    ...


container = Container()
container.wire(modules=[__name__])

foo()  # <--- Argument "bar" is injected

Injections are done as keyword arguments.

foo()  # Equivalent to:
foo(bar=container.bar())

Context keyword arguments have a priority over injections.

foo(bar=Bar())  # Bar() is injected

To unpatch previously patched functions and methods call container.unwire() method.

container.unwire()

You can use that in testing to re-create and re-wire a container before each test.

import unittest


class SomeTest(unittest.TestCase):

    def setUp(self):
        self.container = Container()
        self.container.wire(modules=["yourapp.module1", "yourapp.module2"])
        self.addCleanup(self.container.unwire)
import pytest


@pytest.fixture
def container():
    container = Container()
    container.wire(modules=["yourapp.module1", "yourapp.module2"])
    yield container
    container.unwire()

Note

Wiring can take time if you have a large codebase. Consider to persist a container instance and avoid re-wiring between tests.

Note

Python has a limitation on patching individually imported functions. To protect from errors prefer importing modules to importing individual functions or make sure imports happen after the wiring:

# Potential error:

from .module import fn

fn()

Instead use next:

# Always works:

from . import module

module.fn()

Wiring configuration

You can specify wiring configuration in the container. When wiring configuration is defined, container will call method .wire() automatically when you create an instance:

class Container(containers.DeclarativeContainer):

    wiring_config = containers.WiringConfiguration(
        modules=[
            "yourapp.module1",
            "yourapp.module2",
        ],
        packages=[
            "yourapp.package1",
            "yourapp.package2",
        ],
    )

    ...


if __name__ == "__main__":
    container = Container()  # container.wire() is called automatically
    ...

You can also use relative imports. Container will resolve them corresponding to the module of the container class:

# In module "yourapp.container":

class Container(containers.DeclarativeContainer):

    wiring_config = containers.WiringConfiguration(
        modules=[
           ".module1",  # Resolved to: "yourapp.module1"
           ".module2",  # Resolved to: "yourapp.module2"
        ],
    )
)


# In module "yourapp.foo.bar.main":

if __name__ == "__main__":
    container = Container()  # wire to "yourapp.module1" and "yourapp.module2"
    ...

To use wiring configuration and call method .wire() manually, set flag auto_wire=False:

class Container(containers.DeclarativeContainer):

    wiring_config = containers.WiringConfiguration(
        modules=["yourapp.module1"],
        auto_wire=False,
    )


if __name__ == "__main__":
    container = Container()  # container.wire() is NOT called automatically
    container.wire()         # wire to "yourapp.module1"
    ...

Asynchronous injections

Wiring feature supports asynchronous injections:

class Container(containers.DeclarativeContainer):

    db = providers.Resource(init_async_db_client)

    cache = providers.Resource(init_async_cache_client)


@inject
async def main(
    db: Database = Provide[Container.db],
    cache: Cache = Provide[Container.cache],
):
    ...

When you call asynchronous function wiring prepares injections asynchronously. Here is what it does for previous example:

db, cache = await asyncio.gather(
    container.db(),
    container.cache(),
)

await main(db=db, cache=cache)

You can also use Closing marker with the asynchronous Resource providers:

@inject
async def main(
    db: Database = Closing[Provide[Container.db]],
    cache: Cache = Closing[Provide[Container.cache]],
):
    ...

Wiring does closing asynchronously:

db, cache = await asyncio.gather(
    container.db(),
    container.cache(),
)

await main(db=db, cache=cache)

await asyncio.gather(
    container.db.shutdown(),
    container.cache.shutdown(),
)

See Resources, wiring and per-function execution scope for details on Closing marker.

Note

Wiring does not not convert asynchronous injections to synchronous.

It handles asynchronous injections only for async def functions. Asynchronous injections into synchronous def function still work, but you need to take care of awaitables by your own.

See also:

Wiring of dynamically imported modules

You can install an import hook that automatically wires containers to the imported modules. This is useful when you import modules dynamically.

import importlib

from dependency_injector.wiring import register_loader_containers

from .containers import Container


if __name__ == "__main__":
    container = Container()
    register_loader_containers(container)  # <--- installs import hook

    module = importlib.import_module("package.module")
    module.foo()

You can register multiple containers in the import hook. For doing this call register function with multiple containers register_loader_containers(container1, container2, ...) or with a single container register_loader_containers(container) multiple times.

To unregister a container use unregister_loader_containers(container). Wiring module will uninstall the import hook when unregister last container.

Integration with other frameworks

Wiring feature helps to integrate with other frameworks like Django, Flask, etc.

With wiring you do not need to change the traditional application structure of your framework.

  1. Create a container and put framework-independent components as providers.

  2. Place wiring markers in the functions and methods where you want the providers to be injected (Flask or Django views, Aiohttp or Sanic handlers, etc).

  3. Wire the container with the application modules.

  4. Run the application.

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
from flask import Flask, json


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def index_view(service: Service = Provide[Container.service]) -> str:
    return json.dumps({"service_id": id(service)})


if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])

    app = Flask(__name__)
    app.add_url_rule("/", "index", index_view)
    app.run()

Take a look at other application examples: