anydi 0.25.0a1__tar.gz → 0.25.0a2__tar.gz

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.
Files changed (31) hide show
  1. {anydi-0.25.0a1 → anydi-0.25.0a2}/PKG-INFO +57 -1
  2. {anydi-0.25.0a1 → anydi-0.25.0a2}/README.md +56 -0
  3. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/_container.py +37 -50
  4. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/_context.py +8 -8
  5. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/_module.py +4 -4
  6. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/_scanner.py +19 -27
  7. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/_types.py +6 -4
  8. anydi-0.25.0a2/anydi/_utils.py +111 -0
  9. anydi-0.25.0a2/anydi/ext/django/__init__.py +9 -0
  10. anydi-0.25.0a2/anydi/ext/django/_settings.py +39 -0
  11. anydi-0.25.0a2/anydi/ext/django/_utils.py +111 -0
  12. anydi-0.25.0a2/anydi/ext/django/apps.py +82 -0
  13. anydi-0.25.0a2/anydi/ext/django/middleware.py +26 -0
  14. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/django/ninja/__init__.py +1 -1
  15. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/django/ninja/_operation.py +2 -0
  16. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/django/ninja/_signature.py +2 -0
  17. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/fastapi.py +2 -2
  18. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/pytest_plugin.py +12 -10
  19. {anydi-0.25.0a1 → anydi-0.25.0a2}/pyproject.toml +2 -2
  20. anydi-0.25.0a1/anydi/_utils.py +0 -111
  21. anydi-0.25.0a1/anydi/ext/django/__init__.py +0 -3
  22. anydi-0.25.0a1/anydi/ext/django/_utils.py +0 -14
  23. anydi-0.25.0a1/anydi/ext/django/apps.py +0 -177
  24. {anydi-0.25.0a1 → anydi-0.25.0a2}/LICENSE +0 -0
  25. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/__init__.py +0 -0
  26. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/_logger.py +0 -0
  27. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/__init__.py +0 -0
  28. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/django/_container.py +0 -0
  29. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/starlette/__init__.py +0 -0
  30. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/ext/starlette/middleware.py +0 -0
  31. {anydi-0.25.0a1 → anydi-0.25.0a2}/anydi/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anydi
3
- Version: 0.25.0a1
3
+ Version: 0.25.0a2
4
4
  Summary: Dependency Injection library
5
5
  Home-page: https://github.com/antonrh/anydi
6
6
  License: MIT
@@ -139,3 +139,59 @@ def say_hello(message: str = Inject()) -> dict[str, str]:
139
139
  anydi.ext.fastapi.install(app, container)
140
140
  ```
141
141
 
142
+
143
+
144
+ ## Django Ninja Example
145
+
146
+ *container.py*
147
+
148
+ ```python
149
+ from anydi import Container
150
+
151
+
152
+ def get_container() -> Container:
153
+ container = Container()
154
+
155
+ @container.provider(scope="singleton")
156
+ def message() -> str:
157
+ return "Hello, World!"
158
+
159
+ return container
160
+ ```
161
+
162
+ *settings.py*
163
+
164
+ ```python
165
+ INSTALLED_APPS = [
166
+ ...
167
+ "anydi.ext.django",
168
+ ]
169
+
170
+ ANYDI = {
171
+ "CONTAINER_FACTORY": "myapp.container.get_container",
172
+ "PATCH_NINJA": True,
173
+ }
174
+ ```
175
+
176
+ *urls.py*
177
+
178
+ ```python
179
+ from django.http import HttpRequest
180
+ from django.urls import path
181
+ from ninja import NinjaAPI
182
+
183
+ from anydi import auto
184
+
185
+ api = NinjaAPI()
186
+
187
+
188
+ @api.get("/hello")
189
+ def say_hello(request: HttpRequest, message: str = auto) -> dict[str, str]:
190
+ return {"message": message}
191
+
192
+
193
+ urlpatterns = [
194
+ path("api/", api.urls),
195
+ ]
196
+ ```
197
+
@@ -98,3 +98,59 @@ def say_hello(message: str = Inject()) -> dict[str, str]:
98
98
  # Install the container into the FastAPI app
99
99
  anydi.ext.fastapi.install(app, container)
100
100
  ```
