anydi 0.25.0__py3-none-any.whl → 0.25.0a1__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/_container.py CHANGED
@@ -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
@@ -43,18 +48,12 @@ from ._logger import logger
43
48
  from ._module import Module, ModuleRegistry
44
49
  from ._scanner import Scanner
45
50
  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
- )
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,7 +759,7 @@ class Container:
749
759
  of the injected parameters.
750
760
  """
751
761
  injected_params = {}
752
- for parameter in get_typed_parameters(obj):
762
+ for parameter in get_signature(obj).parameters.values():
753
763
  if not is_marker(parameter.default):
754
764
  continue
755
765
  try:
anydi/_context.py CHANGED
@@ -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:
anydi/_module.py CHANGED
@@ -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
 
anydi/_scanner.py CHANGED
@@ -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,
@@ -20,7 +22,7 @@ from typing import (
20
22
  from typing_extensions import NamedTuple, ParamSpec
21
23
 
22
24
  from ._types import is_marker
23
- from ._utils import get_typed_parameters
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,10 +159,10 @@ 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
+ signature = get_signature(member)
165
+ for parameter in signature.parameters.values():
164
166
  if is_marker(parameter.default):
165
167
  dependencies.append(
166
168
  self._create_dependency(member=member, module=module)
@@ -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:
anydi/_types.py CHANGED
@@ -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, Self, 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
 
@@ -60,13 +58,13 @@ class Provider:
60
58
  return get_full_qualname(self.obj)
61
59
 
62
60
  @cached_property
63
- def parameters(self) -> list[inspect.Parameter]:
61
+ def parameters(self) -> Mapping[str, inspect.Parameter]:
64
62
  """Returns the parameters of the provider as a mapping.
65
63
 
66
64
  Returns:
67
65
  The parameters of the provider.
68
66
  """
69
- return get_typed_parameters(self.obj)
67
+ return get_signature(self.obj).parameters
70
68
 
71
69
  @cached_property
72
70
  def is_class(self) -> bool:
anydi/_utils.py CHANGED
@@ -1,12 +1,10 @@
1
1
  """Shared AnyDI utils module."""
2
2
 
3
- from __future__ import annotations
4
-
5
3
  import builtins
6
4
  import functools
7
5
  import inspect
8
6
  import sys
9
- from typing import Any, AsyncIterator, Callable, ForwardRef, Iterator, TypeVar, cast
7
+ from typing import Any, Callable, Dict, Type, TypeVar
10
8
 
11
9
  from typing_extensions import Annotated, ParamSpec, get_origin
12
10
 
@@ -16,23 +14,22 @@ except ImportError:
16
14
  anyio = None # type: ignore[assignment]
17
15
 
18
16
 
19
- if sys.version_info < (3, 9): # pragma: nocover
20
-
21
- def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
22
- return type_._evaluate(globalns, localns) # noqa
23
-
24
- else:
25
-
26
- def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
27
- return cast(Any, type_)._evaluate(globalns, localns, set()) # noqa
28
-
29
-
30
17
  T = TypeVar("T")
31
18
  P = ParamSpec("P")
32
19
 
33
20
 
34
21
  def get_full_qualname(obj: Any) -> str:
35
- """Get the fully qualified name of an object."""
22
+ """Get the fully qualified name of an object.
23
+
24
+ This function returns the fully qualified name of the given object,
25
+ which includes both the module name and the object's qualname.
26
+
27
+ Args:
28
+ obj: The object for which to retrieve the fully qualified name.
29
+
30
+ Returns:
31
+ The fully qualified name of the object.
32
+ """
36
33
  origin = get_origin(obj)
37
34
  if origin is Annotated:
38
35
  metadata = ", ".join(
@@ -56,55 +53,35 @@ def get_full_qualname(obj: Any) -> str:
56
53
  return f"{module_name}.{qualname}"
57
54
 
58
55
 
59
- def is_builtin_type(tp: type[Any]) -> bool:
60
- """Check if the given type is a built-in type."""
56
+ def is_builtin_type(tp: Type[Any]) -> bool:
57
+ """
58
+ Check if the given type is a built-in type.
59
+ Args:
60
+ tp (type): The type to check.
61
+ Returns:
62
+ bool: True if the type is a built-in type, False otherwise.
63
+ """
61
64
  return tp.__module__ == builtins.__name__
62
65
 
63
66
 
64
- def make_forwardref(annotation: str, globalns: dict[str, Any]) -> Any:
65
- """Create a forward reference from a string annotation."""
66
- forward_ref = ForwardRef(annotation)
67
- return evaluate_forwardref(forward_ref, globalns, globalns)
68
-
67
+ @functools.lru_cache(maxsize=None)
68
+ def get_signature(obj: Callable[..., Any]) -> inspect.Signature:
69
+ """Get the signature of a callable object.
69
70
 
