anydi 0.22.0__py3-none-any.whl → 0.37.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import types
5
+ from typing import Callable, cast
6
+
7
+ from django.apps import AppConfig
8
+ from django.conf import settings
9
+ from django.core.exceptions import ImproperlyConfigured
10
+ from django.utils.module_loading import import_string
11
+
12
+ import anydi
13
+
14
+ from ._settings import get_settings
15
+ from ._utils import inject_urlpatterns, register_components, register_settings
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ContainerConfig(AppConfig):
21
+ name = "anydi.ext.django"
22
+ label = "anydi_django"
23
+
24
+ def __init__(self, app_name: str, app_module: types.ModuleType | None) -> None:
25
+ super().__init__(app_name, app_module)
26
+ self.settings = get_settings()
27
+ # Create a container
28
+ container_factory_path = self.settings["CONTAINER_FACTORY"]
29
+ if container_factory_path:
30
+ try:
31
+ container_factory = cast(
32
+ Callable[[], anydi.Container], import_string(container_factory_path)
33
+ )
34
+ except ImportError as exc:
35
+ raise ImproperlyConfigured(
36
+ f"Cannot import container factory '{container_factory_path}'."
37
+ ) from exc
38
+ self.container = container_factory()
39
+ else:
40
+ self.container = anydi.Container(
41
+ strict=self.settings["STRICT_MODE"],
42
+ )
43
+
44
+ def ready(self) -> None: # noqa: C901
45
+ # Register Django settings
46
+ if self.settings["REGISTER_SETTINGS"]:
47
+ register_settings(
48
+ self.container,
49
+ prefix=getattr(
50
+ settings,
51
+ "ANYDI_SETTINGS_PREFIX",
52
+ "django.conf.settings.",
53
+ ),
54
+ )
55
+
56
+ # Register Django components
57
+ if self.settings["REGISTER_COMPONENTS"]:
58
+ register_components(self.container)
59
+
60
+ # Register modules
61
+ for module_path in self.settings["MODULES"]:
62
+ try:
63
+ module_cls = import_string(module_path)
64
+ except ImportError as exc:
65
+ raise ImproperlyConfigured(
66
+ f"Cannot import module '{module_path}'."
67
+ ) from exc
68
+ self.container.register_module(module_cls)
69
+
70
+ # Patching the django-ninja framework if it installed
71
+ if self.settings["PATCH_NINJA"]:
72
+ from .ninja import patch_ninja
73
+
74
+ patch_ninja()
75
+
76
+ # Auto-injecting the container into views
77
+ if urlconf := self.settings["INJECT_URLCONF"]:
78
+ if isinstance(urlconf, str):
79
+ urlconf = [urlconf]
80
+ for u in urlconf:
81
+ inject_urlpatterns(self.container, urlconf=u)
82
+
83
+ # Scan packages
84
+ for scan_package in self.settings["SCAN_PACKAGES"]:
85
+ self.container.scan(scan_package)
@@ -0,0 +1,28 @@
1
+ from typing import Any, Callable
2
+
3
+ from asgiref.sync import iscoroutinefunction
4
+ from django.http import HttpRequest
5
+ from django.utils.decorators import sync_and_async_middleware
6
+
7
+ from ._container import container
8
+
9
+
10
+ @sync_and_async_middleware
11
+ def request_scoped_middleware(
12
+ get_response: Callable[..., Any],
13
+ ) -> Callable[..., Any]:
14
+ if iscoroutinefunction(get_response):
15
+
16
+ async def async_middleware(request: HttpRequest) -> Any:
17
+ async with container.arequest_context() as context:
18
+ context.set(HttpRequest, request)
19
+ return await get_response(request)
20
+
21
+ return async_middleware
22
+
23
+ def middleware(request: HttpRequest) -> Any:
24
+ with container.request_context() as context:
25
+ context.set(HttpRequest, request)
26
+ return get_response(request)
27
+
28
+ return middleware
@@ -0,0 +1,16 @@
1
+ try:
2
+ from ninja import operation
3
+ except ImportError as exc: # pragma: no cover
4
+ raise ImportError(
5
+ "'django-ninja' is not installed. "
6
+ "Please install it using 'pip install django-ninja'."
7
+ ) from exc
8
+
9
+ from ._operation import AsyncOperation, Operation
10
+ from ._signature import ViewSignature
11
+
12
+
13
+ def patch_ninja() -> None:
14
+ operation.ViewSignature = ViewSignature # type: ignore[attr-defined]
15
+ operation.Operation = Operation # type: ignore[misc]
16
+ operation.AsyncOperation = AsyncOperation # type: ignore[misc]
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from django.http import HttpRequest, HttpResponseBase
6
+ from ninja.operation import (
7
+ AsyncOperation as BaseAsyncOperation, # noqa
8
+ Operation as BaseOperation,
9
+ )
10
+
11
+ from anydi.ext.django import container
12
+
13
+ from ._signature import ViewSignature
14
+
15
+
16
+ def _update_exc_args(exc: Exception) -> None:
17
+ if isinstance(exc, TypeError) and "required positional argument" in str(exc):
18
+ msg = "Did you fail to use functools.wraps() in a decorator?"
19
+ msg = f"{exc.args[0]}: {msg}" if exc.args else msg
20
+ exc.args = (msg,) + exc.args[1:]
21
+
22
+
23
+ class Operation(BaseOperation):
24
+ signature: ViewSignature
25
+
26
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
27
+ super().__init__(*args, **kwargs)
28
+ self.dependencies = self.signature.dependencies
29
+
30
+ def run(self, request: HttpRequest, **kw: Any) -> HttpResponseBase:
31
+ error = self._run_checks(request)
32
+ if error:
33
+ return error
34
+ try:
35
+ temporal_response = self.api.create_temporal_response(request)
36
+ values = self._get_values(request, kw, temporal_response)
37
+ values.update(self._get_dependencies())
38
+ result = self.view_func(request, **values)
39
+ return self._result_to_response(request, result, temporal_response)
40
+ except Exception as e:
41
+ _update_exc_args(e)
42
+ return self.api.on_exception(request, e)
43
+
44
+ def _get_dependencies(self) -> dict[str, Any]:
45
+ return {
46
+ name: container.resolve(interface) for name, interface in self.dependencies
47
+ }
48
+
49
+
50
+ class AsyncOperation(BaseAsyncOperation):
51
+ signature: ViewSignature
52
+
53
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
54
+ super().__init__(*args, **kwargs)
55
+ self.dependencies = self.signature.dependencies
56
+
57
+ async def run(self, request: HttpRequest, **kw: Any) -> HttpResponseBase: # type: ignore
58
+ error = await self._run_checks(request)
59
+ if error:
60
+ return error
61
+ try:
62
+ temporal_response = self.api.create_temporal_response(request)
63
+ values = self._get_values(request, kw, temporal_response)
64
+ values.update(await self._get_dependencies())
65
+ result = await self.view_func(request, **values)
66
+ return self._result_to_response(request, result, temporal_response)
67
+ except Exception as e:
68
+ _update_exc_args(e)
69
+ return self.api.on_exception(request, e)
70
+
71
+ async def _get_dependencies(self) -> dict[str, Any]:
72
+ return {
73
+ name: await container.aresolve(interface)
74
+ for name, interface in self.dependencies
75
+ }
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from collections.abc import Callable
5
+ from typing import Any
6
+
7
+ from django.http import HttpResponse
8
+ from ninja.signature.details import (
9
+ FuncParam, # noqa
10
+ ViewSignature as BaseViewSignature,
11
+ )
12
+ from ninja.signature.utils import get_path_param_names, get_typed_signature
13
+
14
+ from anydi._types import is_marker # noqa
15
+
16
+
17
+ class ViewSignature(BaseViewSignature):
18
+ def __init__(self, path: str, view_func: Callable[..., Any]) -> None:
19
+ self.view_func = view_func
20
+ self.signature = get_typed_signature(self.view_func)
21
+ self.path = path
22
+ self.path_params_names = get_path_param_names(path)
23
+ self.docstring = inspect.cleandoc(view_func.__doc__ or "")
24
+ self.has_kwargs = False
25
+ self.dependencies = []
26
+
27
+ self.params = []
28
+ for name, arg in self.signature.parameters.items():
29
+ if name == "request":
30
+ # TODO: maybe better assert that 1st param is request or check by type?
31
+ # maybe even have attribute like `has_request`
32
+ # so that users can ignore passing request if not needed
33
+ continue
34
+
35
+ if arg.kind == arg.VAR_KEYWORD:
36
+ # Skipping **kwargs
37
+ self.has_kwargs = True
38
+ continue
39
+
40
+ if arg.kind == arg.VAR_POSITIONAL:
41
+ # Skipping *args
42
+ continue
43
+
44
+ if arg.annotation is HttpResponse:
45
+ self.response_arg = name
46
+ continue
47
+
48
+ # Skip default values that are anydi dependency markers
49
+ if is_marker(arg.default):
50
+ self.dependencies.append((name, arg.annotation))
51
+ continue
52
+
53
+ func_param = self._get_param_type(name, arg)
54
+ self.params.append(func_param)
55
+
56
+ if hasattr(view_func, "_ninja_contribute_args"):
57
+ for p_name, p_type, p_source in view_func._ninja_contribute_args: # noqa
58
+ self.params.append(
59
+ FuncParam(p_name, p_source.alias or p_name, p_source, p_type, False)
60
+ )
61
+
62
+ self.models = self._create_models()
63
+
64
+ self._validate_view_path_params()
anydi/ext/fastapi.py CHANGED
@@ -1,6 +1,9 @@
1
1
  """AnyDI FastAPI extension."""