101
+
102
+
103
+
104
+ ## Django Ninja Example
105
+
106
+ *container.py*
107
+
108
+ ```python
109
+ from anydi import Container
110
+
111
+
112
+ def get_container() -> Container:
113
+ container = Container()
114
+
115
+ @container.provider(scope="singleton")
116
+ def message() -> str:
117
+ return "Hello, World!"
118
+
119
+ return container
120
+ ```
121
+
122
+ *settings.py*
123
+
124
+ ```python
125
+ INSTALLED_APPS = [
126
+ ...
127
+ "anydi.ext.django",
128
+ ]
129
+
130
+ ANYDI = {
131
+ "CONTAINER_FACTORY": "myapp.container.get_container",
132
+ "PATCH_NINJA": True,
133
+ }
134
+ ```
135
+
136
+ *urls.py*
137
+
138
+ ```python
139
+ from django.http import HttpRequest
140
+ from django.urls import path
141
+ from ninja import NinjaAPI
142
+
143
+ from anydi import auto
144
+
145
+ api = NinjaAPI()
146
+
147
+
148
+ @api.get("/hello")
149
+ def say_hello(request: HttpRequest, message: str = auto) -> dict[str, str]:
150
+ return {"message": message}
151
+
152
+
153
+ urlpatterns = [
154
+ path("api/", api.urls),
155
+ ]
156
+ ```
@@ -15,21 +15,16 @@ from typing import (
15
15
  Awaitable,
16
16
  Callable,
17
17
  ContextManager,
18
- Dict,
19
18
  Iterable,
20
19
  Iterator,
21
- List,
22
20
  Mapping,
23
- Optional,
24
21
  Sequence,
25
- Type,
26
22
  TypeVar,
27
- Union,
28
23
  cast,
29
24
  overload,
30
25
  )
31
26
 
32
- from typing_extensions import Annotated, ParamSpec, final, get_args, get_origin
27
+ from typing_extensions import ParamSpec, final, get_args, get_origin
33
28
 
34
29
  try:
35
30
  from types import NoneType
@@ -48,12 +43,17 @@ from ._logger import logger
48
43
  from ._module import Module, ModuleRegistry
49
44
  from ._scanner import Scanner
50
45
  from ._types import AnyInterface, Interface, Provider, Scope, is_marker
51
- from ._utils import get_full_qualname, get_signature, is_builtin_type
46
+ from ._utils import (
47
+ get_full_qualname,
48
+ get_typed_parameters,
49
+ get_typed_return_annotation,
50
+ is_builtin_type,
51
+ )
52
52
 
53
53
  T = TypeVar("T", bound=Any)
54
54
  P = ParamSpec("P")
55
55
 
