diff --git a/docs/providers/context_local_resource.rst b/docs/providers/context_local_resource.rst new file mode 100644 index 00000000..c169b6a1 --- /dev/null +++ b/docs/providers/context_local_resource.rst @@ -0,0 +1,32 @@ +.. _context-local-resource-provider: + +Context Local Resource provider +================================ + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Resource,Context Local, + Context Variables,Singleton,Per-context + :description: Context Local Resource provider provides a component with initialization and shutdown + that is scoped to execution context using contextvars. This page demonstrates how to + use context local resource provider. + +.. currentmodule:: dependency_injector.providers + +``ContextLocalResource`` inherits from :ref:`resource-provider` and uses the same initialization and shutdown logic +as the standard ``Resource`` provider. +It extends it with context-local storage using Python's ``contextvars`` module. +This means that objects are context local singletons - the same context will +receive the same instance, but different execution contexts will have their own separate instances. + +This is particularly useful in asynchronous applications where you need per-request resource instances +(such as database sessions) that are automatically cleaned up when the request context ends. +Example: + +.. literalinclude:: ../../examples/providers/context_local_resource.py + :language: python + :lines: 3- + + + +.. disqus:: + diff --git a/docs/providers/index.rst b/docs/providers/index.rst index 3edbf127..0dacb826 100644 --- a/docs/providers/index.rst +++ b/docs/providers/index.rst @@ -46,6 +46,7 @@ Providers module API docs - :py:mod:`dependency_injector.providers` dict configuration resource + context_local_resource aggregate selector dependency diff --git a/docs/providers/resource.rst b/docs/providers/resource.rst index b07c2db0..02863a47 100644 --- a/docs/providers/resource.rst +++ b/docs/providers/resource.rst @@ -21,6 +21,9 @@ Resource provider Resource providers help to initialize and configure logging, event loop, thread or process pool, etc. Resource provider is similar to ``Singleton``. Resource initialization happens only once. +If you need a context local singleton (where each execution context has its own instance), +see :ref:`context-local-resource-provider`. + You can make injections and use provided instance the same way like you do with any other provider. .. code-block:: python diff --git a/examples/providers/context_local_resource.py b/examples/providers/context_local_resource.py new file mode 100644 index 00000000..87af2f9d --- /dev/null +++ b/examples/providers/context_local_resource.py @@ -0,0 +1,50 @@ +from uuid import uuid4 + +from fastapi import Depends, FastAPI + +from dependency_injector import containers, providers +from dependency_injector.wiring import Closing, Provide, inject + +global_list = [] + + +class AsyncSessionLocal: + def __init__(self): + self.id = uuid4() + + async def __aenter__(self): + print("Entering session !") + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + print("Closing session !") + + async def execute(self, user_input): + return f"Executing {user_input} in session {self.id}" + + +app = FastAPI() + + +class Container(containers.DeclarativeContainer): + db_session = providers.ContextLocalResource(AsyncSessionLocal) + + +@app.get("/") +@inject +async def index(db: AsyncSessionLocal = Depends(Closing[Provide["db_session"]])): + global global_list + if db.id in global_list: + raise Exception("The db session is already used") # never reaches here + global_list.append(db.id) + res = await db.execute("SELECT 1") + return str(res) + + +if __name__ == "__main__": + import uvicorn + + container = Container() + container.wire(modules=["__main__"]) + uvicorn.run(app, host="localhost", port=8000) + container.unwire()