2
2
 
3
- from typing import Any, Iterator, cast
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from typing import Any, cast
4
7
 
5
8
  from fastapi import Depends, FastAPI, params
6
9
  from fastapi.dependencies.models import Dependant
@@ -8,9 +11,9 @@ from fastapi.routing import APIRoute
8
11
  from starlette.requests import Request
9
12
 
10
13
  from anydi import Container
11
- from anydi._utils import get_signature
14
+ from anydi._utils import get_typed_parameters
12
15
 
13
- from ._utils import HasInterface, patch_parameter_interface
16
+ from ._utils import HasInterface, patch_call_parameter
14
17
  from .starlette.middleware import RequestScopedMiddleware
15
18
 
16
19
  __all__ = ["RequestScopedMiddleware", "install", "get_container", "Inject"]
@@ -19,10 +22,6 @@ __all__ = ["RequestScopedMiddleware", "install", "get_container", "Inject"]
19
22
  def install(app: FastAPI, container: Container) -> None:
20
23
  """Install AnyDI into a FastAPI application.
21
24
 
22
- Args:
23
- app: The FastAPI application instance.
24
- container: The container.
25
-
26
25
  This function installs the AnyDI container into a FastAPI application by attaching
27
26
  it to the application state. It also patches the route dependencies to inject the
28
27
  required dependencies using AnyDI.
@@ -41,42 +40,27 @@ def install(app: FastAPI, container: Container) -> None:
41
40
  call, *params = dependant.cache_key
42
41
  if not call:
43
42
  continue # pragma: no cover
44
- for parameter in get_signature(call).parameters.values():
45
- patch_parameter_interface(call, parameter, container)
43
+ for parameter in get_typed_parameters(call):
44
+ patch_call_parameter(container, call, parameter)
46
45
 
47
46
 
48
47
  def get_container(request: Request) -> Container:
49
- """Get the AnyDI container from a FastAPI request.
50
-
51
- Args:
52
- request: The FastAPI request.
53
-
54
- Returns:
55
- The AnyDI container associated with the request.
56
- """
48
+ """Get the AnyDI container from a FastAPI request."""
57
49
  return cast(Container, request.app.state.container)
58
50
 
59
51
 
60
- class Resolver(params.Depends, HasInterface):
52
+ class Resolver(HasInterface, params.Depends):
61
53
  """Parameter dependency class for injecting dependencies using AnyDI."""
62
54
 
63
55
  def __init__(self) -> None:
64
56
  super().__init__(dependency=self._dependency, use_cache=True)
65
- HasInterface.__init__(self)
66
57
 
67
58
  async def _dependency(self, container: Container = Depends(get_container)) -> Any:
68
59
  return await container.aresolve(self.interface)
69
60
 
70
61
 
71
62
  def Inject() -> Any: # noqa
72
- """Decorator for marking a function parameter as requiring injection.
73
-
74
- The `Inject` decorator is used to mark a function parameter as requiring injection
75
- of a dependency resolved by AnyDI.
76
-
77
- Returns:
78
- The `Resolver` instance representing the parameter dependency.
79
- """
63
+ """Decorator for marking a function parameter as requiring injection."""
80
64
  return Resolver()
81
65
 
82
66
 
@@ -0,0 +1,58 @@
1
+ """AnyDI FastStream extension."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, cast
6
+
7
+ from fast_depends.dependencies import Depends
8
+ from faststream import ContextRepo
9
+ from faststream.broker.core.usecase import BrokerUsecase
10
+
11
+ from anydi import Container
12
+ from anydi._utils import get_typed_parameters
13
+
14
+ from ._utils import HasInterface, patch_call_parameter
15
+
16
+
17
+ def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
18
+ """Install AnyDI into a FastStream broker.
19
+
20
+ This function installs the AnyDI container into a FastStream broker by attaching
21
+ it to the broker. It also patches the broker handlers to inject the required
22
+ dependencies using AnyDI.
23
+ """
24
+ broker._container = container # type: ignore[attr-defined]
25
+
26
+ for handler in _get_broken_handlers(broker):
27
+ call = handler._original_call # noqa
28
+ for parameter in get_typed_parameters(call):
29
+ patch_call_parameter(container, call, parameter)
30
+
31
+
32
+ def _get_broken_handlers(broker: BrokerUsecase[Any, Any]) -> list[Any]:
33
+ if hasattr(broker, "handlers"):
34
+ return [handler.calls[0][0] for handler in broker.handlers.values()]
35
+ # faststream > 0.5.0
36
+ return [
37
+ subscriber.calls[0].handler
38
+ for subscriber in broker._subscribers.values() # noqa
39
+ ]
40
+
41
+
42
+ def get_container(broker: BrokerUsecase[Any, Any]) -> Container:
43
+ return cast(Container, getattr(broker, "_container")) # noqa
44
+
45
+
46
+ class Resolver(HasInterface, Depends):
47
+ """Parameter dependency class for injecting dependencies using AnyDI."""
48
+
49
+ def __init__(self) -> None:
50
+ super().__init__(dependency=self._dependency, use_cache=True, cast=True)
51
+
52
+ async def _dependency(self, context: ContextRepo) -> Any:
53
+ container = get_container(context.get("broker"))
54
+ return await container.aresolve(self.interface)
55
+
56
+
57
+ def Inject() -> Any: # noqa
58
+ return Resolver()
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import Annotated, Any, Callable
5
+
6
+ from pydantic.fields import ComputedFieldInfo, FieldInfo # noqa
7
+ from pydantic_settings import BaseSettings
8
+
9
+ from anydi import Container
10
+
11
+
12
+ def install(
13
+ settings: BaseSettings | Iterable[BaseSettings],
14
+ container: Container,
15
+ *,
16
+ prefix: str = "settings.",
17
+ ) -> None:
18
+ """Install Pydantic settings into an AnyDI container."""
19
+
20
+ # Ensure prefix ends with a dot
21
+ if prefix[-1] != ".":
22
+ prefix += "."
23
+
24
+ def _register_settings(_settings: BaseSettings) -> None:
25
+ all_fields = {**_settings.model_fields, **_settings.model_computed_fields}
26
+ for setting_name, field_info in all_fields.items():
27
+ if isinstance(field_info, ComputedFieldInfo):
28
+ interface = field_info.return_type
29
+ elif isinstance(field_info, FieldInfo):
30
+ interface = field_info.annotation
31
+ else:
32
+ continue
33
+
34
+ container.register(
35
+ Annotated[interface, f"{prefix}{setting_name}"],
36
+ _get_setting_value(getattr(_settings, setting_name)),
37
+ scope="singleton",
38
+ )
39
+
40
+ if isinstance(settings, BaseSettings):
41
+ _register_settings(settings)
42
+ else:
43
+ for _settings in settings:
44
+ _register_settings(_settings)
45
+
46
+
47
+ def _get_setting_value(setting_value: Any) -> Callable[[], Any]:
48
+ return lambda: setting_value
@@ -1,9 +1,18 @@
1
+ from __future__ import annotations
2
+
1
3
  import inspect