56
- ALLOWED_SCOPES: Dict[Scope, List[Scope]] = {
56
+ ALLOWED_SCOPES: dict[Scope, list[Scope]] = {
57
57
  "singleton": ["singleton"],
58
58
  "request": ["request", "singleton"],
59
59
  "transient": ["transient", "singleton", "request"],
@@ -71,10 +71,9 @@ class Container:
71
71
  def __init__(
72
72
  self,
73
73
  *,
74
- providers: Optional[Mapping[Type[Any], Provider]] = None,
75
- modules: Optional[
76
- Sequence[Union[Module, Type[Module], Callable[[Container], None]]]
77
- ] = None,
74
+ providers: Mapping[type[Any], Provider] | None = None,
75
+ modules: Sequence[Module | type[Module] | Callable[[Container], None]]
76
+ | None = None,
78
77
  strict: bool = False,
79
78
  ) -> None:
80
79
  """Initialize the AnyDI instance.
@@ -84,13 +83,13 @@ class Container:
84
83
  modules: Optional sequence of modules to register during initialization.
85
84
  strict: Whether to enable strict mode. Defaults to False.
86
85
  """
87
- self._providers: Dict[Type[Any], Provider] = {}
86
+ self._providers: dict[type[Any], Provider] = {}
88
87
  self._singleton_context = SingletonContext(self)
89
88
  self._transient_context = TransientContext(self)
90
- self._request_context_var: ContextVar[Optional[RequestContext]] = ContextVar(
89
+ self._request_context_var: ContextVar[RequestContext | None] = ContextVar(
91
90
  "request_context", default=None
92
91
  )
93
- self._override_instances: Dict[Type[Any], Any] = {}
92
+ self._override_instances: dict[type[Any], Any] = {}
94
93
  self._strict = strict
95
94
 
96
95
  # Components
@@ -117,7 +116,7 @@ class Container:
117
116
  return self._strict
118
117
 
119
118
  @property
120
- def providers(self) -> Dict[Type[Any], Provider]:
119
+ def providers(self) -> dict[type[Any], Provider]:
121
120
  """Get the registered providers.
122
121
 
123
122
  Returns:
@@ -333,7 +332,7 @@ class Container:
333
332
  """
334
333
  related_providers = []
335
334
 
336
- for parameter in provider.parameters.values():
335
+ for parameter in provider.parameters:
337
336
  if parameter.annotation is inspect._empty: # noqa
338
337
  raise TypeError(
339
338
  f"Missing provider `{provider}` "
@@ -363,7 +362,7 @@ class Container:
363
362
  "registered with matching scopes."
364
363
  )
365
364
 
366
- def _detect_scope(self, obj: Callable[..., Any]) -> Optional[Scope]:
365
+ def _detect_scope(self, obj: Callable[..., Any]) -> Scope | None:
367
366
  """Detect the scope for a provider.
368
367
 
369
368
  Args:
@@ -372,7 +371,7 @@ class Container:
372
371
  The auto scope, or None if the auto scope cannot be detected.
373
372
  """
374
373
  has_transient, has_request, has_singleton = False, False, False
375
- for parameter in get_signature(obj).parameters.values():
374
+ for parameter in get_typed_parameters(obj):
376
375
  sub_provider = self._get_or_register_provider(parameter.annotation)
377
376
  if not has_transient and sub_provider.scope == "transient":
378
377
  has_transient = True
@@ -389,7 +388,7 @@ class Container:
389
388
  return None
390
389
 
391
390
  def register_module(
392
- self, module: Union[Module, Type[Module], Callable[[Container], None]]
391
+ self, module: Module | type[Module] | Callable[[Container], None]
393
392
  ) -> None:
394
393
  """Register a module as a callable, module type, or module instance.
395
394
 
@@ -633,14 +632,11 @@ class Container:
633
632
  def inject(self) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
634
633
 
635
634
  def inject(
636
- self, obj: Union[Callable[P, Union[T, Awaitable[T]]], None] = None
637
- ) -> Union[
638
- Callable[
639
- [Callable[P, Union[T, Awaitable[T]]]],
640
- Callable[P, Union[T, Awaitable[T]]],
641
- ],
642
- Callable[P, Union[T, Awaitable[T]]],
643
- ]:
635
+ self, obj: Callable[P, T | Awaitable[T]] | None = None
636
+ ) -> (
637
+ Callable[[Callable[P, T | Awaitable[T]]], Callable[P, T | Awaitable[T]]]
638
+ | Callable[P, T | Awaitable[T]]
639
+ ):
644
640
  """Decorator to inject dependencies into a callable.
645
641
 
646
642
  Args:
@@ -652,8 +648,8 @@ class Container:
652
648
  """
653
649
 
654
650
  def decorator(
655
- obj: Callable[P, Union[T, Awaitable[T]]],
656
- ) -> Callable[P, Union[T, Awaitable[T]]]:
651
+ obj: Callable[P, T | Awaitable[T]],
652
+ ) -> Callable[P, T | Awaitable[T]]:
657
653
  injected_params = self._get_injected_params(obj)
658
654
 
659
655
  if inspect.iscoroutinefunction(obj):
@@ -694,12 +690,9 @@ class Container:
694
690
  def scan(
695
691
  self,
696
692
  /,
697
- packages: Union[
698
- Union[types.ModuleType, str],
699
- Iterable[Union[types.ModuleType, str]],
700
- ],
693
+ packages: types.ModuleType | str | Iterable[types.ModuleType | str],
701
694
  *,
702
- tags: Optional[Iterable[str]] = None,
695
+ tags: Iterable[str] | None = None,
703
696
  ) -> None:
704
697
  """Scan packages or modules for decorated members and inject dependencies.
705
698
 
@@ -723,32 +716,26 @@ class Container:
723
716
  Raises:
724
717
  TypeError: If the provider return annotation is missing or invalid.
725
718
  """
726
- annotation = get_signature(obj).return_annotation
719
+ annotation = get_typed_return_annotation(obj)
727
720
 
728
- if annotation is inspect._empty: # noqa
721
+ if annotation is None:
729
722
  raise TypeError(
730
723
  f"Missing `{get_full_qualname(obj)}` provider return annotation."
731
724
  )
732
725
 
733
- origin = get_origin(annotation) or annotation
734
- args = get_args(annotation)
735
-
736
- # Supported generic types
737
- if origin in (list, dict, tuple, Annotated):
726
+ if get_origin(annotation) in (get_origin(Iterator), get_origin(AsyncIterator)):
727
+ args = get_args(annotation)
738
728
  if args:
739
- return annotation
729
+ return args[0]
740
730
  else:
741
731
  raise TypeError(
742
- f"Cannot use `{get_full_qualname(obj)}` generic type annotation "
732
+ f"Cannot use `{get_full_qualname(obj)}` resource type annotation "
743
733
  "without actual type."
744
734
  )
745
735
 
746
- try:
747
- return args[0]
748
- except IndexError:
749
- return annotation
736
+ return annotation
750
737
 
751
- def _get_injected_params(self, obj: Callable[..., Any]) -> Dict[str, Any]:
738
+ def _get_injected_params(self, obj: Callable[..., Any]) -> dict[str, Any]:
752
739
  """Get the injected parameters of a callable object.
753
740
 
754
741
  Args:
@@ -759,7 +746,7 @@ class Container:
759
746
  of the injected parameters.
760
747
  """
761
748
  injected_params = {}
762
- for parameter in get_signature(obj).parameters.values():
749
+ for parameter in get_typed_parameters(obj):
763
750
  if not is_marker(parameter.default):
764
751
  continue
765
752
  try:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import abc
4
4
  import contextlib
5
5
  from types import TracebackType
6
- from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Type, TypeVar, cast
6
+ from typing import TYPE_CHECKING, Any, TypeVar, cast
7
7
 
8
8
  from typing_extensions import Self, final
9
9
 
@@ -87,7 +87,7 @@ class ScopedContext(abc.ABC):
87
87
 
88
88
  def _get_provider_arguments(
89
89
  self, provider: Provider
90
- ) -> Tuple[List[Any], Dict[str, Any]]:
90
+ ) -> tuple[list[Any], dict[str, Any]]:
91
91
  """Retrieve the arguments for a provider.
92
92
 
93
93
  Args:
@@ -97,7 +97,7 @@ class ScopedContext(abc.ABC):
97
97
  The arguments for the provider.
98
98
  """
99
99
  args, kwargs = [], {}
100
- for parameter in provider.parameters.values():
100
+ for parameter in provider.parameters:
101
101
  instance = self.container.resolve(parameter.annotation)
102
102
  if parameter.kind == parameter.POSITIONAL_ONLY:
103
103
  args.append(instance)
@@ -107,7 +107,7 @@ class ScopedContext(abc.ABC):
107
107
 
108
108
  async def _aget_provider_arguments(
109
109
  self, provider: Provider
110
- ) -> Tuple[List[Any], Dict[str, Any]]:
110
+ ) -> tuple[list[Any], dict[str, Any]]:
111
111
  """Asynchronously retrieve the arguments for a provider.
112
112
 
113
113
  Args:
@@ -117,7 +117,7 @@ class ScopedContext(abc.ABC):
117
117
  The arguments for the provider.
118
118
  """
119
119
  args, kwargs = [], {}
120
- for parameter in provider.parameters.values():
120
+ for parameter in provider.parameters:
121
121
  instance = await self.container.aresolve(parameter.annotation)
122
122
  if parameter.kind == parameter.POSITIONAL_ONLY:
123
123
  args.append(instance)
@@ -132,7 +132,7 @@ class ResourceScopedContext(ScopedContext):
132
132
  def __init__(self, container: Container) -> None:
133
133
  """Initialize the ScopedContext."""
134
134
  super().__init__(container)
135
- self._instances: Dict[Type[Any], Any] = {}
135
+ self._instances: dict[type[Any], Any] = {}
136
136
  self._stack = contextlib.ExitStack()
137
137
  self._async_stack = contextlib.AsyncExitStack()
138
138
 
@@ -237,7 +237,7 @@ class ResourceScopedContext(ScopedContext):
237
237
 
238
238
  def __exit__(
239
239
  self,
240
- exc_type: Type[BaseException] | None,
240
+ exc_type: type[BaseException] | None,
241
241
  exc_val: BaseException | None,
242
242
  exc_tb: TracebackType | None,
243
243
  ) -> None:
@@ -265,7 +265,7 @@ class ResourceScopedContext(ScopedContext):
265
265
 
266
266
  async def __aexit__(
267
267
  self,
268
- exc_type: Type[BaseException] | None,
268
+ exc_type: type[BaseException] | None,
269
269
  exc_val: BaseException | None,
270
270
  exc_tb: TracebackType | None,
271
271
  ) -> None:
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import inspect
6
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type, TypeVar, Union
6
+ from typing import TYPE_CHECKING, Any, Callable, TypeVar
7
7
 
8
8
  from typing_extensions import Concatenate, NamedTuple, ParamSpec
9
9
 
@@ -24,7 +24,7 @@ class ModuleMeta(type):
24
24
  and stores it in the `providers` attribute.
25
25
  """
26
26
 
27
- def __new__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]) -> Any:
27
+ def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> Any:
28
28
  """Create a new instance of the ModuleMeta class.
29
29
 
30
30
  This method extracts provider information from the class attributes and
@@ -49,7 +49,7 @@ class ModuleMeta(type):
49
49
  class Module(metaclass=ModuleMeta):
50
50
  """A base class for defining AnyDI modules."""
51
51
 
52
- providers: List[Tuple[str, ProviderDecoratorArgs]]
52
+ providers: list[tuple[str, ProviderDecoratorArgs]]
53
53
 
54
54
  def configure(self, container: Container) -> None:
55
55
  """Configure the AnyDI container with providers and their dependencies.
@@ -67,7 +67,7 @@ class ModuleRegistry:
67
67
  self.container = container
68
68
 
69
69
  def register(
70
- self, module: Union[Module, Type[Module], Callable[[Container], None]]
70
+ self, module: Module | type[Module] | Callable[[Container], None]
71
71
  ) -> None:
72
72
  """Register a module as a callable, module type, or module instance.
73
73
 
@@ -10,8 +10,6 @@ from typing import (
10
10
  Any,
11
11
  Callable,
12
12
  Iterable,
13
- List,
14
- Optional,
15
13
  TypeVar,
16
14
  Union,
17
15
  cast,
@@ -22,7 +20,7 @@ from typing import (
22
20
  from typing_extensions import NamedTuple, ParamSpec
23
21
 
24
22
  from ._types import is_marker
25
- from ._utils import get_signature
23
+ from ._utils import get_typed_parameters
26
24
 
27
25
  if TYPE_CHECKING:
28
26
  from ._container import Container
@@ -56,9 +54,9 @@ class Scanner:
56
54
  def scan(
57
55
  self,
58
56
  /,
59
- packages: Union[Union[ModuleType, str], Iterable[Union[ModuleType, str]]],
57
+ packages: ModuleType | str | Iterable[ModuleType | str],
60
58
  *,
61
- tags: Optional[Iterable[str]] = None,
59
+ tags: Iterable[str] | None = None,
62
60
  ) -> None:
63
61
  """Scan packages or modules for decorated members and inject dependencies.
64
62
 
@@ -68,10 +66,10 @@ class Scanner:
68
66
  tags: Optional list of tags to filter the scanned members. Only members
69
67
  with at least one matching tag will be scanned. Defaults to None.
70
68
  """
71
- dependencies: List[Dependency] = []
69
+ dependencies: list[Dependency] = []
72
70
 
73
71
  if isinstance(packages, Iterable) and not isinstance(packages, str):
74
- scan_packages: Iterable[Union[ModuleType, str]] = packages
72
+ scan_packages: Iterable[ModuleType | str] = packages
75
73
  else:
76
74
  scan_packages = cast(Iterable[Union[ModuleType, str]], [packages])
77
75
 
@@ -84,10 +82,10 @@ class Scanner:
84
82
 
85
83
  def _scan_package(
86
84
  self,
87
- package: Union[ModuleType, str],
85
+ package: ModuleType | str,
88
86
  *,
89
- tags: Optional[Iterable[str]] = None,
90
- ) -> List[Dependency]:
87
+ tags: Iterable[str] | None = None,
88
+ ) -> list[Dependency]:
91
89
  """Scan a package or module for decorated members.
92
90
 
93
91
  Args:
@@ -107,7 +105,7 @@ class Scanner:
107
105
  if not package_path:
108
106
  return self._scan_module(package, tags=tags)
109
107
 
110
- dependencies: List[Dependency] = []
108
+ dependencies: list[Dependency] = []
111
109
 
112
110
  for module_info in pkgutil.walk_packages(
113
111
  path=package_path, prefix=package.__name__ + "."
@@ -119,7 +117,7 @@ class Scanner:
119
117
 
120
118
  def _scan_module(
121
119
  self, module: ModuleType, *, tags: Iterable[str]
122
- ) -> List[Dependency]:
120
+ ) -> list[Dependency]:
123
121
  """Scan a module for decorated members.
124
122
 
125
123
  Args:
@@ -130,7 +128,7 @@ class Scanner:
130
128
  Returns:
131
129
  A list of scanned dependencies.
132
130
  """
133
- dependencies: List[Dependency] = []
131
+ dependencies: list[Dependency] = []
134
132
 
135
133
  for _, member in inspect.getmembers(module):
136
134
  if getattr(member, "__module__", None) != module.__name__ or not callable(
@@ -159,10 +157,10 @@ class Scanner:
159
157
 
160
158
  # Get by Marker
161
159
  if inspect.isclass(member):
162
- signature = get_signature(member.__init__)
160
+ parameters = get_typed_parameters(member.__init__)
163
161
  else:
164
- signature = get_signature(member)
165
- for parameter in signature.parameters.values():
162
+ parameters = get_typed_parameters(member)
163
+ for parameter in parameters:
166
164
  if is_marker(parameter.default):
167
165
  dependencies.append(
168
166
  self._create_dependency(member=member, module=module)
@@ -188,7 +186,7 @@ class Scanner:
188
186
 
189
187
  class InjectDecoratorArgs(NamedTuple):
190
188
  wrapped: bool
191
- tags: Optional[Iterable[str]]
189
+ tags: Iterable[str] | None
192
190
 
193
191
 
194
192
  @overload
@@ -197,20 +195,14 @@ def injectable(obj: Callable[P, T]) -> Callable[P, T]: ...
197
195
 
198
196
  @overload
199
197
  def injectable(
200
- *, tags: Optional[Iterable[str]] = None
198
+ *, tags: Iterable[str] | None = None
201
199
  ) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
202
200
 
203
201
 
204
202
  def injectable(
205
- obj: Optional[Callable[P, T]] = None,
206
- tags: Optional[Iterable[str]] = None,
207
- ) -> Union[
208
- Callable[
209
- [Callable[P, T]],
210
- Callable[P, T],
211
- ],
212
- Callable[P, T],
213
- ]:
203
+ obj: Callable[P, T] | None = None,
204
+ tags: Iterable[str] | None = None,
205
+ ) -> Callable[[Callable[P, T]], Callable[P, T]] | Callable[P, T]:
214
206
  """Decorator for marking a function or method as requiring dependency injection.
215
207
 
216
208
  Args:
@@ -1,11 +1,13 @@
1
+ from __future__ import annotations
2
+
1
3
  import inspect
2
4
  from dataclasses import dataclass
3
5
  from functools import cached_property
4
6
  from typing import Any, Callable, Type, TypeVar, Union
5
7
 
6
- from typing_extensions import Annotated, Literal, Mapping, Self, TypeAlias
8
+ from typing_extensions import Annotated, Literal, Self, TypeAlias
7
9
 
8
- from ._utils import get_full_qualname, get_signature
10
+ from ._utils import get_full_qualname, get_typed_parameters
9
11
 
10
12
  Scope = Literal["transient", "singleton", "request"]
11
13
 
@@ -58,13 +60,13 @@ class Provider:
58
60
  return get_full_qualname(self.obj)
59
61
 
60
62
  @cached_property
61
- def parameters(self) -> Mapping[str, inspect.Parameter]:
63
+ def parameters(self) -> list[inspect.Parameter]:
62
64
  """Returns the parameters of the provider as a mapping.
63
65
 
64
66
  Returns:
65
67
  The parameters of the provider.
66
68
  """
67
- return get_signature(self.obj).parameters
69
+ return get_typed_parameters(self.obj)
68
70
 
69
71
  @cached_property
70
72
  def is_class(self) -> bool: