anydi 0.25.0__tar.gz → 0.25.0a0__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.0 → anydi-0.25.0a0}/PKG-INFO +3 -59
  2. {anydi-0.25.0 → anydi-0.25.0a0}/README.md +2 -58
  3. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/__init__.py +2 -2
  4. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/_container.py +51 -41
  5. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/_context.py +8 -8
  6. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/_module.py +4 -4
  7. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/_scanner.py +29 -21
  8. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/_types.py +4 -14
  9. anydi-0.25.0a0/anydi/_utils.py +111 -0
  10. anydi-0.25.0a0/anydi/ext/django/__init__.py +3 -0
  11. anydi-0.25.0a0/anydi/ext/django/_utils.py +14 -0
  12. anydi-0.25.0a0/anydi/ext/django/apps.py +138 -0
  13. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/django/ninja/__init__.py +2 -2
  14. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/django/ninja/_operation.py +0 -2
  15. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/django/ninja/_signature.py +2 -4
  16. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/fastapi.py +2 -2
  17. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/pytest_plugin.py +10 -12
  18. {anydi-0.25.0 → anydi-0.25.0a0}/pyproject.toml +2 -10
  19. anydi-0.25.0/anydi/_utils.py +0 -122
  20. anydi-0.25.0/anydi/ext/django/__init__.py +0 -9
  21. anydi-0.25.0/anydi/ext/django/_settings.py +0 -39
  22. anydi-0.25.0/anydi/ext/django/_utils.py +0 -111
  23. anydi-0.25.0/anydi/ext/django/apps.py +0 -82
  24. anydi-0.25.0/anydi/ext/django/middleware.py +0 -26
  25. {anydi-0.25.0 → anydi-0.25.0a0}/LICENSE +0 -0
  26. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/_logger.py +0 -0
  27. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/__init__.py +0 -0
  28. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/django/_container.py +0 -0
  29. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/starlette/__init__.py +0 -0
  30. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/ext/starlette/middleware.py +0 -0
  31. {anydi-0.25.0 → anydi-0.25.0a0}/anydi/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anydi
3
- Version: 0.25.0
3
+ Version: 0.25.0a0
4
4
  Summary: Dependency Injection library
5
5
  Home-page: https://github.com/antonrh/anydi
6
6
  License: MIT
@@ -89,7 +89,7 @@ pip install anydi
89
89
  *app.py*
90
90
 
91
91
  ```python
92
- from anydi import auto, Container
92
+ from anydi import Container, dep
93
93
 
94
94
  container = Container()
95
95
 
@@ -100,7 +100,7 @@ def message() -> str:
100
100
 
101
101
 
102
102
  @container.inject
103
- def say_hello(message: str = auto) -> None:
103
+ def say_hello(message: str = dep()) -> None:
104
104
  print(message)
105
105
 
106
106
 
@@ -139,59 +139,3 @@ 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
-
@@ -49,7 +49,7 @@ pip install anydi
49
49
  *app.py*
50
50
 
51
51
  ```python
52
- from anydi import auto, Container
52
+ from anydi import Container, dep
53
53
 
54
54
  container = Container()
55
55
 
@@ -60,7 +60,7 @@ def message() -> str:
60
60
 
61
61
 
62
62
  @container.inject
63
- def say_hello(message: str = auto) -> None:
63
+ def say_hello(message: str = dep()) -> None:
64
64
  print(message)
65
65
 
66
66
 
