anydi 0.54.1__py3-none-any.whl → 0.55.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/__init__.py +1 -2
- anydi/{_async.py → _async_lock.py} +3 -13
- anydi/_container.py +83 -365
- anydi/_context.py +27 -9
- anydi/_decorators.py +1 -1
- anydi/_provider.py +9 -38
- anydi/_resolver.py +563 -0
- anydi/{_scan.py → _scanner.py} +1 -1
- anydi/{_typing.py → _types.py} +22 -21
- anydi/ext/fastapi.py +1 -1
- anydi/ext/faststream.py +1 -1
- anydi/testing.py +14 -48
- {anydi-0.54.1.dist-info → anydi-0.55.0.dist-info}/METADATA +10 -16
- anydi-0.55.0.dist-info/RECORD +24 -0
- anydi/_scope.py +0 -9
- anydi-0.54.1.dist-info/RECORD +0 -24
- {anydi-0.54.1.dist-info → anydi-0.55.0.dist-info}/WHEEL +0 -0
- {anydi-0.54.1.dist-info → anydi-0.55.0.dist-info}/entry_points.txt +0 -0
anydi/_context.py
CHANGED
|
@@ -5,10 +5,11 @@ import threading
|
|
|
5
5
|
from types import TracebackType
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
import anyio.to_thread
|
|
8
9
|
from typing_extensions import Self
|
|
9
10
|
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
11
|
+
from ._async_lock import AsyncRLock
|
|
12
|
+
from ._types import NOT_SET
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class InstanceContext:
|
|
@@ -18,10 +19,10 @@ class InstanceContext:
|
|
|
18
19
|
|
|
19
20
|
def __init__(self) -> None:
|
|
20
21
|
self._instances: dict[Any, Any] = {}
|
|
21
|
-
self._stack
|
|
22
|
-
self._async_stack
|
|
23
|
-
self._lock
|
|
24
|
-
self._async_lock =
|
|
22
|
+
self._stack: contextlib.ExitStack | None = None
|
|
23
|
+
self._async_stack: contextlib.AsyncExitStack | None = None
|
|
24
|
+
self._lock: threading.RLock | None = None
|
|
25
|
+
self._async_lock: AsyncRLock | None = None
|
|
25
26
|
|
|
26
27
|
def get(self, interface: Any, default: Any = NOT_SET) -> Any:
|
|
27
28
|
"""Get an instance from the context."""
|
|
@@ -33,10 +34,14 @@ class InstanceContext:
|
|
|
33
34
|
|
|
34
35
|
def enter(self, cm: contextlib.AbstractContextManager[Any]) -> Any:
|
|
35
36
|
"""Enter the context."""
|
|
37
|
+
if self._stack is None:
|
|
38
|
+
self._stack = contextlib.ExitStack()
|
|
36
39
|
return self._stack.enter_context(cm)
|
|
37
40
|
|
|
38
41
|
async def aenter(self, cm: contextlib.AbstractAsyncContextManager[Any]) -> Any:
|
|
39
42
|
"""Enter the context asynchronously."""
|
|
43
|
+
if self._async_stack is None:
|
|
44
|
+
self._async_stack = contextlib.AsyncExitStack()
|
|
40
45
|
return await self._async_stack.enter_async_context(cm)
|
|
41
46
|
|
|
42
47
|
def __setitem__(self, interface: Any, value: Any) -> None:
|
|
@@ -62,11 +67,14 @@ class InstanceContext:
|
|
|
62
67
|
exc_tb: TracebackType | None,
|
|
63
68
|
) -> Any:
|
|
64
69
|
"""Exit the context."""
|
|
70
|
+
if self._stack is None:
|
|
71
|
+
return False
|
|
65
72
|
return self._stack.__exit__(exc_type, exc_val, exc_tb)
|
|
66
73
|
|
|
67
74
|
def close(self) -> None:
|
|
68
75
|
"""Close the scoped context."""
|
|
69
|
-
self._stack
|
|
76
|
+
if self._stack is not None:
|
|
77
|
+
self._stack.__exit__(None, None, None)
|
|
70
78
|
|
|
71
79
|
async def __aenter__(self) -> Self:
|
|
72
80
|
"""Enter the context asynchronously."""
|
|
@@ -79,8 +87,14 @@ class InstanceContext:
|
|
|
79
87
|
exc_tb: TracebackType | None,
|
|
80
88
|
) -> bool:
|
|
81
89
|
"""Exit the context asynchronously."""
|
|
82
|
-
sync_exit =
|
|
83
|
-
async_exit =
|
|
90
|
+
sync_exit = False
|
|
91
|
+
async_exit = False
|
|
92
|
+
if self._stack is not None:
|
|
93
|
+
sync_exit = await anyio.to_thread.run_sync(
|
|
94
|
+
self.__exit__, exc_type, exc_val, exc_tb
|
|
95
|
+
)
|
|
96
|
+
if self._async_stack is not None:
|
|
97
|
+
async_exit = await self._async_stack.__aexit__(exc_type, exc_val, exc_tb)
|
|
84
98
|
return bool(sync_exit) or bool(async_exit)
|
|
85
99
|
|
|
86
100
|
async def aclose(self) -> None:
|
|
@@ -89,8 +103,12 @@ class InstanceContext:
|
|
|
89
103
|
|
|
90
104
|
def lock(self) -> threading.RLock:
|
|
91
105
|
"""Acquire the context lock."""
|
|
106
|
+
if self._lock is None:
|
|
107
|
+
self._lock = threading.RLock()
|
|
92
108
|
return self._lock
|
|
93
109
|
|
|
94
110
|
def alock(self) -> AsyncRLock:
|
|
95
111
|
"""Acquire the context lock asynchronously."""
|
|
112
|
+
if self._async_lock is None:
|
|
113
|
+
self._async_lock = AsyncRLock()
|
|
96
114
|
return self._async_lock
|
anydi/_decorators.py
CHANGED
anydi/_provider.py
CHANGED
|
@@ -4,11 +4,9 @@ import enum
|
|
|
4
4
|
import inspect
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from functools import cached_property
|
|
8
7
|
from typing import Any
|
|
9
8
|
|
|
10
|
-
from .
|
|
11
|
-
from ._typing import NOT_SET
|
|
9
|
+
from ._types import NOT_SET, Scope
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class ProviderKind(enum.IntEnum):
|
|
@@ -34,56 +32,29 @@ class ProviderKind(enum.IntEnum):
|
|
|
34
32
|
f"The provider `{call}` is invalid because it is not a callable object."
|
|
35
33
|
)
|
|
36
34
|
|
|
37
|
-
@classmethod
|
|
38
|
-
def is_resource(cls, kind: ProviderKind) -> bool:
|
|
39
|
-
return kind in (cls.GENERATOR, cls.ASYNC_GENERATOR)
|
|
40
|
-
|
|
41
35
|
|
|
42
|
-
@dataclass(
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
43
37
|
class ProviderParameter:
|
|
44
38
|
name: str
|
|
45
39
|
annotation: Any
|
|
46
40
|
default: Any
|
|
47
41
|
has_default: bool
|
|
48
42
|
provider: Provider | None = None
|
|
49
|
-
shared_scope: bool = False
|
|
50
43
|
|
|
51
44
|
|
|
52
|
-
@dataclass(
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
53
46
|
class Provider:
|
|
54
47
|
call: Callable[..., Any]
|
|
55
48
|
scope: Scope
|
|
56
49
|
interface: Any
|
|
57
50
|
name: str
|
|
58
51
|
parameters: tuple[ProviderParameter, ...]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def is_class(self) -> bool:
|
|
66
|
-
return self.kind == ProviderKind.CLASS
|
|
67
|
-
|
|
68
|
-
@cached_property
|
|
69
|
-
def is_coroutine(self) -> bool:
|
|
70
|
-
return self.kind == ProviderKind.COROUTINE
|
|
71
|
-
|
|
72
|
-
@cached_property
|
|
73
|
-
def is_generator(self) -> bool:
|
|
74
|
-
return self.kind == ProviderKind.GENERATOR
|
|
75
|
-
|
|
76
|
-
@cached_property
|
|
77
|
-
def is_async_generator(self) -> bool:
|
|
78
|
-
return self.kind == ProviderKind.ASYNC_GENERATOR
|
|
79
|
-
|
|
80
|
-
@cached_property
|
|
81
|
-
def is_async(self) -> bool:
|
|
82
|
-
return self.is_coroutine or self.is_async_generator
|
|
83
|
-
|
|
84
|
-
@cached_property
|
|
85
|
-
def is_resource(self) -> bool:
|
|
86
|
-
return ProviderKind.is_resource(self.kind)
|
|
52
|
+
is_class: bool
|
|
53
|
+
is_coroutine: bool
|
|
54
|
+
is_generator: bool
|
|
55
|
+
is_async_generator: bool
|
|
56
|
+
is_async: bool
|
|
57
|
+
is_resource: bool
|
|
87
58
|
|
|
88
59
|
|
|
89
60
|
@dataclass(frozen=True, slots=True)
|