Skip to content

Add Context Manager support to Resource provider #899

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions docs/providers/resource.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ When you call ``.shutdown()`` method on a resource provider, it will remove the
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
resource shutdown.

Resource provider supports 3 types of initializers:
Resource provider supports 4 types of initializers:

- Function
- Generator
- Subclass of ``resources.Resource``
- Context Manager
- Generator (legacy)
- Subclass of ``resources.Resource`` (legacy)

Function initializer
--------------------
Expand Down Expand Up @@ -103,8 +104,44 @@ you configure global resource:

Function initializer does not provide a way to specify custom resource shutdown.

Generator initializer
---------------------
Context Manager initializer
---------------------------

This is an extension to the Function initializer. Resource provider automatically detects if the initializer returns a
context manager and uses it to manage the resource lifecycle.

.. code-block:: python

from dependency_injector import containers, providers

class DatabaseConnection:
def __init__(self, host, port, user, password):
self.host = host
self.port = port
self.user = user
self.password = password

def __enter__(self):
print(f"Connecting to {self.host}:{self.port} as {self.user}")
return self

def __exit__(self, exc_type, exc_val, exc_tb):
print("Closing connection")


class Container(containers.DeclarativeContainer):

config = providers.Configuration()
db = providers.Resource(
DatabaseConnection,
host=config.db.host,
port=config.db.port,
user=config.db.user,
password=config.db.password,
)

Generator initializer (legacy)
------------------------------

Resource provider can use 2-step generators:

Expand Down Expand Up @@ -154,8 +191,13 @@ object is not mandatory. You can leave ``yield`` statement empty:
argument2=...,
)

Subclass initializer
--------------------
.. note::

Generator initializers are automatically wrapped with ``contextmanager`` or ``asynccontextmanager`` decorator when
provided to a ``Resource`` provider.

Subclass initializer (legacy)
-----------------------------

You can create resource initializer by implementing a subclass of the ``resources.Resource``:

Expand Down Expand Up @@ -263,10 +305,11 @@ Asynchronous function initializer:
argument2=...,
)

Asynchronous generator initializer:
Asynchronous Context Manager initializer:

.. code-block:: python

@asynccontextmanager
async def init_async_resource(argument1=..., argument2=...):
connection = await connect()
yield connection
Expand Down
2 changes: 2 additions & 0 deletions examples/providers/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import sys
import logging
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager

from dependency_injector import containers, providers


@contextmanager
def init_thread_pool(max_workers: int):
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
yield thread_pool
Expand Down
Loading