@@ -98,59 +98,3 @@ 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
- ```
@@ -1,6 +1,6 @@
1
1
  """AnyDI public objects and functions."""
2
2
 
3
- from typing import Any, cast
3
+ from typing import Any
4
4
 
5
5
  from ._container import Container, request, singleton, transient
6
6
  from ._module import Module, provider
@@ -14,7 +14,7 @@ def dep() -> Any:
14
14
 
15
15
 
16
16
  # Alias for dependency auto marker
17
- auto = cast(Any, Marker())
17
+ auto = dep
18
18
 
19
19
 
20
20
  __all__ = [
@@ -15,16 +15,21 @@ from typing import (
15
15
  Awaitable,
16
16
  Callable,
17
17
  ContextManager,
18
+ Dict,
18
19
  Iterable,
19
20
  Iterator,
21
+ List,
20
22
  Mapping,
23
+ Optional,
21
24
  Sequence,
25
+ Type,
22
26
  TypeVar,
27
+ Union,
23
28
  cast,
24
29
  overload,
25
30
  )
26
31
 
27
- from typing_extensions import ParamSpec, final, get_args, get_origin
32
+ from typing_extensions import Annotated, ParamSpec, final, get_args, get_origin
28
33
 
29
34
  try:
30
35
  from types import NoneType
@@ -42,19 +47,13 @@ from ._context import (
42
47
  from ._logger import logger
43
48
  from ._module import Module, ModuleRegistry
44
49
  from ._scanner import Scanner
45
- from ._types import AnyInterface, Interface, Provider, Scope, is_marker
46
- from ._utils import (
47
- get_full_qualname,
48
- get_typed_parameters,
49
- get_typed_return_annotation,
50
- has_resource_origin,
51
- is_builtin_type,
52
- )
50
+ from ._types import AnyInterface, Interface, Marker, Provider, Scope
51
+ from ._utils import get_full_qualname, get_signature, is_builtin_type
53
52
 
54
53
  T = TypeVar("T", bound=Any)
55
54
  P = ParamSpec("P")
56
55
 
57
- ALLOWED_SCOPES: dict[Scope, list[Scope]] = {
56
+ ALLOWED_SCOPES: Dict[Scope, List[Scope]] = {
58
57
  "singleton": ["singleton"],
59
58
  "request": ["request", "singleton"],
60
59
  "transient": ["transient", "singleton", "request"],
@@ -72,9 +71,10 @@ class Container:
72
71
  def __init__(
73
72
  self,
74
73
  *,
75
- providers: Mapping[type[Any], Provider] | None = None,
76
- modules: Sequence[Module | type[Module] | Callable[[Container], None]]
77
- | None = None,
74
+ providers: Optional[Mapping[Type[Any], Provider]] = None,
75
+ modules: Optional[
76
+ Sequence[Union[Module, Type[Module], Callable[[Container], None]]]
77
+ ] = None,
78
78
  strict: bool = False,
79
79
  ) -> None:
80
80
  """Initialize the AnyDI instance.
@@ -84,13 +84,13 @@ class Container:
84
84
  modules: Optional sequence of modules to register during initialization.
85
85
  strict: Whether to enable strict mode. Defaults to False.
86
86
  """
87
- self._providers: dict[type[Any], Provider] = {}
87
+ self._providers: Dict[Type[Any], Provider] = {}
88
88
  self._singleton_context = SingletonContext(self)
89
89
  self._transient_context = TransientContext(self)
90
- self._request_context_var: ContextVar[RequestContext | None] = ContextVar(
90
+ self._request_context_var: ContextVar[Optional[RequestContext]] = ContextVar(
91
91
  "request_context", default=None
92
92
  )
93
- self._override_instances: dict[type[Any], Any] = {}
93
+ self._override_instances: Dict[Type[Any], Any] = {}
94
94
  self._strict = strict
95
95
 
96
96
  # Components
@@ -117,7 +117,7 @@ class Container:
117
117
  return self._strict
118
118
 
119
119
  @property
120
- def providers(self) -> dict[type[Any], Provider]:
120
+ def providers(self) -> Dict[Type[Any], Provider]:
121
121
  """Get the registered providers.
122
122
 
123
123
  Returns:
@@ -333,7 +333,7 @@ class Container:
333
333
  """
334
334
  related_providers = []
335
335
 
336
- for parameter in provider.parameters:
336
+ for parameter in provider.parameters.values():
337
337
  if parameter.annotation is inspect._empty: # noqa
338
338
  raise TypeError(
339
339
  f"Missing provider `{provider}` "
@@ -363,7 +363,7 @@ class Container:
363
363
  "registered with matching scopes."
364
364
  )
365
365
 
366
- def _detect_scope(self, obj: Callable[..., Any]) -> Scope | None:
366
+ def _detect_scope(self, obj: Callable[..., Any]) -> Optional[Scope]:
367
367
  """Detect the scope for a provider.
368
368
 
369
369
  Args:
@@ -372,7 +372,7 @@ class Container:
372
372
  The auto scope, or None if the auto scope cannot be detected.
373
373
  """
374
374
  has_transient, has_request, has_singleton = False, False, False
375
- for parameter in get_typed_parameters(obj):
375
+ for parameter in get_signature(obj).parameters.values():
376
376
  sub_provider = self._get_or_register_provider(parameter.annotation)
377
377
  if not has_transient and sub_provider.scope == "transient":
378
378
  has_transient = True
@@ -389,7 +389,7 @@ class Container:
389
389
  return None
390
390
 
391
391
  def register_module(
392
- self, module: Module | type[Module] | Callable[[Container], None]
392
+ self, module: Union[Module, Type[Module], Callable[[Container], None]]
393
393
  ) -> None:
394
394
  """Register a module as a callable, module type, or module instance.
395
395
 
@@ -633,11 +633,14 @@ class Container:
633
633
  def inject(self) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
634
634
 
635
635
  def inject(
636
- self, obj: Callable[P, T | Awaitable[T]] | None = None
637
- ) -> (
638
- Callable[[Callable[P, T | Awaitable[T]]], Callable[P, T | Awaitable[T]]]
639
- | Callable[P, T | Awaitable[T]]
640
- ):
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
+ ]:
641
644
  """Decorator to inject dependencies into a callable.
642
645
 
643
646
  Args:
@@ -649,8 +652,8 @@ class Container:
649
652
  """
650
653
 
651
654
  def decorator(
652
- obj: Callable[P, T | Awaitable[T]],
653
- ) -> Callable[P, T | Awaitable[T]]:
655
+ obj: Callable[P, Union[T, Awaitable[T]]],
656
+ ) -> Callable[P, Union[T, Awaitable[T]]]:
654
657
  injected_params = self._get_injected_params(obj)
655
658
 
656
659
  if inspect.iscoroutinefunction(obj):
@@ -691,9 +694,12 @@ class Container:
691
694
  def scan(
692
695
  self,
693
696
  /,
694
- packages: types.ModuleType | str | Iterable[types.ModuleType | str],
697
+ packages: Union[
698
+ Union[types.ModuleType, str],
699
+ Iterable[Union[types.ModuleType, str]],
700
+ ],
695
701
  *,
696
- tags: Iterable[str] | None = None,
702
+ tags: Optional[Iterable[str]] = None,
697
703
  ) -> None:
698
704
  """Scan packages or modules for decorated members and inject dependencies.
699
705
 
@@ -717,28 +723,32 @@ class Container:
717
723
  Raises:
718
724
  TypeError: If the provider return annotation is missing or invalid.
719
725
  """
720
- annotation = get_typed_return_annotation(obj)
726
+ annotation = get_signature(obj).return_annotation
721
727
 
722
- if annotation is None:
728
+ if annotation is inspect._empty: # noqa
723
729
  raise TypeError(
724
730
  f"Missing `{get_full_qualname(obj)}` provider return annotation."
725
731
  )
726
732
 
727
- origin = get_origin(annotation)
733
+ origin = get_origin(annotation) or annotation
734
+ args = get_args(annotation)
728
735
 
729
- if has_resource_origin(origin):
730
- args = get_args(annotation)
736
+ # Supported generic types
737
+ if origin in (list, dict, tuple, Annotated):
731
738
  if args:
732
- return args[0]
739
+ return annotation
733
740
  else:
734
741
  raise TypeError(
735
- f"Cannot use `{get_full_qualname(obj)}` resource type annotation "
742
+ f"Cannot use `{get_full_qualname(obj)}` generic type annotation "
736
743
  "without actual type."
737
744
  )
738
745
 
739
- return annotation
746
+ try:
747
+ return args[0]
748
+ except IndexError:
749
+ return annotation
740
750
 
741
- def _get_injected_params(self, obj: Callable[..., Any]) -> dict[str, Any]:
751
+ def _get_injected_params(self, obj: Callable[..., Any]) -> Dict[str, Any]:
742
752
  """Get the injected parameters of a callable object.
743
753
 
744
754
  Args:
@@ -749,8 +759,8 @@ class Container:
749
759
  of the injected parameters.
750
760
  """
751
761
  injected_params = {}
752
- for parameter in get_typed_parameters(obj):
753
- if not is_marker(parameter.default):
762
+ for parameter in get_signature(obj).parameters.values():
763
+ if not isinstance(parameter.default, Marker):
754
764
  continue
755
765
  try:
756
766
  self._validate_injected_parameter(obj, parameter)
@@ -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, TypeVar, cast
6
+ from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Type, 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:
100
+ for parameter in provider.parameters.values():
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:
120
+ for parameter in provider.parameters.values():
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, TypeVar
6
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type, TypeVar, Union
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: Module | type[Module] | Callable[[Container], None]
70
+ self, module: Union[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,6 +10,8 @@ from typing import (
10
10
  Any,
11
11
  Callable,
12
12
  Iterable,
13
+ List,
14
+ Optional,
13
15
  TypeVar,
14
16
  Union,
15
17
  cast,
@@ -19,8 +21,8 @@ from typing import (
19
21
 
20
22
  from typing_extensions import NamedTuple, ParamSpec
21
23
 
22
- from ._types import is_marker
23
- from ._utils import get_typed_parameters
24
+ from ._types import Marker
25
+ from ._utils import get_signature
24
26
 
25
27
  if TYPE_CHECKING:
26
28
  from ._container import Container
@@ -54,9 +56,9 @@ class Scanner:
54
56
  def scan(
55
57
  self,
56
58
  /,
57
- packages: ModuleType | str | Iterable[ModuleType | str],
59
+ packages: Union[Union[ModuleType, str], Iterable[Union[ModuleType, str]]],
58
60
  *,
59
- tags: Iterable[str] | None = None,
61
+ tags: Optional[Iterable[str]] = None,
60
62
  ) -> None:
61
63
  """Scan packages or modules for decorated members and inject dependencies.
62
64
 
@@ -66,10 +68,10 @@ class Scanner:
66
68
  tags: Optional list of tags to filter the scanned members. Only members
67
69
  with at least one matching tag will be scanned. Defaults to None.
68
70
  """
69
- dependencies: list[Dependency] = []
71
+ dependencies: List[Dependency] = []
70
72
 
71
73
  if isinstance(packages, Iterable) and not isinstance(packages, str):
72
- scan_packages: Iterable[ModuleType | str] = packages
74
+ scan_packages: Iterable[Union[ModuleType, str]] = packages
73
75
  else:
74
76
  scan_packages = cast(Iterable[Union[ModuleType, str]], [packages])
75
77
 
@@ -82,10 +84,10 @@ class Scanner:
82
84
 
83
85
  def _scan_package(
84
86
  self,
85
- package: ModuleType | str,
87
+ package: Union[ModuleType, str],
86
88
  *,
87
- tags: Iterable[str] | None = None,
88
- ) -> list[Dependency]:
89
+ tags: Optional[Iterable[str]] = None,
90
+ ) -> List[Dependency]:
89
91
  """Scan a package or module for decorated members.
90
92
 
91
93
  Args:
@@ -105,7 +107,7 @@ class Scanner:
105
107
  if not package_path:
106
108
  return self._scan_module(package, tags=tags)
107
109
 
108
- dependencies: list[Dependency] = []
110
+ dependencies: List[Dependency] = []
109
111
 
110
112
  for module_info in pkgutil.walk_packages(
111
113
  path=package_path, prefix=package.__name__ + "."
@@ -117,7 +119,7 @@ class Scanner:
117
119
 
118
120
  def _scan_module(
119
121
  self, module: ModuleType, *, tags: Iterable[str]
120
- ) -> list[Dependency]:
122
+ ) -> List[Dependency]:
121
123
  """Scan a module for decorated members.
122
124
 
123
125
  Args:
@@ -128,7 +130,7 @@ class Scanner:
128
130
  Returns:
129
131
  A list of scanned dependencies.
130
132
  """
131
- dependencies: list[Dependency] = []
133
+ dependencies: List[Dependency] = []
132
134
 
133
135
  for _, member in inspect.getmembers(module):
134
136
  if getattr(member, "__module__", None) != module.__name__ or not callable(
@@ -157,11 +159,11 @@ class Scanner:
157
159
 
158
160
  # Get by Marker
159
161
  if inspect.isclass(member):
160
- parameters = get_typed_parameters(member.__init__)
162
+ signature = get_signature(member.__init__)
161
163
  else:
162
- parameters = get_typed_parameters(member)
163
- for parameter in parameters:
164
- if is_marker(parameter.default):
164
+ signature = get_signature(member)
165
+ for parameter in signature.parameters.values():
166
+ if isinstance(parameter.default, Marker):
165
167
  dependencies.append(
166
168
  self._create_dependency(member=member, module=module)
167
169
  )
@@ -186,7 +188,7 @@ class Scanner:
186
188
 
187
189
  class InjectDecoratorArgs(NamedTuple):
188
190
  wrapped: bool
189
- tags: Iterable[str] | None
191
+ tags: Optional[Iterable[str]]
190
192
 
191
193
 
192
194
  @overload
@@ -195,14 +197,20 @@ def injectable(obj: Callable[P, T]) -> Callable[P, T]: ...
195
197
 
196
198
  @overload
197
199
  def injectable(
198
- *, tags: Iterable[str] | None = None
200
+ *, tags: Optional[Iterable[str]] = None
199
201
  ) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
200
202
 
201
203
 
202
204
  def injectable(
203
- obj: Callable[P, T] | None = None,
204
- tags: Iterable[str] | None = None,
205
- ) -> Callable[[Callable[P, T]], Callable[P, T]] | Callable[P, T]:
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
+ ]:
206
214
  """Decorator for marking a function or method as requiring dependency injection.
207
215
 
208
216
  Args:
@@ -1,13 +1,11 @@
1
- from __future__ import annotations
2
-
3
1
  import inspect
4
2
  from dataclasses import dataclass
5
3
  from functools import cached_property
6
4
  from typing import Any, Callable, Type, TypeVar, Union
7
5
 
8
- from typing_extensions import Annotated, Literal, Self, TypeAlias
6
+ from typing_extensions import Annotated, Literal, Mapping, TypeAlias
9
7
 
10
- from ._utils import get_full_qualname, get_typed_parameters
8
+ from ._utils import get_full_qualname, get_signature
11
9
 
12
10
  Scope = Literal["transient", "singleton", "request"]
13
11
 
@@ -21,14 +19,6 @@ class Marker:
21
19
 
22
20
  __slots__ = ()
23
21
 
24
- def __call__(self) -> Self:
25
- return self
26
-
27
-
28
- def is_marker(obj: Any) -> bool:
29
- """Checks if an object is a marker."""
30
- return isinstance(obj, Marker)
31
-
32
22
 
33
23
  @dataclass(frozen=True)
34
24
  class Provider:
@@ -60,13 +50,13 @@ class Provider:
60
50
  return get_full_qualname(self.obj)
61
51
 
62
52
  @cached_property
63
- def parameters(self) -> list[inspect.Parameter]:
53
+ def parameters(self) -> Mapping[str, inspect.Parameter]:
64
54
  """Returns the parameters of the provider as a mapping.
65
55
 
66
56
  Returns:
67
57
  The parameters of the provider.
68
58
  """
69
- return get_typed_parameters(self.obj)
59
+ return get_signature(self.obj).parameters
70
60
 
71
61
  @cached_property
72
62
  def is_class(self) -> bool: