Skip to content

Context manager dependencies

Dependencies can use @contextlib.contextmanager and @contextlib.asynccontextmanager decorators to execute code during their init and shutdown.

Implementation

Internally aioinject uses contextlib.ExitStack and contextlib.AsyncExitStack

Providers

Scoped and Transient providers

For Scoped and Transient providers dependencies will close when context is closed:

import contextlib
from collections.abc import Iterator

import aioinject
from aioinject import Scoped


@contextlib.contextmanager
def dependency() -> Iterator[int]:
    print("Startup")
    yield 42
    print("Shutdown")


container = aioinject.Container()
container.register(Scoped(dependency))

with container.sync_context() as ctx:
    print(ctx.resolve(int))  # Startup, 42
# Shutdown

Singleton provider

In case of a Singleton they're closed when you close container itself:

import contextlib
from collections.abc import Iterator

import aioinject
from aioinject import Singleton


@contextlib.contextmanager
def dependency() -> Iterator[int]:
    print("Startup")
    yield 42
    print("Shutdown")


container = aioinject.Container()
container.register(Singleton(dependency))

with container:
    with container.sync_context() as ctx:
        print(ctx.resolve(int))  # Startup, 42
    print("Context is closed")
# Shutdown

Using your own or 3rd party class as a context manager

Even if your class has __enter__ and __exit__ methods it won't implicitly be used as a context manager:

from types import TracebackType

from typing_extensions import Self

import aioinject
from aioinject import Scoped


class Class:
    def __enter__(self) -> Self:
        print("Startup")
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        print("Shutdown")


container = aioinject.Container()
container.register(Scoped(Class))

with container.sync_context() as ctx:
    print(ctx.resolve(Class))  # <__main__.Class object at ...>
Nothing is printed! You have to explicitly create a function and decorate it with contextlib decorator:

@contextlib.contextmanager
def create_class() -> Class:
    with Class() as cls:
        yield cls