anydi 0.34.0__py3-none-any.whl → 0.34.1__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/_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 TYPE_CHECKING, Any, Callable, ClassVar
5
+ from typing import Any
8
6
 
9
- from typing_extensions import Self, final
7
+ from typing_extensions import Self
10
8
 
11
- from ._provider import CallableKind, Provider
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
- class ScopedContext(abc.ABC):
25
- """ScopedContext base class."""
15
+ __slots__ = ("_instances", "_stack", "_async_stack")
26
16
 
27
- scope: ClassVar[Scope]
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 get_or_create(self, provider: Provider) -> tuple[Any, bool]:
157
- """Get an instance of a dependency from the scoped context."""
158
- instance = self._instances.get(provider.interface)
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
- async def aget_or_create(self, provider: Provider) -> tuple[Any, bool]:
175
- """Get an async instance of a dependency from the scoped context."""
176
- instance = self._instances.get(provider.interface)
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 has(self, interface: AnyInterface) -> bool:
189
- """Check if the scoped context has an instance of the dependency."""
190
- return interface in self._instances
30
+ def enter(self, cm: contextlib.AbstractContextManager[Any]) -> Any:
31
+ """Enter the context."""
32
+ return self._stack.enter_context(cm)
191
33
 
192
- def _create_instance(self, provider: Provider) -> Any:
193
- """Create an instance using the provider."""
194
- instance = super()._create_instance(provider)
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 _create_resource(self, provider: Provider) -> Any:
201
- """Create a resource using the provider."""
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
- async def _acreate_instance(self, provider: Provider) -> Any:
207
- """Create an instance asynchronously using the provider."""
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
- async def _acreate_resource(self, provider: Provider) -> Any:
215
- """Create a resource asynchronously using the provider."""
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 delete(self, interface: AnyInterface) -> None:
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
- ) -> bool:
59
+ ) -> Any:
235
60
  """Exit the context."""
236
- return self._stack.__exit__(exc_type, exc_val, exc_tb) # type: ignore[return-value]
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._kind in {
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 typing import Annotated, Any, TypeVar, Union
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
@@ -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 ctx:
18
- ctx.set(HttpRequest, instance=request)
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 ctx:
25
- ctx.set(HttpRequest, instance=request)
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 ctx:
22
- ctx.set(Request, instance=request)
21
+ async with self.container.arequest_context() as context:
22
+ context.set(Request, request)
23
23
  return await call_next(request)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anydi
3
- Version: 0.34.0
3
+ Version: 0.34.1
4
4
  Summary: Dependency Injection library
5
5
  Home-page: https://github.com/antonrh/anydi
6
6
  License: MIT
@@ -29,7 +29,7 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
29
29
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
30
  Classifier: Typing :: Typed
31
31
  Provides-Extra: docs
32
- Requires-Dist: anyio (>=3.6.2,<4.0.0)
32
+ Requires-Dist: anyio (>=3.7.1)
33
33
  Requires-Dist: mkdocs (>=1.4.2,<2.0.0) ; extra == "docs"
34
34
  Requires-Dist: mkdocs-material (>=9.5.29,<10.0.0) ; extra == "docs"
35
35
  Requires-Dist: typing-extensions (>=4.12.1,<5.0.0)
@@ -1,11 +1,9 @@
1
- anydi/__init__.py,sha256=EsR-HiMe8cWS9PQbY23ibc91STK1WTn02DFMPV-TNU4,509
2
- anydi/_container.py,sha256=p1e8Lrh5JIzrtSsvqYWe8sP-W7dmlpxERspOGS4iivE,21884
3
- anydi/_context.py,sha256=2QvG4fDnfafXu7yF2EbY8kds96hkKyd8vni_NVmUrRs,12734
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/_module.py,sha256=cgojC7Z7oMtsUnkfSc65cRYOfZ8Q6KwjusNJzx_VSbk,2729
6
- anydi/_provider.py,sha256=w_GnRo324aqNORRJwuURexA54c1M3smj34Q8EaV0QGE,6213
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=tryIaBVmfPmilGrnKpLNlLCOPN5rw4_pXY3ojFXv3O0,856
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=PKip_omFZDgg7h2OY-nnV2OIS1MbbmrrOJBwG7_Peuw,793
24
+ anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
27
25
  anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- anydi-0.34.0.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
29
- anydi-0.34.0.dist-info/METADATA,sha256=VwSg8GNsINsMsEnlpRkFPxzGDCqYuqfyt5yJ1sL3pR8,5071
30
- anydi-0.34.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
31
- anydi-0.34.0.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
32
- anydi-0.34.0.dist-info/RECORD,,
26
+ anydi-0.34.1.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
27
+ anydi-0.34.1.dist-info/METADATA,sha256=aeIeJl8UmjLDk5vhQfgIJedZH7UCFUrAXtHdcJZx1gI,5064
28
+ anydi-0.34.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
+ anydi-0.34.1.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
30
+ anydi-0.34.1.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