anydi 0.34.0__py3-none-any.whl → 0.34.1a0__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.
- anydi/__init__.py +9 -3
- anydi/_container.py +466 -82
- anydi/_context.py +29 -268
- anydi/_provider.py +32 -4
- anydi/_types.py +19 -3
- anydi/ext/django/middleware.py +4 -4
- anydi/ext/starlette/middleware.py +2 -2
- {anydi-0.34.0.dist-info → anydi-0.34.1a0.dist-info}/METADATA +1 -1
- {anydi-0.34.0.dist-info → anydi-0.34.1a0.dist-info}/RECORD +12 -14
- anydi/_module.py +0 -94
- anydi/_scanner.py +0 -171
- {anydi-0.34.0.dist-info → anydi-0.34.1a0.dist-info}/LICENSE +0 -0
- {anydi-0.34.0.dist-info → anydi-0.34.1a0.dist-info}/WHEEL +0 -0
- {anydi-0.34.0.dist-info → anydi-0.34.1a0.dist-info}/entry_points.txt +0 -0
anydi/_context.py
CHANGED
|
@@ -1,229 +1,54 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import abc
|
|
4
3
|
import contextlib
|
|
5
|
-
import inspect
|
|
6
4
|
from types import TracebackType
|
|
7
|
-
from typing import
|
|
5
|
+
from typing import Any
|
|
8
6
|
|
|
9
|
-
from typing_extensions import Self
|
|
7
|
+
from typing_extensions import Self
|
|
10
8
|
|
|
11
|
-
from .
|
|
12
|
-
from ._types import AnyInterface, DependencyWrapper, Scope, is_event_type
|
|
13
|
-
from ._utils import (
|
|
14
|
-
get_full_qualname,
|
|
15
|
-
is_async_context_manager,
|
|
16
|
-
is_context_manager,
|
|
17
|
-
run_async,
|
|
18
|
-
)
|
|
9
|
+
from ._utils import run_async
|
|
19
10
|
|
|
20
|
-
if TYPE_CHECKING:
|
|
21
|
-
from ._container import Container
|
|
22
11
|
|
|
12
|
+
class InstanceContext:
|
|
13
|
+
"""A context to store instances."""
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
"""ScopedContext base class."""
|
|
15
|
+
__slots__ = ("_instances", "_stack", "_async_stack")
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def __init__(self, container: Container) -> None:
|
|
30
|
-
self.container = container
|
|
31
|
-
self._instances: dict[Any, Any] = {}
|
|
32
|
-
|
|
33
|
-
def set(self, interface: AnyInterface, instance: Any) -> None:
|
|
34
|
-
"""Set an instance of a dependency in the scoped context."""
|
|
35
|
-
self._instances[interface] = instance
|
|
36
|
-
|
|
37
|
-
@abc.abstractmethod
|
|
38
|
-
def get_or_create(self, provider: Provider) -> tuple[Any, bool]:
|
|
39
|
-
"""Get or create an instance of a dependency from the scoped context."""
|
|
40
|
-
|
|
41
|
-
@abc.abstractmethod
|
|
42
|
-
async def aget_or_create(self, provider: Provider) -> tuple[Any, bool]:
|
|
43
|
-
"""Get or create an async instance of a dependency from the scoped context."""
|
|
44
|
-
|
|
45
|
-
def _create_instance(self, provider: Provider) -> Any:
|
|
46
|
-
"""Create an instance using the provider."""
|
|
47
|
-
if provider.kind == CallableKind.COROUTINE:
|
|
48
|
-
raise TypeError(
|
|
49
|
-
f"The instance for the coroutine provider `{provider}` cannot be "
|
|
50
|
-
"created in synchronous mode."
|
|
51
|
-
)
|
|
52
|
-
args, kwargs = self._get_provided_args(provider)
|
|
53
|
-
return provider.call(*args, **kwargs)
|
|
54
|
-
|
|
55
|
-
async def _acreate_instance(self, provider: Provider) -> Any:
|
|
56
|
-
"""Create an instance asynchronously using the provider."""
|
|
57
|
-
args, kwargs = await self._aget_provided_args(provider)
|
|
58
|
-
if provider.kind == CallableKind.COROUTINE:
|
|
59
|
-
return await provider.call(*args, **kwargs)
|
|
60
|
-
return await run_async(provider.call, *args, **kwargs)
|
|
61
|
-
|
|
62
|
-
def _resolve_parameter(
|
|
63
|
-
self, provider: Provider, parameter: inspect.Parameter
|
|
64
|
-
) -> Any:
|
|
65
|
-
self._validate_resolvable_parameter(parameter, call=provider.call)
|
|
66
|
-
return self.container.resolve(parameter.annotation)
|
|
67
|
-
|
|
68
|
-
async def _aresolve_parameter(
|
|
69
|
-
self, provider: Provider, parameter: inspect.Parameter
|
|
70
|
-
) -> Any:
|
|
71
|
-
self._validate_resolvable_parameter(parameter, call=provider.call)
|
|
72
|
-
return await self.container.aresolve(parameter.annotation)
|
|
73
|
-
|
|
74
|
-
def _validate_resolvable_parameter(
|
|
75
|
-
self, parameter: inspect.Parameter, call: Callable[..., Any]
|
|
76
|
-
) -> None:
|
|
77
|
-
"""Ensure that the specified interface is resolved."""
|
|
78
|
-
if parameter.annotation in self.container._unresolved_interfaces: # noqa
|
|
79
|
-
raise LookupError(
|
|
80
|
-
f"You are attempting to get the parameter `{parameter.name}` with the "
|
|
81
|
-
f"annotation `{get_full_qualname(parameter.annotation)}` as a "
|
|
82
|
-
f"dependency into `{get_full_qualname(call)}` which is not registered "
|
|
83
|
-
"or set in the scoped context."
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
def _get_provided_args(
|
|
87
|
-
self, provider: Provider
|
|
88
|
-
) -> tuple[list[Any], dict[str, Any]]:
|
|
89
|
-
"""Retrieve the arguments for a provider."""
|
|
90
|
-
args: list[Any] = []
|
|
91
|
-
kwargs: dict[str, Any] = {}
|
|
92
|
-
|
|
93
|
-
for parameter in provider.parameters:
|
|
94
|
-
if parameter.annotation in self.container._override_instances: # noqa
|
|
95
|
-
instance = self.container._override_instances[parameter.annotation] # noqa
|
|
96
|
-
elif parameter.annotation in self._instances:
|
|
97
|
-
instance = self._instances[parameter.annotation]
|
|
98
|
-
else:
|
|
99
|
-
try:
|
|
100
|
-
instance = self._resolve_parameter(provider, parameter)
|
|
101
|
-
except LookupError:
|
|
102
|
-
if parameter.default is inspect.Parameter.empty:
|
|
103
|
-
raise
|
|
104
|
-
instance = parameter.default
|
|
105
|
-
else:
|
|
106
|
-
if self.container.testing:
|
|
107
|
-
instance = DependencyWrapper(
|
|
108
|
-
interface=parameter.annotation, instance=instance
|
|
109
|
-
)
|
|
110
|
-
if parameter.kind == parameter.POSITIONAL_ONLY:
|
|
111
|
-
args.append(instance)
|
|
112
|
-
else:
|
|
113
|
-
kwargs[parameter.name] = instance
|
|
114
|
-
return args, kwargs
|
|
115
|
-
|
|
116
|
-
async def _aget_provided_args(
|
|
117
|
-
self, provider: Provider
|
|
118
|
-
) -> tuple[list[Any], dict[str, Any]]:
|
|
119
|
-
"""Asynchronously retrieve the arguments for a provider."""
|
|
120
|
-
args: list[Any] = []
|
|
121
|
-
kwargs: dict[str, Any] = {}
|
|
122
|
-
|
|
123
|
-
for parameter in provider.parameters:
|
|
124
|
-
if parameter.annotation in self.container._override_instances: # noqa
|
|
125
|
-
instance = self.container._override_instances[parameter.annotation] # noqa
|
|
126
|
-
elif parameter.annotation in self._instances:
|
|
127
|
-
instance = self._instances[parameter.annotation]
|
|
128
|
-
else:
|
|
129
|
-
try:
|
|
130
|
-
instance = await self._aresolve_parameter(provider, parameter)
|
|
131
|
-
except LookupError:
|
|
132
|
-
if parameter.default is inspect.Parameter.empty:
|
|
133
|
-
raise
|
|
134
|
-
instance = parameter.default
|
|
135
|
-
else:
|
|
136
|
-
if self.container.testing:
|
|
137
|
-
instance = DependencyWrapper(
|
|
138
|
-
interface=parameter.annotation, instance=instance
|
|
139
|
-
)
|
|
140
|
-
if parameter.kind == parameter.POSITIONAL_ONLY:
|
|
141
|
-
args.append(instance)
|
|
142
|
-
else:
|
|
143
|
-
kwargs[parameter.name] = instance
|
|
144
|
-
return args, kwargs
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
class ResourceScopedContext(ScopedContext):
|
|
148
|
-
"""ScopedContext with closable resources support."""
|
|
149
|
-
|
|
150
|
-
def __init__(self, container: Container) -> None:
|
|
151
|
-
"""Initialize the ScopedContext."""
|
|
152
|
-
super().__init__(container)
|
|
17
|
+
def __init__(self) -> None:
|
|
18
|
+
self._instances: dict[type[Any], Any] = {}
|
|
153
19
|
self._stack = contextlib.ExitStack()
|
|
154
20
|
self._async_stack = contextlib.AsyncExitStack()
|
|
155
21
|
|
|
156
|
-
def
|
|
157
|
-
"""Get an instance
|
|
158
|
-
|
|
159
|
-
if instance is None:
|
|
160
|
-
if provider.kind == CallableKind.GENERATOR:
|
|
161
|
-
instance = self._create_resource(provider)
|
|
162
|
-
elif provider.kind == CallableKind.ASYNC_GENERATOR:
|
|
163
|
-
raise TypeError(
|
|
164
|
-
f"The provider `{provider}` cannot be started in synchronous mode "
|
|
165
|
-
"because it is an asynchronous provider. Please start the provider "
|
|
166
|
-
"in asynchronous mode before using it."
|
|
167
|
-
)
|
|
168
|
-
else:
|
|
169
|
-
instance = self._create_instance(provider)
|
|
170
|
-
self._instances[provider.interface] = instance
|
|
171
|
-
return instance, True
|
|
172
|
-
return instance, False
|
|
22
|
+
def get(self, interface: type[Any]) -> Any | None:
|
|
23
|
+
"""Get an instance from the context."""
|
|
24
|
+
return self._instances.get(interface)
|
|
173
25
|
|
|
174
|
-
|
|
175
|
-
"""
|
|
176
|
-
|
|
177
|
-
if instance is None:
|
|
178
|
-
if provider.kind == CallableKind.GENERATOR:
|
|
179
|
-
instance = await run_async(self._create_resource, provider)
|
|
180
|
-
elif provider.kind == CallableKind.ASYNC_GENERATOR:
|
|
181
|
-
instance = await self._acreate_resource(provider)
|
|
182
|
-
else:
|
|
183
|
-
instance = await self._acreate_instance(provider)
|
|
184
|
-
self._instances[provider.interface] = instance
|
|
185
|
-
return instance, True
|
|
186
|
-
return instance, False
|
|
26
|
+
def set(self, interface: type[Any], value: Any) -> None:
|
|
27
|
+
"""Set an instance in the context."""
|
|
28
|
+
self._instances[interface] = value
|
|
187
29
|
|
|
188
|
-
def
|
|
189
|
-
"""
|
|
190
|
-
return
|
|
30
|
+
def enter(self, cm: contextlib.AbstractContextManager[Any]) -> Any:
|
|
31
|
+
"""Enter the context."""
|
|
32
|
+
return self._stack.enter_context(cm)
|
|
191
33
|
|
|
192
|
-
def
|
|
193
|
-
"""
|
|
194
|
-
|
|
195
|
-
# Enter the context manager if the instance is closable.
|
|
196
|
-
if is_context_manager(instance):
|
|
197
|
-
self._stack.enter_context(instance)
|
|
198
|
-
return instance
|
|
34
|
+
async def aenter(self, cm: contextlib.AbstractAsyncContextManager[Any]) -> Any:
|
|
35
|
+
"""Enter the context asynchronously."""
|
|
36
|
+
return await self._async_stack.enter_async_context(cm)
|
|
199
37
|
|
|
200
|
-
def
|
|
201
|
-
|
|
202
|
-
args, kwargs = self._get_provided_args(provider)
|
|
203
|
-
cm = contextlib.contextmanager(provider.call)(*args, **kwargs)
|
|
204
|
-
return self._stack.enter_context(cm)
|
|
38
|
+
def __setitem__(self, interface: type[Any], value: Any) -> None:
|
|
39
|
+
self._instances[interface] = value
|
|
205
40
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
instance = await super()._acreate_instance(provider)
|
|
209
|
-
# Enter the context manager if the instance is closable.
|
|
210
|
-
if is_async_context_manager(instance):
|
|
211
|
-
await self._async_stack.enter_async_context(instance)
|
|
212
|
-
return instance
|
|
41
|
+
def __getitem__(self, interface: type[Any]) -> Any:
|
|
42
|
+
return self._instances[interface]
|
|
213
43
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
args, kwargs = await self._aget_provided_args(provider)
|
|
217
|
-
cm = contextlib.asynccontextmanager(provider.call)(*args, **kwargs)
|
|
218
|
-
return await self._async_stack.enter_async_context(cm)
|
|
44
|
+
def __contains__(self, interface: type[Any]) -> bool:
|
|
45
|
+
return interface in self._instances
|
|
219
46
|
|
|
220
|
-
def
|
|
221
|
-
"""Delete a dependency instance from the scoped context."""
|
|
47
|
+
def __delitem__(self, interface: type[Any]) -> None:
|
|
222
48
|
self._instances.pop(interface, None)
|
|
223
49
|
|
|
224
50
|
def __enter__(self) -> Self:
|
|
225
51
|
"""Enter the context."""
|
|
226
|
-
self.start()
|
|
227
52
|
return self
|
|
228
53
|
|
|
229
54
|
def __exit__(
|
|
@@ -231,13 +56,9 @@ class ResourceScopedContext(ScopedContext):
|
|
|
231
56
|
exc_type: type[BaseException] | None,
|
|
232
57
|
exc_val: BaseException | None,
|
|
233
58
|
exc_tb: TracebackType | None,
|
|
234
|
-
) ->
|
|
59
|
+
) -> Any:
|
|
235
60
|
"""Exit the context."""
|
|
236
|
-
return self._stack.__exit__(exc_type, exc_val, exc_tb)
|
|
237
|
-
|
|
238
|
-
@abc.abstractmethod
|
|
239
|
-
def start(self) -> None:
|
|
240
|
-
"""Start the scoped context."""
|
|
61
|
+
return self._stack.__exit__(exc_type, exc_val, exc_tb)
|
|
241
62
|
|
|
242
63
|
def close(self) -> None:
|
|
243
64
|
"""Close the scoped context."""
|
|
@@ -245,7 +66,6 @@ class ResourceScopedContext(ScopedContext):
|
|
|
245
66
|
|
|
246
67
|
async def __aenter__(self) -> Self:
|
|
247
68
|
"""Enter the context asynchronously."""
|
|
248
|
-
await self.astart()
|
|
249
69
|
return self
|
|
250
70
|
|
|
251
71
|
async def __aexit__(
|
|
@@ -259,65 +79,6 @@ class ResourceScopedContext(ScopedContext):
|
|
|
259
79
|
self.__exit__, exc_type, exc_val, exc_tb
|
|
260
80
|
) or await self._async_stack.__aexit__(exc_type, exc_val, exc_tb)
|
|
261
81
|
|
|
262
|
-
@abc.abstractmethod
|
|
263
|
-
async def astart(self) -> None:
|
|
264
|
-
"""Start the scoped context asynchronously."""
|
|
265
|
-
|
|
266
82
|
async def aclose(self) -> None:
|
|
267
83
|
"""Close the scoped context asynchronously."""
|
|
268
84
|
await self.__aexit__(None, None, None)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
@final
|
|
272
|
-
class SingletonContext(ResourceScopedContext):
|
|
273
|
-
"""A scoped context representing the "singleton" scope."""
|
|
274
|
-
|
|
275
|
-
scope = "singleton"
|
|
276
|
-
|
|
277
|
-
def start(self) -> None:
|
|
278
|
-
"""Start the scoped context."""
|
|
279
|
-
for interface in self.container._resource_cache.get(self.scope, []): # noqa
|
|
280
|
-
self.container.resolve(interface)
|
|
281
|
-
|
|
282
|
-
async def astart(self) -> None:
|
|
283
|
-
"""Start the scoped context asynchronously."""
|
|
284
|
-
for interface in self.container._resource_cache.get(self.scope, []): # noqa
|
|
285
|
-
await self.container.aresolve(interface)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
@final
|
|
289
|
-
class RequestContext(ResourceScopedContext):
|
|
290
|
-
"""A scoped context representing the "request" scope."""
|
|
291
|
-
|
|
292
|
-
scope = "request"
|
|
293
|
-
|
|
294
|
-
def start(self) -> None:
|
|
295
|
-
"""Start the scoped context."""
|
|
296
|
-
for interface in self.container._resource_cache.get(self.scope, []): # noqa
|
|
297
|
-
if not is_event_type(interface):
|
|
298
|
-
continue
|
|
299
|
-
self.container.resolve(interface)
|
|
300
|
-
|
|
301
|
-
async def astart(self) -> None:
|
|
302
|
-
"""Start the scoped context asynchronously."""
|
|
303
|
-
for interface in self.container._resource_cache.get(self.scope, []): # noqa
|
|
304
|
-
if not is_event_type(interface):
|
|
305
|
-
continue
|
|
306
|
-
await self.container.aresolve(interface)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
@final
|
|
310
|
-
class TransientContext(ScopedContext):
|
|
311
|
-
"""A scoped context representing the "transient" scope."""
|
|
312
|
-
|
|
313
|
-
scope = "transient"
|
|
314
|
-
|
|
315
|
-
def get_or_create(self, provider: Provider) -> tuple[Any, bool]:
|
|
316
|
-
"""Get or create an instance of a dependency from the transient context."""
|
|
317
|
-
return self._create_instance(provider), True
|
|
318
|
-
|
|
319
|
-
async def aget_or_create(self, provider: Provider) -> tuple[Any, bool]:
|
|
320
|
-
"""
|
|
321
|
-
Get or create an async instance of a dependency from the transient context.
|
|
322
|
-
"""
|
|
323
|
-
return await self._acreate_instance(provider), True
|
anydi/_provider.py
CHANGED
|
@@ -38,6 +38,11 @@ class Provider:
|
|
|
38
38
|
"_kind",
|
|
39
39
|
"_interface",
|
|
40
40
|
"_parameters",
|
|
41
|
+
"_is_coroutine",
|
|
42
|
+
"_is_generator",
|
|
43
|
+
"_is_async_generator",
|
|
44
|
+
"_is_async",
|
|
45
|
+
"_is_resource",
|
|
41
46
|
)
|
|
42
47
|
|
|
43
48
|
def __init__(
|
|
@@ -52,6 +57,12 @@ class Provider:
|
|
|
52
57
|
# Detect the kind of callable provider
|
|
53
58
|
self._detect_kind()
|
|
54
59
|
|
|
60
|
+
self._is_coroutine = self._kind == CallableKind.COROUTINE
|
|
61
|
+
self._is_generator = self._kind == CallableKind.GENERATOR
|
|
62
|
+
self._is_async_generator = self._kind == CallableKind.ASYNC_GENERATOR
|
|
63
|
+
self._is_async = self._is_coroutine or self._is_async_generator
|
|
64
|
+
self._is_resource = self._is_generator or self._is_async_generator
|
|
65
|
+
|
|
55
66
|
# Validate the scope of the provider
|
|
56
67
|
self._validate_scope()
|
|
57
68
|
|
|
@@ -96,13 +107,30 @@ class Provider:
|
|
|
96
107
|
def parameters(self) -> list[inspect.Parameter]:
|
|
97
108
|
return self._parameters
|
|
98
109
|
|
|
110
|
+
@property
|
|
111
|
+
def is_coroutine(self) -> bool:
|
|
112
|
+
"""Check if the provider is a coroutine."""
|
|
113
|
+
return self._is_coroutine
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def is_generator(self) -> bool:
|
|
117
|
+
"""Check if the provider is a generator."""
|
|
118
|
+
return self._is_generator
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def is_async_generator(self) -> bool:
|
|
122
|
+
"""Check if the provider is an async generator."""
|
|
123
|
+
return self._is_async_generator
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def is_async(self) -> bool:
|
|
127
|
+
"""Check if the provider is an async callable."""
|
|
128
|
+
return self._is_async
|
|
129
|
+
|
|
99
130
|
@property
|
|
100
131
|
def is_resource(self) -> bool:
|
|
101
132
|
"""Check if the provider is a resource."""
|
|
102
|
-
return self.
|
|
103
|
-
CallableKind.GENERATOR,
|
|
104
|
-
CallableKind.ASYNC_GENERATOR,
|
|
105
|
-
}
|
|
133
|
+
return self._is_resource
|
|
106
134
|
|
|
107
135
|
def _validate_scope(self) -> None:
|
|
108
136
|
"""Validate the scope of the provider."""
|
anydi/_types.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
from collections.abc import Iterable
|
|
4
5
|
from dataclasses import dataclass
|
|
5
|
-
from
|
|
6
|
+
from types import ModuleType
|
|
7
|
+
from typing import Annotated, Any, NamedTuple, Union
|
|
6
8
|
|
|
7
9
|
from typing_extensions import Literal, Self, TypeAlias
|
|
8
10
|
|
|
9
11
|
Scope = Literal["transient", "singleton", "request"]
|
|
10
12
|
|
|
11
|
-
T = TypeVar("T")
|
|
12
13
|
AnyInterface: TypeAlias = Union[type[Any], Annotated[Any, ...]]
|
|
13
|
-
Interface: TypeAlias = type[T]
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Marker:
|
|
@@ -47,3 +47,19 @@ class DependencyWrapper:
|
|
|
47
47
|
if name in {"interface", "instance"}:
|
|
48
48
|
return object.__getattribute__(self, name)
|
|
49
49
|
return getattr(self.instance, name)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ProviderDecoratorArgs(NamedTuple):
|
|
53
|
+
scope: Scope
|
|
54
|
+
override: bool
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True)
|
|
58
|
+
class Dependency:
|
|
59
|
+
member: Any
|
|
60
|
+
module: ModuleType
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class InjectableDecoratorArgs(NamedTuple):
|
|
64
|
+
wrapped: bool
|
|
65
|
+
tags: Iterable[str] | None
|
anydi/ext/django/middleware.py
CHANGED
|
@@ -14,15 +14,15 @@ def request_scoped_middleware(
|
|
|
14
14
|
if iscoroutinefunction(get_response):
|
|
15
15
|
|
|
16
16
|
async def async_middleware(request: HttpRequest) -> Any:
|
|
17
|
-
async with container.arequest_context() as
|
|
18
|
-
|
|
17
|
+
async with container.arequest_context() as context:
|
|
18
|
+
context.set(HttpRequest, request)
|
|
19
19
|
return await get_response(request)
|
|
20
20
|
|
|
21
21
|
return async_middleware
|
|
22
22
|
|
|
23
23
|
def middleware(request: HttpRequest) -> Any:
|
|
24
|
-
with container.request_context() as
|
|
25
|
-
|
|
24
|
+
with container.request_context() as context:
|
|
25
|
+
context.set(HttpRequest, request)
|
|
26
26
|
return get_response(request)
|
|
27
27
|
|
|
28
28
|
return middleware
|
|
@@ -18,6 +18,6 @@ class RequestScopedMiddleware(BaseHTTPMiddleware):
|
|
|
18
18
|
async def dispatch(
|
|
19
19
|
self, request: Request, call_next: RequestResponseEndpoint
|
|
20
20
|
) -> Response:
|
|
21
|
-
async with self.container.arequest_context() as
|
|
22
|
-
|
|
21
|
+
async with self.container.arequest_context() as context:
|
|
22
|
+
context.set(Request, request)
|
|
23
23
|
return await call_next(request)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
anydi/__init__.py,sha256=
|
|
2
|
-
anydi/_container.py,sha256=
|
|
3
|
-
anydi/_context.py,sha256=
|
|
1
|
+
anydi/__init__.py,sha256=OfRg2EfXD65pHTGQKhfkABMwUhw5LvsuTQV_Tv4V4wk,501
|
|
2
|
+
anydi/_container.py,sha256=RyP0k8MXZfDr3mjjMo86auidKrlAGstEgY2AxpAHEAg,35346
|
|
3
|
+
anydi/_context.py,sha256=7LV_SL4QWkJeiG7_4D9PZ5lmU-MPzhofxC95zCgY9Gc,2651
|
|
4
4
|
anydi/_logger.py,sha256=UpubJUnW83kffFxkhUlObm2DmZX1Pjqoz9YFKS-JOPg,52
|
|
5
|
-
anydi/
|
|
6
|
-
anydi/
|
|
7
|
-
anydi/_scanner.py,sha256=F2sHgJvkRYXYnu4F5iSrnIPVzwnNeS7tRPXziirh4NI,4898
|
|
8
|
-
anydi/_types.py,sha256=90xdbH2NrFXbridFf9mjOknhcXMW5L0jm92zP_LvKrg,1120
|
|
5
|
+
anydi/_provider.py,sha256=fcfzYOlpJ1fMqbMPAd6uI9_lap8n2ljJ9e10OEQpI4s,7176
|
|
6
|
+
anydi/_types.py,sha256=55Wvaxcs2DPpVXrMqhHebT_ZeGDnH-H_zhND306vaoU,1397
|
|
9
7
|
anydi/_utils.py,sha256=INI0jNIXrJ6LS4zqJymMO2yUEobpxmBGASf4G_vR6AU,4378
|
|
10
8
|
anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
9
|
anydi/ext/_utils.py,sha256=U6sRqWzccWUu7eMhbXX1NrwcaxitQF9cO1KxnKF37gw,2566
|
|
@@ -14,7 +12,7 @@ anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb
|
|
|
14
12
|
anydi/ext/django/_settings.py,sha256=Z0RlAuXoO73oahWeMkK10w8c-4uCBde-DBpeKTV5USY,853
|
|
15
13
|
anydi/ext/django/_utils.py,sha256=q6X6GApBm0oBK8DnoRZhTq2m4tAdKRYL__gVgKn3idg,3977
|
|
16
14
|
anydi/ext/django/apps.py,sha256=mjbf_mDCpNSriGnILzhRIr8wFHLMEK8sUerbmRku6i0,2844
|
|
17
|
-
anydi/ext/django/middleware.py,sha256=
|
|
15
|
+
anydi/ext/django/middleware.py,sha256=5OUdp0OWRozyW338Sq04BDhacaFlyUTTOduS_7EwCTA,854
|
|
18
16
|
anydi/ext/django/ninja/__init__.py,sha256=kW3grUgWp_nkWSG_-39ADHMrZLGNcj9TsJ9OW8iWWrk,546
|
|
19
17
|
anydi/ext/django/ninja/_operation.py,sha256=wSWa7D73XTVlOibmOciv2l6JHPe1ERZcXrqI8W-oO2w,2696
|
|
20
18
|
anydi/ext/django/ninja/_signature.py,sha256=2cSzKxBIxXLqtwNuH6GSlmjVJFftoGmleWfyk_NVEWw,2207
|
|
@@ -23,10 +21,10 @@ anydi/ext/faststream.py,sha256=qXnNGvAqWWp9kbhbQUE6EF_OPUiYQmtOH211_O7BI_0,1898
|
|
|
23
21
|
anydi/ext/pydantic_settings.py,sha256=8IXXLuG_OvKbvKlBkBRQUHcXgbTpgQUxeWyoMcRIUQM,1488
|
|
24
22
|
anydi/ext/pytest_plugin.py,sha256=3x_ZYFcLp4ZCRrs7neoohmWz56O9ydm92jxi_LnyD7w,4298
|
|
25
23
|
anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
anydi/ext/starlette/middleware.py,sha256=
|
|
24
|
+
anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
|
|
27
25
|
anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
anydi-0.34.
|
|
29
|
-
anydi-0.34.
|
|
30
|
-
anydi-0.34.
|
|
31
|
-
anydi-0.34.
|
|
32
|
-
anydi-0.34.
|
|
26
|
+
anydi-0.34.1a0.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
|
|
27
|
+
anydi-0.34.1a0.dist-info/METADATA,sha256=ifXLCYsyeltoiS9jE3Kx49Bh3AOj86Q3zYXhypnb3sc,5073
|
|
28
|
+
anydi-0.34.1a0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
29
|
+
anydi-0.34.1a0.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
|
|
30
|
+
anydi-0.34.1a0.dist-info/RECORD,,
|
anydi/_module.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""AnyDI decorators module."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import inspect
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
|
7
|
-
|
|
8
|
-
from typing_extensions import Concatenate, NamedTuple, ParamSpec
|
|
9
|
-
|
|
10
|
-
from ._types import Scope
|
|
11
|
-
from ._utils import import_string
|
|
12
|
-
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from ._container import Container
|
|
15
|
-
|
|
16
|
-
T = TypeVar("T")
|
|
17
|
-
M = TypeVar("M", bound="Module")
|
|
18
|
-
P = ParamSpec("P")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ModuleMeta(type):
|
|
22
|
-
"""A metaclass used for the Module base class."""
|
|
23
|
-
|
|
24
|
-
def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> Any:
|
|
25
|
-
attrs["providers"] = [
|
|
26
|
-
(name, getattr(value, "__provider__"))
|
|
27
|
-
for name, value in attrs.items()
|
|
28
|
-
if hasattr(value, "__provider__")
|
|
29
|
-
]
|
|
30
|
-
return super().__new__(cls, name, bases, attrs)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Module(metaclass=ModuleMeta):
|
|
34
|
-
"""A base class for defining AnyDI modules."""
|
|
35
|
-
|
|
36
|
-
providers: list[tuple[str, ProviderDecoratorArgs]]
|
|
37
|
-
|
|
38
|
-
def configure(self, container: Container) -> None:
|
|
39
|
-
"""Configure the AnyDI container with providers and their dependencies."""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class ModuleRegistry:
|
|
43
|
-
def __init__(self, container: Container) -> None:
|
|
44
|
-
self.container = container
|
|
45
|
-
|
|
46
|
-
def register(
|
|
47
|
-
self, module: Module | type[Module] | Callable[[Container], None] | str
|
|
48
|
-
) -> None:
|
|
49
|
-
"""Register a module as a callable, module type, or module instance."""
|
|
50
|
-
|
|
51
|
-
# Callable Module
|
|
52
|
-
if inspect.isfunction(module):
|
|
53
|
-
module(self.container)
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
# Module path
|
|
57
|
-
if isinstance(module, str):
|
|
58
|
-
module = import_string(module)
|
|
59
|
-
|
|
60
|
-
# Class based Module or Module type
|
|
61
|
-
if inspect.isclass(module) and issubclass(module, Module):
|
|
62
|
-
module = module()
|
|
63
|
-
|
|
64
|
-
if isinstance(module, Module):
|
|
65
|
-
module.configure(self.container)
|
|
66
|
-
for provider_name, decorator_args in module.providers:
|
|
67
|
-
obj = getattr(module, provider_name)
|
|
68
|
-
self.container.provider(
|
|
69
|
-
scope=decorator_args.scope,
|
|
70
|
-
override=decorator_args.override,
|
|
71
|
-
)(obj)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class ProviderDecoratorArgs(NamedTuple):
|
|
75
|
-
scope: Scope
|
|
76
|
-
override: bool
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def provider(
|
|
80
|
-
*, scope: Scope, override: bool = False
|
|
81
|
-
) -> Callable[[Callable[Concatenate[M, P], T]], Callable[Concatenate[M, P], T]]:
|
|
82
|
-
"""Decorator for marking a function or method as a provider in a AnyDI module."""
|
|
83
|
-
|
|
84
|
-
def decorator(
|
|
85
|
-
target: Callable[Concatenate[M, P], T],
|
|
86
|
-
) -> Callable[Concatenate[M, P], T]:
|
|
87
|
-
setattr(
|
|
88
|
-
target,
|
|
89
|
-
"__provider__",
|
|
90
|
-
ProviderDecoratorArgs(scope=scope, override=override),
|
|
91
|
-
)
|
|
92
|
-
return target
|
|
93
|
-
|
|
94
|
-
return decorator
|