2
- from typing import Any, Callable, Iterator, List, Tuple, cast
4
+ import logging
5
+ from collections.abc import Iterator
6
+ from typing import Any, Callable, cast
3
7
 
4
8
  import pytest
9
+ from _pytest.python import async_warn_and_skip
10
+ from anyio.pytest_plugin import extract_backend_and_options, get_runner
5
11
 
6
12
  from anydi import Container
13
+ from anydi._utils import get_typed_parameters
14
+
15
+ logger = logging.getLogger(__name__)
7
16
 
8
17
 
9
18
  def pytest_configure(config: pytest.Config) -> None:
@@ -49,8 +58,8 @@ def _anydi_should_inject(request: pytest.FixtureRequest) -> bool:
49
58
 
50
59
 
51
60
  @pytest.fixture(scope="session")
52
- def _anydi_unresolved() -> Iterator[List[Any]]:
53
- unresolved: List[Any] = []
61
+ def _anydi_unresolved() -> Iterator[list[Any]]:
62
+ unresolved: list[Any] = []
54
63
  yield unresolved
55
64
  unresolved.clear()
56
65
 
@@ -58,17 +67,20 @@ def _anydi_unresolved() -> Iterator[List[Any]]:
58
67
  @pytest.fixture
59
68
  def _anydi_injected_parameter_iterator(
60
69
  request: pytest.FixtureRequest,
61
- _anydi_unresolved: List[str],
62
- ) -> Callable[[], Iterator[Tuple[str, Any]]]:
63
- def _iterator() -> Iterator[Tuple[str, inspect.Parameter]]:
64
- for name, parameter in inspect.signature(request.function).parameters.items():
70
+ _anydi_unresolved: list[str],
71
+ ) -> Callable[[], Iterator[tuple[str, Any]]]:
72
+ registered_fixtures = request.session._fixturemanager._arg2fixturedefs # noqa
73
+
74
+ def _iterator() -> Iterator[tuple[str, inspect.Parameter]]:
75
+ for parameter in get_typed_parameters(request.function):
76
+ interface = parameter.annotation
65
77
  if (
66
- ((interface := parameter.annotation) is parameter.empty)
78
+ interface is inspect.Parameter.empty
67
79
  or interface in _anydi_unresolved
68
- or name in request.node.funcargs
80
+ or parameter.name in registered_fixtures
69
81
  ):
