anydi 0.55.1__py3-none-any.whl → 0.57.0__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/_types.py CHANGED
@@ -3,19 +3,21 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import inspect
6
- from collections.abc import AsyncIterator, Iterator
6
+ from collections.abc import AsyncIterator, Callable, Iterator
7
7
  from types import NoneType
8
- from typing import Any, Literal
8
+ from typing import TYPE_CHECKING, Annotated, Any, Literal, TypeVar
9
9
 
10
10
  from typing_extensions import Sentinel
11
11
 
12
+ T = TypeVar("T")
13
+
12
14
  Scope = Literal["transient", "singleton", "request"]
13
15
 
14
16
  NOT_SET = Sentinel("NOT_SET")
15
17
 
16
18
 
17
- class InjectMarker:
18
- """A marker object for declaring injectable dependencies."""
19
+ class ProvideMarker:
20
+ """A marker object for declaring dependency."""
19
21
 
20
22
  __slots__ = ("_interface",)
21
23
 
@@ -32,13 +34,52 @@ class InjectMarker:
32
34
  def interface(self, interface: Any) -> None:
33
35
  self._interface = interface
34
36
 
37
+ def __class_getitem__(cls, item: Any) -> Any:
38
+ return Annotated[item, cls()]
39
+
40
+
41
+ _provide_factory: Callable[[], Any] = ProvideMarker
42
+
43
+
44
+ def set_provide_factory(factory: Callable[[], Any]) -> Callable[[], Any]:
45
+ """Set the global factory used by Inject() and Provide."""
46
+ global _provide_factory
47
+ previous = _provide_factory
48
+ _provide_factory = factory
49
+ return previous
50
+
51
+
52
+ def is_provide_marker(obj: Any) -> bool:
53
+ return isinstance(obj, ProvideMarker)
54
+
35
55
 
36
- def is_inject_marker(obj: Any) -> bool:
37
- return isinstance(obj, InjectMarker)
56
+ class _ProvideMeta(type):
57
+ """Metaclass for Provide that delegates __class_getitem__ to the current factory."""
58
+
59
+ def __getitem__(cls, item: Any) -> Any:
60
+ # Use the current factory's __class_getitem__ if available
61
+ factory = _provide_factory
62
+ if hasattr(factory, "__class_getitem__"):
63
+ return factory.__class_getitem__(item) # type: ignore[attr-defined]
64
+ # Fallback to creating Annotated with factory instance
65
+ return Annotated[item, factory()]
66
+
67
+
68
+ if TYPE_CHECKING:
69
+ Provide = Annotated[T, ProvideMarker()]
70
+
71
+ else:
72
+
73
+ class Provide(metaclass=_ProvideMeta):
74
+ pass
38
75
 
39
76
 
40
77
  def Inject() -> Any:
41
- return InjectMarker()
78
+ return _provide_factory()
79
+
80
+
81
+ # Alias from backward compatibility
82
+ is_inject_marker = is_provide_marker
42
83
 
43
84
 
44
85
  class Event:
anydi/ext/fastapi.py CHANGED
@@ -11,37 +11,12 @@ from fastapi.dependencies.models import Dependant
11
11
  from fastapi.routing import APIRoute
12
12
  from starlette.requests import Request
13
13
 
14
- from anydi._container import Container
15
- from anydi._types import InjectMarker
14
+ from anydi import Container
15
+ from anydi._types import Inject, ProvideMarker, set_provide_factory
16
16
 
17
17
  from .starlette.middleware import RequestScopedMiddleware
18
18
 
19
- __all__ = ["RequestScopedMiddleware", "install", "get_container", "Inject"]
20
-
21
-
22
- def install(app: FastAPI, container: Container) -> None:
23
- """Install AnyDI into a FastAPI application.
24
-
25
- This function installs the AnyDI container into a FastAPI application by attaching
26
- it to the application state. It also patches the route dependencies to inject the
27
- required dependencies using AnyDI.
28
- """
29
- app.state.container = container # noqa
30
-
31
- patched = []
32
-
33
- for route in app.routes:
34
- if not isinstance(route, APIRoute):
35
- continue
36
- for dependant in _iter_dependencies(route.dependant):
37
- if dependant.cache_key in patched:
38
- continue
39
- patched.append(dependant.cache_key)
40
- call, *params = dependant.cache_key
41
- if not call:
42
- continue # pragma: no cover
43
- for parameter in inspect.signature(call, eval_str=True).parameters.values():
44
- container.validate_injected_parameter(parameter, call=call)
19
+ __all__ = ["install", "get_container", "Inject", "RequestScopedMiddleware"]
45
20
 
46
21
 
47
22
  def get_container(request: Request) -> Container:
@@ -49,10 +24,10 @@ def get_container(request: Request) -> Container:
49
24
  return cast(Container, request.app.state.container)
50
25
 
51
26
 
52
- class _Inject(params.Depends, InjectMarker):
27
+ class _ProvideMarker(params.Depends, ProvideMarker):
53
28
  def __init__(self) -> None:
54
29
  super().__init__(dependency=self._dependency, use_cache=True)
55
- InjectMarker.__init__(self)
30
+ ProvideMarker.__init__(self)
56
31
 
57
32
  async def _dependency(
58
33
  self, container: Annotated[Container, Depends(get_container)]
@@ -60,13 +35,36 @@ class _Inject(params.Depends, InjectMarker):
60
35
  return await container.aresolve(self.interface)
61
36
 
62
37
 
63
- def Inject() -> Any:
64
- return _Inject()
38
+ # Configure Inject() and Provide[T] to use FastAPI-specific marker
39
+ set_provide_factory(_ProvideMarker)
65
40
 
66
41
 
67
42
  def _iter_dependencies(dependant: Dependant) -> Iterator[Dependant]:
68
- """Iterate over the dependencies of a dependant."""
69
43
  yield dependant
70
44
  if dependant.dependencies:
71
45
  for sub_dependant in dependant.dependencies:
72
46
  yield from _iter_dependencies(sub_dependant)
47
+
48
+
49
+ def _validate_route_dependencies(
50
+ route: APIRoute, container: Container, patched: set[tuple[Any, ...]]
51
+ ) -> None:
52
+ for dependant in _iter_dependencies(route.dependant):
53
+ if dependant.cache_key in patched:
54
+ continue
55
+ patched.add(dependant.cache_key)
56
+ call, *_ = dependant.cache_key
57
+ if not call:
58
+ continue # pragma: no cover
59
+ for parameter in inspect.signature(call, eval_str=True).parameters.values():
60
+ container.validate_injected_parameter(parameter, call=call)
61
+
62
+
63
+ def install(app: FastAPI, container: Container) -> None:
64
+ """Install AnyDI into a FastAPI application."""
65
+ app.state.container = container # noqa
66
+ patched: set[tuple[Any, ...]] = set()
67
+ for route in app.routes:
68
+ if not isinstance(route, APIRoute):
69
+ continue
70
+ _validate_route_dependencies(route, container, patched)
anydi/ext/faststream.py CHANGED
@@ -10,49 +10,43 @@ from faststream import ContextRepo
10
10
  from faststream.broker.core.usecase import BrokerUsecase
11
11
 
12
12
  from anydi import Container
13
- from anydi._types import InjectMarker
13
+ from anydi._types import Inject, ProvideMarker, set_provide_factory
14
14
 
15
-
16
- def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
17
- """Install AnyDI into a FastStream broker.
18
-
19
- This function installs the AnyDI container into a FastStream broker by attaching
20
- it to the broker. It also patches the broker handlers to inject the required
21
- dependencies using AnyDI.
22
- """
23
- broker._container = container # type: ignore
24
-
25
- for handler in _get_broken_handlers(broker):
26
- call = handler._original_call # noqa
27
- for parameter in inspect.signature(call, eval_str=True).parameters.values():
28
- container.validate_injected_parameter(parameter, call=call)
29
-
30
-
31
- def _get_broken_handlers(broker: BrokerUsecase[Any, Any]) -> list[Any]:
32
- if (handlers := getattr(broker, "handlers", None)) is not None:
33
- return [handler.calls[0][0] for handler in handlers.values()]
34
- # faststream > 0.5.0
35
- return [
36
- subscriber.calls[0].handler
37
- for subscriber in broker._subscribers.values() # noqa
38
- ]
15
+ __all__ = ["install", "get_container", "Inject"]
39
16
 
40
17
 
41
18
  def get_container(broker: BrokerUsecase[Any, Any]) -> Container:
19
+ """Get the AnyDI container from a FastStream broker."""
42
20
  return cast(Container, getattr(broker, "_container")) # noqa
43
21
 
44
22
 
45
- class _Inject(Depends, InjectMarker):
46
- """Parameter dependency class for injecting dependencies using AnyDI."""
47
-
23
+ class _ProvideMarker(Depends, ProvideMarker):
48
24
  def __init__(self) -> None:
49
25
  super().__init__(dependency=self._dependency, use_cache=True, cast=True)
50
- InjectMarker.__init__(self)
26
+ ProvideMarker.__init__(self)
51
27
 
52
28
  async def _dependency(self, context: ContextRepo) -> Any:
53
29
  container = get_container(context.get("broker"))
54
30
  return await container.aresolve(self.interface)
55
31
 
56
32
 
57
- def Inject() -> Any:
58
- return _Inject()
33
+ # Configure Inject() and Provide[T] to use FastStream-specific marker
34
+ set_provide_factory(_ProvideMarker)
35
+
36
+
37
+ def _get_broker_handlers(broker: BrokerUsecase[Any, Any]) -> list[Any]:
38
+ if (handlers := getattr(broker, "handlers", None)) is not None:
39
+ return [handler.calls[0][0] for handler in handlers.values()]
40
+ return [
41
+ subscriber.calls[0].handler
42
+ for subscriber in broker._subscribers.values() # noqa
43
+ ]
44
+
45
+
46
+ def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
47
+ """Install AnyDI into a FastStream broker."""
48
+ broker._container = container # type: ignore
49
+ for handler in _get_broker_handlers(broker):
50
+ call = handler._original_call # noqa
51
+ for parameter in inspect.signature(call, eval_str=True).parameters.values():
52
+ container.validate_injected_parameter(parameter, call=call)
@@ -22,7 +22,8 @@ def install(
22
22
  prefix += "."
23
23
 
24
24
  def _register_settings(_settings: BaseSettings) -> None:
25
- all_fields = {**_settings.model_fields, **_settings.model_computed_fields}
25
+ settings_cls = type(_settings)
26
+ all_fields = {**settings_cls.model_fields, **settings_cls.model_computed_fields}
26
27
  for setting_name, field_info in all_fields.items():
27
28
  if isinstance(field_info, ComputedFieldInfo):
28
29
  interface = field_info.return_type