70
- def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
71
- """Get the typed annotation of a parameter."""
72
- if isinstance(annotation, str):
73
- annotation = ForwardRef(annotation)
74
- annotation = evaluate_forwardref(annotation, globalns, globalns)
75
- return annotation
71
+ This function uses the `inspect.signature` function to retrieve the signature
72
+ of the given callable object. It applies an LRU cache decorator to improve
73
+ performance by caching the signatures of previously inspected objects.
76
74
 
75
+ Args:
76
+ obj: The callable object to inspect.
77
77
 
78
- def get_typed_return_annotation(obj: Callable[..., Any]) -> Any:
79
- """Get the typed return annotation of a callable object."""
80
- signature = inspect.signature(obj)
81
- annotation = signature.return_annotation
82
- if annotation is inspect.Signature.empty:
83
- return None
84
- globalns = getattr(obj, "__globals__", {})
85
- return get_typed_annotation(annotation, globalns)
86
-
87
-
88
- def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
89
- """Get the typed parameters of a callable object."""
90
- globalns = getattr(obj, "__globals__", {})
91
- return [
92
- parameter.replace(
93
- annotation=get_typed_annotation(parameter.annotation, globalns)
94
- )
95
- for name, parameter in inspect.signature(obj).parameters.items()
96
- ]
97
-
98
-
99
- _resource_origins = (
100
- get_origin(Iterator),
101
- get_origin(AsyncIterator),
102
- )
103
-
104
-
105
- def has_resource_origin(origin: Any) -> bool:
106
- """Check if the given origin is a resource origin."""
107
- return origin in _resource_origins
78
+ Returns:
79
+ The signature of the callable object.
80
+ """
81
+ signature_kwargs: Dict[str, Any] = {}
82
+ if sys.version_info >= (3, 10):
83
+ signature_kwargs["eval_str"] = True
84
+ return inspect.signature(obj, **signature_kwargs)
108
85
 
109
86
 
110
87
  async def run_async(
@@ -113,7 +90,19 @@ async def run_async(
113
90
  *args: P.args,
114
91
  **kwargs: P.kwargs,
115
92
  ) -> T:
116
- """Runs the given function asynchronously using the `anyio` library."""
93
+ """Runs the given function asynchronously using the `anyio` library.
94
+
95
+ Args:
96
+ func: The function to run asynchronously.
97
+ args: The positional arguments to pass to the function.
98
+ kwargs: The keyword arguments to pass to the function.
99
+
100
+ Returns:
101
+ The result of the function.
102
+
103
+ Raises:
104
+ ImportError: If the `anyio` library is not installed.
105
+ """
117
106
  if not anyio:
118
107
  raise ImportError(
119
108
  "`anyio` library is not currently installed. Please make sure to install "
@@ -1,9 +1,3 @@
1
1
  from ._container import container
2
- from ._utils import inject_urlpatterns, register_components, register_settings
3
2
 
4
- __all__ = [
5
- "container",
6
- "register_components",
7
- "register_settings",
8
- "inject_urlpatterns",
9
- ]
3
+ __all__ = ["container"]
@@ -1,103 +1,6 @@
1
- from __future__ import annotations
2
-
3
1
  from collections.abc import Iterator
4
- from functools import wraps
5
- from typing import Any
6
-
7
- from django.conf import settings
8
- from django.core.cache import BaseCache, caches
9
- from django.db import connections
10
- from django.db.backends.base.base import BaseDatabaseWrapper
11
- from django.urls import URLPattern, URLResolver, get_resolver
12
- from typing_extensions import Annotated, get_origin
13
-
14
- import anydi
15
-
16
-
17
- def register_settings(
18
- container: anydi.Container, prefix: str = "django.conf.setting."
19
- ) -> None:
20
- """Register Django settings into the container."""
21
-
22
- def _get_setting_value(value: Any) -> Any:
23
- return lambda: value
24
-
25
- for setting_name in dir(settings):
26
- setting_value = getattr(settings, setting_name)
27
- if not setting_name.isupper():
28
- continue
29
-
30
- container.register(
31
- Annotated[Any, f"{prefix}{setting_name}"],
32
- _get_setting_value(setting_value),
33
- scope="singleton",
34
- )
35
-
36
- def _resolve(resolve: Any) -> Any:
37
- @wraps(resolve)
38
- def wrapper(interface: Any) -> Any:
39
- return resolve(_aware_settings(interface, prefix))
40
-
41
- return wrapper
42
-
43
- def _aresolve(resolve: Any) -> Any:
44
- @wraps(resolve)
45
- async def wrapper(interface: Any) -> Any:
46
- return await resolve(_aware_settings(interface, prefix))
47
-
48
- return wrapper
49
-
50
- # Patch resolvers
51
- container.resolve = _resolve(container.resolve) # type: ignore[method-assign] # noqa
52
- container.aresolve = _aresolve(container.aresolve) # type: ignore[method-assign] # noqa
53
-
54
-
55
- def _aware_settings(interface: Any, prefix: str) -> Any:
56
- origin = get_origin(interface)
57
- if origin is not Annotated:
58
- return interface # pragma: no cover
59
- named = interface.__metadata__[-1]
60
-
61
- if isinstance(named, str) and named.startswith(prefix):
62
- _, setting_name = named.rsplit(prefix, maxsplit=1)
63
- return Annotated[Any, f"{prefix}{setting_name}"]
64
- return interface
65
-
66
-
67
- def register_components(container: anydi.Container) -> None:
68
- """Register Django components into the container."""
69
-
70
- # Register caches
71
- def _get_cache(cache_name: str) -> Any:
72
- return lambda: caches[cache_name]
73
-
74
- for cache_name in caches:
75
- container.register(
76
- Annotated[BaseCache, cache_name],
77
- _get_cache(cache_name),
78
- scope="singleton",
79
- )
80
-
81
- # Register database connections
82
- def _get_connection(alias: str) -> Any:
83
- return lambda: connections[alias]
84
-
85
- for alias in connections:
86
- container.register(
87
- Annotated[BaseDatabaseWrapper, alias],
88
- _get_connection(alias),
89
- scope="singleton",
90
- )
91
-
92
2
 
93
- def inject_urlpatterns(container: anydi.Container, *, urlconf: str) -> None:
94
- """Auto-inject the container into views."""
95
- resolver = get_resolver(urlconf)
96
- for pattern in iter_urlpatterns(resolver.url_patterns):
97
- # Skip django-ninja views
98
- if pattern.lookup_str.startswith("ninja."):
99
- continue # pragma: no cover
100
- pattern.callback = container.inject(pattern.callback)
3
+ from django.urls import URLPattern, URLResolver
101
4
 
102
5
 
103
6
  def iter_urlpatterns(
anydi/ext/django/apps.py CHANGED
@@ -1,18 +1,22 @@
1
- from __future__ import annotations
2
-
3
1
  import logging
4
2
  import types
5
- from typing import Callable, cast
3
+ from asyncio import get_running_loop
4
+ from functools import wraps
5
+ from typing import Any, Callable, cast
6
6
 
7
7
  from django.apps import AppConfig
8
8
  from django.conf import settings
9
+ from django.core.cache import BaseCache, caches
9
10
  from django.core.exceptions import ImproperlyConfigured
11
+ from django.db import connections
12
+ from django.db.backends.base.base import BaseDatabaseWrapper
13
+ from django.urls import get_resolver
10
14
  from django.utils.module_loading import import_string
15
+ from typing_extensions import Annotated, get_origin
11
16
 
12
17
  import anydi
13
18
 
14
- from ._settings import get_settings
15
- from ._utils import inject_urlpatterns, register_components, register_settings
19
+ from ._utils import iter_urlpatterns
16
20
 
17
21
  logger = logging.getLogger(__name__)
18
22
 
@@ -21,44 +25,39 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
21
25
  name = "anydi.ext.django"
22
26
  label = "anydi_django"
23
27
 
28
+ # Prefix for Django settings
29
+ settings_prefix = "django.conf.settings."
30
+
24
31
  def __init__(self, app_name: str, app_module: types.ModuleType | None) -> None:
25
32
  super().__init__(app_name, app_module)
26
- self.settings = get_settings()
27
33
  # Create a container
28
- container_factory_path = self.settings["CONTAINER_FACTORY"]
29
- if container_factory_path:
34
+ container_getter_path = getattr(settings, "ANYDI_CONTAINER_GETTER", None)
35
+ if container_getter_path:
30
36
  try:
31
- container_factory = cast(
32
- Callable[[], anydi.Container], import_string(container_factory_path)
37
+ container_getter = cast(
38
+ Callable[[], anydi.Container], import_string(container_getter_path)
33
39
  )
34
40
  except ImportError as exc:
35
41
  raise ImproperlyConfigured(
36
- f"Cannot import container factory '{container_factory_path}'."
42
+ f"Cannot import container getter '{container_getter_path}'."
37
43
  ) from exc
38
- self.container = container_factory()
44
+ self.container = container_getter()
39
45
  else:
40
46
  self.container = anydi.Container(
41
- strict=self.settings["STRICT_MODE"],
47
+ strict=getattr(settings, "ANYDI_STRICT_MODE", False),
42
48
  )
43
49
 
44
50
  def ready(self) -> None: # noqa: C901
45
51
  # Register Django settings
46
- if self.settings["REGISTER_SETTINGS"]:
47
- register_settings(
48
- self.container,
49
- prefix=getattr(
50
- settings,
51
- "ANYDI_SETTINGS_PREFIX",
52
- "django.conf.settings.",
53
- ),
54
- )
52
+ if getattr(settings, "ANYDI_REGISTER_SETTINGS", False):
53
+ self.register_settings()
55
54
 
56
55
  # Register Django components
57
- if self.settings["REGISTER_COMPONENTS"]:
58
- register_components(self.container)
56
+ if getattr(settings, "ANYDI_REGISTER_COMPONENTS", False):
57
+ self.register_components()
59
58
 
60
59
  # Register modules
61
- for module_path in self.settings["MODULES"]:
60
+ for module_path in getattr(settings, "ANYDI_MODULES", []):
62
61
  try:
63
62
  module_cls = import_string(module_path)
64
63
  except ImportError as exc:
@@ -68,15 +67,111 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
68
67
  self.container.register_module(module_cls)
69
68
 
70
69
  # Patching the django-ninja framework if it installed
71
- if self.settings["PATCH_NINJA"]:
72
- from .ninja import patch_ninja
73
-
74
- patch_ninja()
70
+ if getattr(settings, "ANYDI_PATCH_NINJA", False):
71
+ self.patch_ninja()
75
72
 
76
73
  # Auto-injecting the container into views
77
- if urlconf := self.settings["INJECT_URLCONF"]:
78
- inject_urlpatterns(self.container, urlconf=urlconf)
74
+ if urlconf := getattr(settings, "ANYDI_AUTO_INJECT_URLCONF", None):
75
+ self.auto_inject_urlconf(urlconf)
79
76
 
80
77
  # Scan packages
81
- for scan_package in self.settings["SCAN_PACKAGES"]:
78
+ for scan_package in getattr(settings, "ANYDI_SCAN_PACKAGES", []):
82
79
  self.container.scan(scan_package)
80
+
81
+ # Start the container
82
+ if getattr(settings, "ANYDI_START_CONTAINER", False):
83
+ try:
84
+ get_running_loop()
85
+ except RuntimeError:
86
+ logger.warning(
87
+ "Starting the container is only supported in an async context."
88
+ )
89
+ else:
90
+ self.container.start()
91
+
92
+ def register_settings(self) -> None: # noqa: C901
93
+ """Register Django settings into the container."""
94
+
95
+ def _get_setting_value(value: Any) -> Any:
96
+ return lambda: value
97
+
98
+ for setting_name in dir(settings):
99
+ setting_value = getattr(settings, setting_name)
100
+ if not setting_name.isupper():
101
+ continue
102
+
103
+ self.container.register(
104
+ Annotated[Any, f"{self.settings_prefix}{setting_name}"],
105
+ _get_setting_value(setting_value),
106
+ scope="singleton",
107
+ )
108
+
109
+ def _aware_settings(interface: Any) -> Any:
110
+ origin = get_origin(interface)
111
+ if origin is not Annotated:
112
+ return interface # pragma: no cover
113
+ named = interface.__metadata__[-1]
114
+
115
+ if isinstance(named, str) and named.startswith(self.settings_prefix):
116
+ _, setting_name = named.rsplit(self.settings_prefix, maxsplit=1)
117
+ return Annotated[Any, f"{self.settings_prefix}{setting_name}"]
118
+ return interface
119
+
120
+ def _resolve(resolve: Any) -> Any:
121
+ @wraps(resolve)
122
+ def wrapper(interface: Any) -> Any:
123
+ return resolve(_aware_settings(interface))
124
+
125
+ return wrapper
126
+
127
+ def _aresolve(resolve: Any) -> Any:
128
+ @wraps(resolve)
129
+ async def wrapper(interface: Any) -> Any:
130
+ return await resolve(_aware_settings(interface))
131
+
132
+ return wrapper
133
+
134
+ # Patch resolvers
135
+ self.container.resolve = _resolve(self.container.resolve) # type: ignore[method-assign] # noqa
136
+ self.container.aresolve = _aresolve(self.container.aresolve) # type: ignore[method-assign] # noqa
137
+
138
+ def register_components(self) -> None:
139
+ """Register Django components into the container."""
140
+
141
+ # Register caches
142
+ def _get_cache(cache_name: str) -> Any:
143
+ return lambda: caches[cache_name]
144
+
145
+ for cache_name in caches:
146
+ self.container.register(
147
+ Annotated[BaseCache, cache_name],
148
+ _get_cache(cache_name),
149
+ scope="singleton",
150
+ )
151
+
152
+ # Register database connections
153
+ def _get_connection(alias: str) -> Any:
154
+ return lambda: connections[alias]
155
+
156
+ for alias in connections:
157
+ self.container.register(
158
+ Annotated[BaseDatabaseWrapper, alias],
159
+ _get_connection(alias),
160
+ scope="singleton",
161
+ )
162
+
163
+ def auto_inject_urlconf(self, urlconf: str) -> None:
164
+ """Auto-inject the container into views."""
165
+ resolver = get_resolver(urlconf)
166
+ for pattern in iter_urlpatterns(resolver.url_patterns):
167
+ # Skip django-ninja views
168
+ if pattern.lookup_str.startswith("ninja."):
169
+ continue # pragma: no cover
170
+ pattern.callback = self.container.inject(pattern.callback)
171
+
172
+ @staticmethod
173
+ def patch_ninja() -> None:
174
+ """Patch the django-ninja framework."""
175
+ from .ninja import patch
176
+
177
+ patch()
@@ -10,7 +10,7 @@ from ._operation import AsyncOperation, Operation
10
10
  from ._signature import ViewSignature
11
11
 
12
12
 
13
- def patch_ninja() -> None:
13
+ def patch() -> None:
14
14
  operation.ViewSignature = ViewSignature # type: ignore[attr-defined]
15
15
  operation.Operation = Operation # type: ignore[misc]
16
16
  operation.AsyncOperation = AsyncOperation # type: ignore[misc]
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  from typing import Any
4
2
 
5
3
  from django.http import HttpRequest, HttpResponseBase
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  import inspect
4
2
  from collections.abc import Callable
5
3
  from typing import Any
anydi/ext/fastapi.py CHANGED
@@ -13,7 +13,7 @@ from starlette.requests import Request
13
13
  from typing_extensions import Annotated, get_args, get_origin
14
14
 
15
15
  from anydi import Container
16
- from anydi._utils import get_full_qualname, get_typed_parameters
16
+ from anydi._utils import get_full_qualname, get_signature
17
17
 
18
18
  from .starlette.middleware import RequestScopedMiddleware
19
19
 
@@ -47,7 +47,7 @@ def install(app: FastAPI, container: Container) -> None:
47
47
  call, *params = dependant.cache_key
48
48
  if not call:
49
49
  continue # pragma: no cover
50
- for parameter in get_typed_parameters(call):
50
+ for parameter in get_signature(call).parameters.values():
51
51
  _patch_route_parameter(call, parameter, container)
52
52
 
53
53
 
@@ -1,7 +1,5 @@
1
- from __future__ import annotations
2
-
3
1
  import inspect
4
- from typing import Any, Callable, Iterator, cast
2
+ from typing import Any, Callable, Iterator, List, Tuple, cast
5
3
 
6
4
  import pytest
7
5
 
@@ -51,8 +49,8 @@ def _anydi_should_inject(request: pytest.FixtureRequest) -> bool:
51
49
 
52
50
 
53
51
  @pytest.fixture(scope="session")
54
- def _anydi_unresolved() -> Iterator[list[Any]]:
55
- unresolved: list[Any] = []
52
+ def _anydi_unresolved() -> Iterator[List[Any]]:
53
+ unresolved: List[Any] = []
56
54
  yield unresolved
57
55
  unresolved.clear()
58
56
 
@@ -60,9 +58,9 @@ def _anydi_unresolved() -> Iterator[list[Any]]:
60
58
  @pytest.fixture
61
59
  def _anydi_injected_parameter_iterator(
62
60
  request: pytest.FixtureRequest,
63
- _anydi_unresolved: list[str],
64
- ) -> Callable[[], Iterator[tuple[str, Any]]]:
65
- def _iterator() -> Iterator[tuple[str, inspect.Parameter]]:
61
+ _anydi_unresolved: List[str],
62
+ ) -> Callable[[], Iterator[Tuple[str, Any]]]:
63
+ def _iterator() -> Iterator[Tuple[str, inspect.Parameter]]:
66
64
  for name, parameter in inspect.signature(request.function).parameters.items():
67
65
  if (
68
66
  ((interface := parameter.annotation) is parameter.empty)
@@ -79,8 +77,8 @@ def _anydi_injected_parameter_iterator(
79
77
  def _anydi_inject(
80
78
  request: pytest.FixtureRequest,
81
79
  _anydi_should_inject: bool,
82
- _anydi_injected_parameter_iterator: Callable[[], Iterator[tuple[str, Any]]],
83
- _anydi_unresolved: list[str],
80
+ _anydi_injected_parameter_iterator: Callable[[], Iterator[Tuple[str, Any]]],
81
+ _anydi_unresolved: List[str],
84
82
  ) -> None:
85
83
  """Inject dependencies into the test function."""
86
84
 
@@ -109,8 +107,8 @@ def _anydi_inject(
109
107
  async def _anydi_ainject(
110
108
  request: pytest.FixtureRequest,
111
109
  _anydi_should_inject: bool,
112
- _anydi_injected_parameter_iterator: Callable[[], Iterator[tuple[str, Any]]],
113
- _anydi_unresolved: list[str],
110
+ _anydi_injected_parameter_iterator: Callable[[], Iterator[Tuple[str, Any]]],
111
+ _anydi_unresolved: List[str],
114
112
  ) -> None:
115
113
  """Inject dependencies into the test function."""
116
114
  if not inspect.iscoroutinefunction(request.function) or not _anydi_should_inject:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anydi
3
- Version: 0.25.0
3
+ Version: 0.25.0a1
4
4
  Summary: Dependency Injection library
5
5
  Home-page: https://github.com/antonrh/anydi
6
6
  License: MIT
@@ -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
-
@@ -0,0 +1,26 @@
1
+ anydi/__init__.py,sha256=aeaBp5vq09sG-e9sqqs9qpUtUIDNfOdFPrlAfE5Ku9E,584
2
+ anydi/_container.py,sha256=LeGrujx8zMzBuR8PD7qNqVJmmLB-frAG74gihX0lxNc,28341
3
+ anydi/_context.py,sha256=btGJzvTMkj5v95rAw6kjOclISKcSugC4wzrHlWlCk_I,10258
4
+ anydi/_logger.py,sha256=UpubJUnW83kffFxkhUlObm2DmZX1Pjqoz9YFKS-JOPg,52
5
+ anydi/_module.py,sha256=1fBo9-RWxo7TeyP0Y2uJokT-NXP2pjik6CXNoeo3l-8,3712
6
+ anydi/_scanner.py,sha256=9S3XbNVFAnHClT3FBo2CWeDBzCLG58i93a1ZJgaMNIo,6775
7
+ anydi/_types.py,sha256=TfqJ7TfudOzGMG4_OliAMMJplu42mXZcZmgmtK2oz14,3412
8
+ anydi/_utils.py,sha256=haRwC6YTTFf6CNTfwsRQ4NZsNByoUkEmE4-rh1-VYLs,3152
9
+ anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ anydi/ext/django/__init__.py,sha256=wsFBQP2j76Ui_0SJlN_8LKhZvEEdxA-w_yoyRAcaM58,59
11
+ anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
12
+ anydi/ext/django/_utils.py,sha256=C-1UabpwgCHUryAT-Pv8vU5QFfBahQlqhjecvihjtgY,430
13
+ anydi/ext/django/apps.py,sha256=qjzKqYIB00qjsSv2RQNVnFMi0lCljmaGtMIy3JMAE0U,6326
14
+ anydi/ext/django/ninja/__init__.py,sha256=FckjVqzXTzlBllFg2PZ62wi432-ydZFyWq6ZkF2LJ9I,540
15
+ anydi/ext/django/ninja/_operation.py,sha256=A_RbMbJyVaafoeBhZpmPQ96l1RQFLA1FchkJC_uRKnM,2660
16
+ anydi/ext/django/ninja/_signature.py,sha256=kdPU0QL3_oloWi43ehi2QJVn_WaRm1klZ6D7sA-SQYo,2177
17
+ anydi/ext/fastapi.py,sha256=zKuo7nNpcMXppUsime5LLKTkAiNrlEtcLRFK4FRur0c,5311
18
+ anydi/ext/pytest_plugin.py,sha256=tmncz2IbIR7FGlgmAtcf00yDKg9SQP9lCFnJBmtHx3A,3976
19
+ anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ anydi/ext/starlette/middleware.py,sha256=Ni0BQaPjs_Ha6zcLZYYJ3-XkslTCnL9aCSa06rnRDMI,1139
21
+ anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ anydi-0.25.0a1.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
23
+ anydi-0.25.0a1.dist-info/METADATA,sha256=WvjurCb1o4pBgxTvNh-7KY3-6qocjK0euML7cVagy2E,4373
24
+ anydi-0.25.0a1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
25
+ anydi-0.25.0a1.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
26
+ anydi-0.25.0a1.dist-info/RECORD,,
@@ -1,39 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Sequence
4
-
5
- from django.conf import settings
6
- from typing_extensions import TypedDict
7
-
8
-
9
- class Settings(TypedDict):
10
- CONTAINER_FACTORY: str | None
11
- STRICT_MODE: bool
12
- REGISTER_SETTINGS: bool
13
- REGISTER_COMPONENTS: bool
14
- INJECT_URLCONF: str | None
15
- MODULES: Sequence[str]
16
- SCAN_PACKAGES: Sequence[str]
17
- PATCH_NINJA: bool
18
-
19
-
20
- DEFAULTS = Settings(
21
- CONTAINER_FACTORY=None,
22
- STRICT_MODE=False,
23
- REGISTER_SETTINGS=False,
24
- REGISTER_COMPONENTS=False,
25
- MODULES=[],
26
- PATCH_NINJA=False,
27
- INJECT_URLCONF=None,
28
- SCAN_PACKAGES=[],
29
- )
30
-
31
-
32
- def get_settings() -> Settings:
33
- """Get the AnyDI settings from the Django settings."""
34
- return Settings(
35
- **{
36
- **DEFAULTS,
37
- **getattr(settings, "ANYDI", {}),
38
- }
39
- )
@@ -1,26 +0,0 @@
1
- from typing import Callable
2
-
3
- from asgiref.sync import iscoroutinefunction
4
- from django.http import HttpRequest, HttpResponse
5
- from django.utils.decorators import sync_and_async_middleware
6
-
7
- from ._container import container
8
-
9
-
10
- @sync_and_async_middleware # type: ignore[misc]
11
- def request_scoped_middleware(
12
- get_response: Callable[[HttpRequest], HttpResponse],
13
- ) -> Callable[[HttpRequest], HttpResponse]:
14
- if iscoroutinefunction(get_response):
15
-
16
- async def async_middleware(request: HttpRequest) -> HttpResponse:
17
- async with container.arequest_context():
18
- return await get_response(request)
19
-
20
- return async_middleware
21
-
22
- def middleware(request: HttpRequest) -> HttpResponse:
23
- with container.request_context():
24
- return get_response(request)
25
-
26
- return middleware
@@ -1,28 +0,0 @@
1
- anydi/__init__.py,sha256=aeaBp5vq09sG-e9sqqs9qpUtUIDNfOdFPrlAfE5Ku9E,584
2
- anydi/_container.py,sha256=rZ0HgWFC7jJuZo7iLjMYnTm4utWBMOeiaPThz8a5sbY,27996
3
- anydi/_context.py,sha256=k956mFE_pfPdU0fxOJ8YRHBZx7sU_ln8fheYNofbmSs,10215
4
- anydi/_logger.py,sha256=UpubJUnW83kffFxkhUlObm2DmZX1Pjqoz9YFKS-JOPg,52
5
- anydi/_module.py,sha256=E1TfLud_Af-MPB83PxIzHVA1jlDW2FGaRP_il1a6y3Y,3675
6
- anydi/_scanner.py,sha256=cyEk-K2Q8ssZStq8GrxMeEcCuAZMw-RXrjlgWEevKCs,6667
7
- anydi/_types.py,sha256=vQTrFjsYhlMxfo1nOFem05x2QUJMQkVh4ZaC7W0XZJY,3434
8
- anydi/_utils.py,sha256=XHVNkd-__SKlWlyeGE2e1Yi-DBr4DPWzZOIVbTrQyMI,3692
9
- anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- anydi/ext/django/__init__.py,sha256=QI1IABCVgSDTUoh7M9WMECKXwB3xvh04HfQ9TOWw1Mk,223
11
- anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
12
- anydi/ext/django/_settings.py,sha256=uS2l3oaSjf2pxAHIAZKpB6bTwqnn8vaNPnlJssL-x_E,828
13
- anydi/ext/django/_utils.py,sha256=1nB2FPTlchrX5z_VALMIeSYj_bmaFTKTEu6uyYcd7Hk,3491
14
- anydi/ext/django/apps.py,sha256=hJuvCZVyaROO-hl46fle6O0bv9tX4dX_M8SJKjsvuT4,2761
15
- anydi/ext/django/middleware.py,sha256=iVHWtE829khMY-BXbNNt0g2FrIApKprna7dCG9ObEis,823
16
- anydi/ext/django/ninja/__init__.py,sha256=kW3grUgWp_nkWSG_-39ADHMrZLGNcj9TsJ9OW8iWWrk,546
17
- anydi/ext/django/ninja/_operation.py,sha256=wSWa7D73XTVlOibmOciv2l6JHPe1ERZcXrqI8W-oO2w,2696
18
- anydi/ext/django/ninja/_signature.py,sha256=xDKIkQ58WaiK4UHdbqUx0mb7vNM_fqES4tZzBUTauws,2213
19
- anydi/ext/fastapi.py,sha256=kVUKVKtqCx1Nfnm1oh2BMyB0G7qQKPw6OGfxFlqUqtc,5305
20
- anydi/ext/pytest_plugin.py,sha256=vtjQCwQ0_saG8qhYAYn2wQzXVrXfwXOEhJlTjGqtXA8,3999
21
- anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- anydi/ext/starlette/middleware.py,sha256=Ni0BQaPjs_Ha6zcLZYYJ3-XkslTCnL9aCSa06rnRDMI,1139
23
- anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- anydi-0.25.0.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
25
- anydi-0.25.0.dist-info/METADATA,sha256=rVRV-iLMPwVHEJTtXvA1LaH_fZNUSGYoDc1bzDYqAi8,5160
26
- anydi-0.25.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
27
- anydi-0.25.0.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
28
- anydi-0.25.0.dist-info/RECORD,,