70
82
  continue
71
- yield name, interface
83
+ yield parameter.name, interface
72
84
 
73
85
  return _iterator
74
86
 
@@ -77,8 +89,8 @@ def _anydi_injected_parameter_iterator(
77
89
  def _anydi_inject(
78
90
  request: pytest.FixtureRequest,
79
91
  _anydi_should_inject: bool,
80
- _anydi_injected_parameter_iterator: Callable[[], Iterator[Tuple[str, Any]]],
81
- _anydi_unresolved: List[str],
92
+ _anydi_injected_parameter_iterator: Callable[[], Iterator[tuple[str, Any]]],
93
+ _anydi_unresolved: list[str],
82
94
  ) -> None:
83
95
  """Inject dependencies into the test function."""
84
96
 
@@ -89,44 +101,58 @@ def _anydi_inject(
89
101
  container = cast(Container, request.getfixturevalue("anydi_setup_container"))
90
102
 
91
103
  for argname, interface in _anydi_injected_parameter_iterator():
104
+ # Skip if the interface is not registered
105
+ if container.strict and not container.is_registered(interface):
106
+ continue
107
+
92
108
  try:
93
- # Release the instance if it was already resolved
94
- container.release(interface)
95
- except (LookupError, TypeError):
96
- pass
97
- try:
98
- # Resolve the instance
99
- instance = container.resolve(interface)
100
- except (LookupError, TypeError):
109
+ request.node.funcargs[argname] = container.resolve(interface)
110
+ except Exception as exc:
111
+ logger.warning(
112
+ f"Failed to resolve dependency for argument '{argname}'.", exc_info=exc
113
+ )
101
114
  _anydi_unresolved.append(interface)
102
- continue
103
- request.node.funcargs[argname] = instance
104
115
 
105
116
 
106
117
  @pytest.fixture(autouse=True)
107
- async def _anydi_ainject(
118
+ def _anydi_ainject(
108
119
  request: pytest.FixtureRequest,
109
120
  _anydi_should_inject: bool,
110
- _anydi_injected_parameter_iterator: Callable[[], Iterator[Tuple[str, Any]]],
111
- _anydi_unresolved: List[str],
121
+ _anydi_injected_parameter_iterator: Callable[[], Iterator[tuple[str, Any]]],
122
+ _anydi_unresolved: list[str],
112
123
  ) -> None:
113
124
  """Inject dependencies into the test function."""
114
- if not inspect.iscoroutinefunction(request.function) or not _anydi_should_inject:
125
+ if (
126
+ not inspect.iscoroutinefunction(request.function)
127
+ and not inspect.isasyncgenfunction(request.function)
128
+ or not _anydi_should_inject
129
+ ):
115
130
  return
116
131
 
117
- # Setup the container
118
- container = cast(Container, request.getfixturevalue("anydi_setup_container"))
132
+ # Skip if the anyio backend is not available
133
+ if "anyio_backend" not in request.fixturenames:
134
+ async_warn_and_skip(request.node.nodeid)
119
135
 
120
- for argname, interface in _anydi_injected_parameter_iterator():
121
- try:
122
- # Release the instance if it was already resolved
123
- container.release(interface)
124
- except (LookupError, TypeError):
125
- pass
126
- try:
127
- # Resolve the instance
128
- instance = await container.aresolve(interface)
129
- except (LookupError, TypeError):
130
- _anydi_unresolved.append(interface)
131
- continue
132
- request.node.funcargs[argname] = instance
136
+ async def _awrapper() -> None:
137
+ # Setup the container
138
+ container = cast(Container, request.getfixturevalue("anydi_setup_container"))
139
+
140
+ for argname, interface in _anydi_injected_parameter_iterator():
141
+ # Skip if the interface is not registered
142
+ if container.strict and not container.is_registered(interface):
143
+ continue
144
+
145
+ try:
146
+ request.node.funcargs[argname] = await container.aresolve(interface)
147
+ except Exception as exc:
148
+ logger.warning(
149
+ f"Failed to resolve dependency for argument '{argname}'.",
150
+ exc_info=exc,
151
+ )
152
+ _anydi_unresolved.append(interface)
153
+
154
+ anyio_backend = request.getfixturevalue("anyio_backend")
155
+ backend_name, backend_options = extract_backend_and_options(anyio_backend)
156
+
157
+ with get_runner(backend_name, backend_options) as runner:
158
+ runner.run_fixture(_awrapper, {})
@@ -12,26 +12,12 @@ class RequestScopedMiddleware(BaseHTTPMiddleware):
12
12
  """Starlette middleware for managing request-scoped AnyDI context."""
13
13
 
14
14
  def __init__(self, app: ASGIApp, container: Container) -> None:
15
- """Initialize the RequestScopedMiddleware.
16
-
17
- Args:
18
- app: The ASGI application.
19
- container: The container.
20
- """
21
15
  super().__init__(app)
22
16
  self.container = container
23
17
 
24
18
  async def dispatch(
25
19
  self, request: Request, call_next: RequestResponseEndpoint
26
20
  ) -> Response:
27
- """Dispatch the request and handle the response.
28
-
29
- Args:
30
- request: The incoming request.
31
- call_next: The next request-response endpoint.
32
-
33
- Returns:
34
- The response to the request.
35
- """
36
- async with self.container.arequest_context():
21
+ async with self.container.arequest_context() as context:
22
+ context.set(Request, request)
37
23
  return await call_next(request)