anydi 0.39.3__py3-none-any.whl → 0.41.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anydi/_container.py +29 -24
- anydi/_context.py +10 -10
- anydi/_types.py +3 -3
- anydi/_utils.py +1 -1
- anydi/ext/django/ninja/_signature.py +3 -2
- anydi/ext/faststream.py +2 -2
- anydi/ext/pytest_plugin.py +6 -2
- {anydi-0.39.3.dist-info → anydi-0.41.0.dist-info}/METADATA +2 -2
- {anydi-0.39.3.dist-info → anydi-0.41.0.dist-info}/RECORD +12 -12
- {anydi-0.39.3.dist-info → anydi-0.41.0.dist-info}/WHEEL +0 -0
- {anydi-0.39.3.dist-info → anydi-0.41.0.dist-info}/entry_points.txt +0 -0
- {anydi-0.39.3.dist-info → anydi-0.41.0.dist-info}/licenses/LICENSE +0 -0
anydi/_container.py
CHANGED
|
@@ -14,7 +14,7 @@ from collections import defaultdict
|
|
|
14
14
|
from collections.abc import AsyncIterator, Iterable, Iterator, Sequence
|
|
15
15
|
from contextvars import ContextVar
|
|
16
16
|
from types import ModuleType
|
|
17
|
-
from typing import Annotated, Any, Callable, TypeVar,
|
|
17
|
+
from typing import Annotated, Any, Callable, TypeVar, cast, overload
|
|
18
18
|
|
|
19
19
|
from typing_extensions import Concatenate, ParamSpec, Self, final, get_args, get_origin
|
|
20
20
|
|
|
@@ -94,18 +94,18 @@ class Container:
|
|
|
94
94
|
testing: bool = False,
|
|
95
95
|
logger: logging.Logger | None = None,
|
|
96
96
|
) -> None:
|
|
97
|
-
self._providers: dict[
|
|
97
|
+
self._providers: dict[Any, Provider] = {}
|
|
98
98
|
self._strict = strict
|
|
99
|
-
self._default_scope = default_scope
|
|
99
|
+
self._default_scope: Scope = default_scope
|
|
100
100
|
self._testing = testing
|
|
101
101
|
self._logger = logger or logging.getLogger(__name__)
|
|
102
|
-
self._resources: dict[str, list[
|
|
102
|
+
self._resources: dict[str, list[Any]] = defaultdict(list)
|
|
103
103
|
self._singleton_context = InstanceContext()
|
|
104
104
|
self._request_context_var: ContextVar[InstanceContext | None] = ContextVar(
|
|
105
105
|
"request_context", default=None
|
|
106
106
|
)
|
|
107
|
-
self._override_instances: dict[
|
|
108
|
-
self._unresolved_interfaces: set[
|
|
107
|
+
self._override_instances: dict[Any, Any] = {}
|
|
108
|
+
self._unresolved_interfaces: set[Any] = set()
|
|
109
109
|
self._inject_cache: dict[Callable[..., Any], Callable[..., Any]] = {}
|
|
110
110
|
|
|
111
111
|
# Register providers
|
|
@@ -364,8 +364,8 @@ class Container:
|
|
|
364
364
|
)
|
|
365
365
|
|
|
366
366
|
unresolved_parameter = None
|
|
367
|
-
parameters = []
|
|
368
|
-
scopes = {}
|
|
367
|
+
parameters: list[inspect.Parameter] = []
|
|
368
|
+
scopes: dict[Scope, Provider] = {}
|
|
369
369
|
|
|
370
370
|
for parameter in signature.parameters.values():
|
|
371
371
|
if parameter.annotation is inspect.Parameter.empty:
|
|
@@ -490,7 +490,10 @@ class Container:
|
|
|
490
490
|
call = interface
|
|
491
491
|
if inspect.isclass(call) and not is_builtin_type(call):
|
|
492
492
|
# Try to get defined scope
|
|
493
|
-
|
|
493
|
+
if hasattr(call, "__scope__"):
|
|
494
|
+
scope = getattr(call, "__scope__")
|
|
495
|
+
else:
|
|
496
|
+
scope = parent_scope
|
|
494
497
|
return self._register_provider(call, scope, interface, **defaults)
|
|
495
498
|
raise
|
|
496
499
|
|
|
@@ -510,9 +513,11 @@ class Container:
|
|
|
510
513
|
def _parameter_has_default(
|
|
511
514
|
self, parameter: inspect.Parameter, /, **defaults: Any
|
|
512
515
|
) -> bool:
|
|
513
|
-
|
|
514
|
-
|
|
516
|
+
has_default_in_kwargs = parameter.name in defaults if defaults else False
|
|
517
|
+
has_non_strict_default = not self.strict and (
|
|
518
|
+
parameter.default is not inspect.Parameter.empty
|
|
515
519
|
)
|
|
520
|
+
return has_default_in_kwargs or has_non_strict_default
|
|
516
521
|
|
|
517
522
|
############################
|
|
518
523
|
# Instance Methods
|
|
@@ -522,7 +527,7 @@ class Container:
|
|
|
522
527
|
def resolve(self, interface: type[T]) -> T: ...
|
|
523
528
|
|
|
524
529
|
@overload
|
|
525
|
-
def resolve(self, interface: T) -> T: ...
|
|
530
|
+
def resolve(self, interface: T) -> T: ... # type: ignore
|
|
526
531
|
|
|
527
532
|
def resolve(self, interface: type[T]) -> T:
|
|
528
533
|
"""Resolve an instance by interface."""
|
|
@@ -942,7 +947,7 @@ class Container:
|
|
|
942
947
|
|
|
943
948
|
def _get_injected_params(self, call: Callable[..., Any]) -> dict[str, Any]:
|
|
944
949
|
"""Get the injected parameters of a callable object."""
|
|
945
|
-
injected_params = {}
|
|
950
|
+
injected_params: dict[str, Any] = {}
|
|
946
951
|
for parameter in get_typed_parameters(call):
|
|
947
952
|
if not is_marker(parameter.default):
|
|
948
953
|
continue
|
|
@@ -1007,6 +1012,10 @@ class Container:
|
|
|
1007
1012
|
scope=decorator_args.scope,
|
|
1008
1013
|
override=decorator_args.override,
|
|
1009
1014
|
)(obj)
|
|
1015
|
+
else:
|
|
1016
|
+
raise TypeError(
|
|
1017
|
+
"The module must be a callable, a module type, or a module instance."
|
|
1018
|
+
)
|
|
1010
1019
|
|
|
1011
1020
|
############################
|
|
1012
1021
|
# Scanner Methods
|
|
@@ -1022,10 +1031,10 @@ class Container:
|
|
|
1022
1031
|
"""Scan packages or modules for decorated members and inject dependencies."""
|
|
1023
1032
|
dependencies: list[ScannedDependency] = []
|
|
1024
1033
|
|
|
1025
|
-
if isinstance(packages,
|
|
1026
|
-
scan_packages: Iterable[ModuleType | str] = packages
|
|
1034
|
+
if isinstance(packages, ModuleType | str):
|
|
1035
|
+
scan_packages: Iterable[ModuleType | str] = [packages]
|
|
1027
1036
|
else:
|
|
1028
|
-
scan_packages =
|
|
1037
|
+
scan_packages = packages
|
|
1029
1038
|
|
|
1030
1039
|
for package in scan_packages:
|
|
1031
1040
|
dependencies.extend(self._scan_package(package, tags=tags))
|
|
@@ -1117,19 +1126,19 @@ class Container:
|
|
|
1117
1126
|
|
|
1118
1127
|
def transient(target: T) -> T:
|
|
1119
1128
|
"""Decorator for marking a class as transient scope."""
|
|
1120
|
-
|
|
1129
|
+
target.__scope__ = "transient"
|
|
1121
1130
|
return target
|
|
1122
1131
|
|
|
1123
1132
|
|
|
1124
1133
|
def request(target: T) -> T:
|
|
1125
1134
|
"""Decorator for marking a class as request scope."""
|
|
1126
|
-
|
|
1135
|
+
target.__scope__ = "request"
|
|
1127
1136
|
return target
|
|
1128
1137
|
|
|
1129
1138
|
|
|
1130
1139
|
def singleton(target: T) -> T:
|
|
1131
1140
|
"""Decorator for marking a class as singleton scope."""
|
|
1132
|
-
|
|
1141
|
+
target.__scope__ = "singleton"
|
|
1133
1142
|
return target
|
|
1134
1143
|
|
|
1135
1144
|
|
|
@@ -1141,11 +1150,7 @@ def provider(
|
|
|
1141
1150
|
def decorator(
|
|
1142
1151
|
target: Callable[Concatenate[M, P], T],
|
|
1143
1152
|
) -> Callable[Concatenate[M, P], T]:
|
|
1144
|
-
|
|
1145
|
-
target,
|
|
1146
|
-
"__provider__",
|
|
1147
|
-
ProviderDecoratorArgs(scope=scope, override=override),
|
|
1148
|
-
)
|
|
1153
|
+
target.__provider__ = ProviderDecoratorArgs(scope=scope, override=override) # type: ignore
|
|
1149
1154
|
return target
|
|
1150
1155
|
|
|
1151
1156
|
return decorator
|
anydi/_context.py
CHANGED
|
@@ -16,17 +16,17 @@ class InstanceContext:
|
|
|
16
16
|
__slots__ = ("_instances", "_stack", "_async_stack", "_lock", "_async_lock")
|
|
17
17
|
|
|
18
18
|
def __init__(self) -> None:
|
|
19
|
-
self._instances: dict[
|
|
19
|
+
self._instances: dict[Any, Any] = {}
|
|
20
20
|
self._stack = contextlib.ExitStack()
|
|
21
21
|
self._async_stack = contextlib.AsyncExitStack()
|
|
22
22
|
self._lock = threading.RLock()
|
|
23
23
|
self._async_lock = AsyncRLock()
|
|
24
24
|
|
|
25
|
-
def get(self, interface:
|
|
25
|
+
def get(self, interface: Any) -> Any | None:
|
|
26
26
|
"""Get an instance from the context."""
|
|
27
27
|
return self._instances.get(interface)
|
|
28
28
|
|
|
29
|
-
def set(self, interface:
|
|
29
|
+
def set(self, interface: Any, value: Any) -> None:
|
|
30
30
|
"""Set an instance in the context."""
|
|
31
31
|
self._instances[interface] = value
|
|
32
32
|
|
|
@@ -38,16 +38,16 @@ class InstanceContext:
|
|
|
38
38
|
"""Enter the context asynchronously."""
|
|
39
39
|
return await self._async_stack.enter_async_context(cm)
|
|
40
40
|
|
|
41
|
-
def __setitem__(self, interface:
|
|
41
|
+
def __setitem__(self, interface: Any, value: Any) -> None:
|
|
42
42
|
self._instances[interface] = value
|
|
43
43
|
|
|
44
|
-
def __getitem__(self, interface:
|
|
44
|
+
def __getitem__(self, interface: Any) -> Any:
|
|
45
45
|
return self._instances[interface]
|
|
46
46
|
|
|
47
|
-
def __contains__(self, interface:
|
|
47
|
+
def __contains__(self, interface: Any) -> bool:
|
|
48
48
|
return interface in self._instances
|
|
49
49
|
|
|
50
|
-
def __delitem__(self, interface:
|
|
50
|
+
def __delitem__(self, interface: Any) -> None:
|
|
51
51
|
self._instances.pop(interface, None)
|
|
52
52
|
|
|
53
53
|
def __enter__(self) -> Self:
|
|
@@ -78,9 +78,9 @@ class InstanceContext:
|
|
|
78
78
|
exc_tb: TracebackType | None,
|
|
79
79
|
) -> bool:
|
|
80
80
|
"""Exit the context asynchronously."""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
) or
|
|
81
|
+
sync_exit = await run_async(self.__exit__, exc_type, exc_val, exc_tb)
|
|
82
|
+
async_exit = await self._async_stack.__aexit__(exc_type, exc_val, exc_tb)
|
|
83
|
+
return bool(sync_exit) or bool(async_exit)
|
|
84
84
|
|
|
85
85
|
async def aclose(self) -> None:
|
|
86
86
|
"""Close the scoped context asynchronously."""
|
anydi/_types.py
CHANGED
|
@@ -8,7 +8,7 @@ from functools import cached_property
|
|
|
8
8
|
from types import ModuleType
|
|
9
9
|
from typing import Annotated, Any, Callable, NamedTuple, Union
|
|
10
10
|
|
|
11
|
-
import wrapt
|
|
11
|
+
import wrapt # type: ignore
|
|
12
12
|
from typing_extensions import Literal, Self, TypeAlias
|
|
13
13
|
|
|
14
14
|
Scope = Literal["transient", "singleton", "request"]
|
|
@@ -43,9 +43,9 @@ def is_event_type(obj: Any) -> bool:
|
|
|
43
43
|
return inspect.isclass(obj) and issubclass(obj, Event)
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
class InstanceProxy(wrapt.ObjectProxy): # type: ignore
|
|
46
|
+
class InstanceProxy(wrapt.ObjectProxy): # type: ignore
|
|
47
47
|
def __init__(self, wrapped: Any, *, interface: type[Any]) -> None:
|
|
48
|
-
super().__init__(wrapped)
|
|
48
|
+
super().__init__(wrapped) # type: ignore
|
|
49
49
|
self._self_interface = interface
|
|
50
50
|
|
|
51
51
|
@property
|
anydi/_utils.py
CHANGED
|
@@ -12,7 +12,7 @@ from collections.abc import AsyncIterator, Iterator
|
|
|
12
12
|
from types import TracebackType
|
|
13
13
|
from typing import Any, Callable, ForwardRef, TypeVar
|
|
14
14
|
|
|
15
|
-
import anyio
|
|
15
|
+
import anyio.to_thread
|
|
16
16
|
from typing_extensions import ParamSpec, Self, get_args, get_origin
|
|
17
17
|
|
|
18
18
|
try:
|
|
@@ -53,8 +53,9 @@ class ViewSignature(BaseViewSignature):
|
|
|
53
53
|
func_param = self._get_param_type(name, arg)
|
|
54
54
|
self.params.append(func_param)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
ninja_contribute_args = getattr(view_func, "_ninja_contribute_args", None)
|
|
57
|
+
if ninja_contribute_args is not None:
|
|
58
|
+
for p_name, p_type, p_source in ninja_contribute_args:
|
|
58
59
|
self.params.append(
|
|
59
60
|
FuncParam(p_name, p_source.alias or p_name, p_source, p_type, False)
|
|
60
61
|
)
|
anydi/ext/faststream.py
CHANGED
|
@@ -30,8 +30,8 @@ def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def _get_broken_handlers(broker: BrokerUsecase[Any, Any]) -> list[Any]:
|
|
33
|
-
if
|
|
34
|
-
return [handler.calls[0][0] for handler in
|
|
33
|
+
if (handlers := getattr(broker, "handlers", None)) is not None:
|
|
34
|
+
return [handler.calls[0][0] for handler in handlers.values()]
|
|
35
35
|
# faststream > 0.5.0
|
|
36
36
|
return [
|
|
37
37
|
subscriber.calls[0].handler
|
anydi/ext/pytest_plugin.py
CHANGED
|
@@ -6,7 +6,6 @@ from collections.abc import Iterator
|
|
|
6
6
|
from typing import Any, Callable, cast
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
|
-
from _pytest.python import async_warn_and_skip
|
|
10
9
|
from anyio.pytest_plugin import extract_backend_and_options, get_runner
|
|
11
10
|
|
|
12
11
|
from anydi import Container
|
|
@@ -117,7 +116,12 @@ def _anydi_ainject(
|
|
|
117
116
|
|
|
118
117
|
# Skip if the anyio backend is not available
|
|
119
118
|
if "anyio_backend" not in request.fixturenames:
|
|
120
|
-
|
|
119
|
+
msg = (
|
|
120
|
+
"To run async test functions with `anyio`, "
|
|
121
|
+
"please configure the `anyio` pytest plugin.\n"
|
|
122
|
+
"See: https://anyio.readthedocs.io/en/stable/testing.html"
|
|
123
|
+
)
|
|
124
|
+
pytest.fail(msg, pytrace=False)
|
|
121
125
|
|
|
122
126
|
async def _awrapper() -> None:
|
|
123
127
|
# Setup the container
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anydi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.41.0
|
|
4
4
|
Summary: Dependency Injection library
|
|
5
5
|
Project-URL: Repository, https://github.com/antonrh/anydi
|
|
6
6
|
Author-email: Anton Ruhlov <antonruhlov@gmail.com>
|
|
@@ -29,7 +29,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
29
29
|
Classifier: Typing :: Typed
|
|
30
30
|
Requires-Python: ~=3.9
|
|
31
31
|
Requires-Dist: anyio>=3.7.1
|
|
32
|
-
Requires-Dist: typing-extensions<5,>=4.
|
|
32
|
+
Requires-Dist: typing-extensions<5,>=4.13.2
|
|
33
33
|
Requires-Dist: wrapt<2,>=1.17.0
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
35
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
anydi/__init__.py,sha256=aAq10a1V_zQ3_Me3p_pll5d1O77PIgqotkOm3pshORI,495
|
|
2
|
-
anydi/_container.py,sha256=
|
|
3
|
-
anydi/_context.py,sha256=
|
|
4
|
-
anydi/_types.py,sha256=
|
|
5
|
-
anydi/_utils.py,sha256=
|
|
2
|
+
anydi/_container.py,sha256=b8Ul94gIP1-F0f_FJoWM9kRyyB8Do7YEmFvpynv8Qu0,41783
|
|
3
|
+
anydi/_context.py,sha256=pR97uMzafpuDe2jZcqnCShXmWp5bijzmNUOm6A6-NtY,3022
|
|
4
|
+
anydi/_types.py,sha256=MwLBNhjqndCraEUCIfEpvgZEJOaVmKan3zAh8BbFa0Y,3657
|
|
5
|
+
anydi/_utils.py,sha256=JHTp46uWdsWYJv4U1iU_twEnX__L3CEodK4yzhq3bHg,4858
|
|
6
6
|
anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
anydi/ext/_utils.py,sha256=U6sRqWzccWUu7eMhbXX1NrwcaxitQF9cO1KxnKF37gw,2566
|
|
9
9
|
anydi/ext/fastapi.py,sha256=AEL3ubu-LxUPHMMt1YIn3En_JZC7nyBKmKxmhka3O3c,2436
|
|
10
|
-
anydi/ext/faststream.py,sha256=
|
|
10
|
+
anydi/ext/faststream.py,sha256=TBpN8DwZ4qOMVo_u1AgG5tAKcTMwJnV8eWavPMgb6lg,1923
|
|
11
11
|
anydi/ext/pydantic_settings.py,sha256=8IXXLuG_OvKbvKlBkBRQUHcXgbTpgQUxeWyoMcRIUQM,1488
|
|
12
|
-
anydi/ext/pytest_plugin.py,sha256=
|
|
12
|
+
anydi/ext/pytest_plugin.py,sha256=4R93VpLePF65NuZqYF3ljjjG5cvJvrzoMguDqMzlT9s,4771
|
|
13
13
|
anydi/ext/django/__init__.py,sha256=QI1IABCVgSDTUoh7M9WMECKXwB3xvh04HfQ9TOWw1Mk,223
|
|
14
14
|
anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
|
|
15
15
|
anydi/ext/django/_settings.py,sha256=Z0RlAuXoO73oahWeMkK10w8c-4uCBde-DBpeKTV5USY,853
|
|
@@ -18,11 +18,11 @@ anydi/ext/django/apps.py,sha256=mjbf_mDCpNSriGnILzhRIr8wFHLMEK8sUerbmRku6i0,2844
|
|
|
18
18
|
anydi/ext/django/middleware.py,sha256=5OUdp0OWRozyW338Sq04BDhacaFlyUTTOduS_7EwCTA,854
|
|
19
19
|
anydi/ext/django/ninja/__init__.py,sha256=kW3grUgWp_nkWSG_-39ADHMrZLGNcj9TsJ9OW8iWWrk,546
|
|
20
20
|
anydi/ext/django/ninja/_operation.py,sha256=wSWa7D73XTVlOibmOciv2l6JHPe1ERZcXrqI8W-oO2w,2696
|
|
21
|
-
anydi/ext/django/ninja/_signature.py,sha256=
|
|
21
|
+
anydi/ext/django/ninja/_signature.py,sha256=uYrG2PFgG2IlXrM24rgDOtRBnrbKQeAMl6ErypRi8qs,2260
|
|
22
22
|
anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
|
|
24
|
-
anydi-0.
|
|
25
|
-
anydi-0.
|
|
26
|
-
anydi-0.
|
|
27
|
-
anydi-0.
|
|
28
|
-
anydi-0.
|
|
24
|
+
anydi-0.41.0.dist-info/METADATA,sha256=G_AXPWt34pdLtXg2ADFd8GxednjSkyBWnQbCp_HrWrQ,4957
|
|
25
|
+
anydi-0.41.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
26
|
+
anydi-0.41.0.dist-info/entry_points.txt,sha256=Nklo9f3Oe4AkNsEgC4g43nCJ-23QDngZSVDNRMdaILI,43
|
|
27
|
+
anydi-0.41.0.dist-info/licenses/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
|
|
28
|
+
anydi-0.41.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|