modern-di 0.7.2__py3-none-any.whl → 0.8.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.
Potentially problematic release.
This version of modern-di might be problematic. Click here for more details.
- modern_di/container.py +27 -6
- modern_di/provider_state.py +5 -3
- modern_di/providers/resource.py +23 -10
- modern_di/providers/singleton.py +17 -6
- {modern_di-0.7.2.dist-info → modern_di-0.8.0.dist-info}/METADATA +1 -1
- {modern_di-0.7.2.dist-info → modern_di-0.8.0.dist-info}/RECORD +7 -7
- {modern_di-0.7.2.dist-info → modern_di-0.8.0.dist-info}/WHEEL +0 -0
modern_di/container.py
CHANGED
|
@@ -14,7 +14,15 @@ T_co = typing.TypeVar("T_co", covariant=True)
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Container(contextlib.AbstractAsyncContextManager["Container"]):
|
|
17
|
-
__slots__ =
|
|
17
|
+
__slots__ = (
|
|
18
|
+
"scope",
|
|
19
|
+
"parent_container",
|
|
20
|
+
"context",
|
|
21
|
+
"_is_async",
|
|
22
|
+
"_provider_states",
|
|
23
|
+
"_overrides",
|
|
24
|
+
"_use_threading_lock",
|
|
25
|
+
)
|
|
18
26
|
|
|
19
27
|
def __init__(
|
|
20
28
|
self,
|
|
@@ -22,6 +30,7 @@ class Container(contextlib.AbstractAsyncContextManager["Container"]):
|
|
|
22
30
|
scope: enum.IntEnum,
|
|
23
31
|
parent_container: typing.Optional["Container"] = None,
|
|
24
32
|
context: dict[str, typing.Any] | None = None,
|
|
33
|
+
use_threading_lock: bool = True,
|
|
25
34
|
) -> None:
|
|
26
35
|
self.scope = scope
|
|
27
36
|
self.parent_container = parent_container
|
|
@@ -29,6 +38,7 @@ class Container(contextlib.AbstractAsyncContextManager["Container"]):
|
|
|
29
38
|
self._is_async: bool | None = None
|
|
30
39
|
self._provider_states: dict[str, ProviderState[typing.Any]] = {}
|
|
31
40
|
self._overrides: dict[str, typing.Any] = {}
|
|
41
|
+
self._use_threading_lock = use_threading_lock
|
|
32
42
|
|
|
33
43
|
def _exit(self) -> None:
|
|
34
44
|
self._is_async = None
|
|
@@ -74,17 +84,28 @@ class Container(contextlib.AbstractAsyncContextManager["Container"]):
|
|
|
74
84
|
return container
|
|
75
85
|
|
|
76
86
|
def fetch_provider_state(
|
|
77
|
-
self,
|
|
87
|
+
self,
|
|
88
|
+
provider_id: str,
|
|
89
|
+
is_async_resource: bool = False,
|
|
90
|
+
use_asyncio_lock: bool = False,
|
|
91
|
+
use_threading_lock: bool = False,
|
|
78
92
|
) -> ProviderState[typing.Any]:
|
|
79
93
|
self._check_entered()
|
|
80
94
|
if is_async_resource and self._is_async is False:
|
|
81
95
|
msg = "Resolving async resource in sync container is not allowed"
|
|
82
96
|
raise RuntimeError(msg)
|
|
83
97
|
|
|
84
|
-
if
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
if provider_state := self._provider_states.get(provider_id):
|
|
99
|
+
return provider_state
|
|
100
|
+
|
|
101
|
+
# expected to be thread-safe, because setdefault is atomic
|
|
102
|
+
return self._provider_states.setdefault(
|
|
103
|
+
provider_id,
|
|
104
|
+
ProviderState(
|
|
105
|
+
use_asyncio_lock=use_asyncio_lock,
|
|
106
|
+
use_threading_lock=self._use_threading_lock and use_threading_lock,
|
|
107
|
+
),
|
|
108
|
+
)
|
|
88
109
|
|
|
89
110
|
def override(self, provider_id: str, override_object: object) -> None:
|
|
90
111
|
self._overrides[provider_id] = override_object
|
modern_di/provider_state.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import contextlib
|
|
3
|
+
import threading
|
|
3
4
|
import typing
|
|
4
5
|
|
|
5
6
|
|
|
@@ -7,12 +8,13 @@ T_co = typing.TypeVar("T_co", covariant=True)
|
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class ProviderState(typing.Generic[T_co]):
|
|
10
|
-
__slots__ = "context_stack", "instance", "
|
|
11
|
+
__slots__ = "context_stack", "instance", "asyncio_lock", "threading_lock"
|
|
11
12
|
|
|
12
|
-
def __init__(self,
|
|
13
|
+
def __init__(self, use_asyncio_lock: bool, use_threading_lock: bool) -> None:
|
|
13
14
|
self.context_stack: contextlib.AsyncExitStack | contextlib.ExitStack | None = None
|
|
14
15
|
self.instance: T_co | None = None
|
|
15
|
-
self.
|
|
16
|
+
self.asyncio_lock: typing.Final = asyncio.Lock() if use_asyncio_lock else None
|
|
17
|
+
self.threading_lock: typing.Final = threading.Lock() if use_threading_lock else None
|
|
16
18
|
|
|
17
19
|
async def async_tear_down(self) -> None:
|
|
18
20
|
if self.context_stack is None:
|
modern_di/providers/resource.py
CHANGED
|
@@ -58,13 +58,13 @@ class Resource(AbstractCreatorProvider[T_co]):
|
|
|
58
58
|
return typing.cast(T_co, override)
|
|
59
59
|
|
|
60
60
|
provider_state = container.fetch_provider_state(
|
|
61
|
-
self.provider_id, is_async_resource=self._is_async,
|
|
61
|
+
self.provider_id, is_async_resource=self._is_async, use_asyncio_lock=True
|
|
62
62
|
)
|
|
63
63
|
if provider_state.instance is not None:
|
|
64
64
|
return typing.cast(T_co, provider_state.instance)
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
if provider_state.asyncio_lock:
|
|
67
|
+
await provider_state.asyncio_lock.acquire()
|
|
68
68
|
|
|
69
69
|
try:
|
|
70
70
|
if provider_state.instance is not None:
|
|
@@ -79,7 +79,8 @@ class Resource(AbstractCreatorProvider[T_co]):
|
|
|
79
79
|
provider_state.context_stack = contextlib.ExitStack()
|
|
80
80
|
provider_state.instance = provider_state.context_stack.enter_context(_intermediate_)
|
|
81
81
|
finally:
|
|
82
|
-
provider_state.
|
|
82
|
+
if provider_state.asyncio_lock:
|
|
83
|
+
provider_state.asyncio_lock.release()
|
|
83
84
|
|
|
84
85
|
return typing.cast(T_co, provider_state.instance)
|
|
85
86
|
|
|
@@ -88,7 +89,9 @@ class Resource(AbstractCreatorProvider[T_co]):
|
|
|
88
89
|
if (override := container.fetch_override(self.provider_id)) is not None:
|
|
89
90
|
return typing.cast(T_co, override)
|
|
90
91
|
|
|
91
|
-
provider_state = container.fetch_provider_state(
|
|
92
|
+
provider_state = container.fetch_provider_state(
|
|
93
|
+
self.provider_id, is_async_resource=self._is_async, use_threading_lock=True
|
|
94
|
+
)
|
|
92
95
|
if provider_state.instance is not None:
|
|
93
96
|
return typing.cast(T_co, provider_state.instance)
|
|
94
97
|
|
|
@@ -96,11 +99,21 @@ class Resource(AbstractCreatorProvider[T_co]):
|
|
|
96
99
|
msg = "Async resource cannot be resolved synchronously"
|
|
97
100
|
raise RuntimeError(msg)
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
if provider_state.threading_lock:
|
|
103
|
+
provider_state.threading_lock.acquire()
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
try:
|
|
106
|
+
if provider_state.instance is not None:
|
|
107
|
+
return typing.cast(T_co, provider_state.instance)
|
|
108
|
+
|
|
109
|
+
_intermediate_ = self._sync_build_creator(container)
|
|
110
|
+
|
|
111
|
+
provider_state.context_stack = contextlib.ExitStack()
|
|
112
|
+
provider_state.instance = provider_state.context_stack.enter_context(
|
|
113
|
+
typing.cast(contextlib.AbstractContextManager[typing.Any], _intermediate_)
|
|
114
|
+
)
|
|
115
|
+
finally:
|
|
116
|
+
if provider_state.threading_lock:
|
|
117
|
+
provider_state.threading_lock.release()
|
|
105
118
|
|
|
106
119
|
return typing.cast(T_co, provider_state.instance)
|
modern_di/providers/singleton.py
CHANGED
|
@@ -26,12 +26,12 @@ class Singleton(AbstractCreatorProvider[T_co]):
|
|
|
26
26
|
if (override := container.fetch_override(self.provider_id)) is not None:
|
|
27
27
|
return typing.cast(T_co, override)
|
|
28
28
|
|
|
29
|
-
provider_state = container.fetch_provider_state(self.provider_id,
|
|
29
|
+
provider_state = container.fetch_provider_state(self.provider_id, use_asyncio_lock=True)
|
|
30
30
|
if provider_state.instance is not None:
|
|
31
31
|
return typing.cast(T_co, provider_state.instance)
|
|
32
32
|
|
|
33
|
-
assert provider_state.
|
|
34
|
-
await provider_state.
|
|
33
|
+
assert provider_state.asyncio_lock
|
|
34
|
+
await provider_state.asyncio_lock.acquire()
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
37
|
if provider_state.instance is not None:
|
|
@@ -39,7 +39,7 @@ class Singleton(AbstractCreatorProvider[T_co]):
|
|
|
39
39
|
|
|
40
40
|
provider_state.instance = typing.cast(T_co, await self._async_build_creator(container))
|
|
41
41
|
finally:
|
|
42
|
-
provider_state.
|
|
42
|
+
provider_state.asyncio_lock.release()
|
|
43
43
|
|
|
44
44
|
return provider_state.instance
|
|
45
45
|
|
|
@@ -48,9 +48,20 @@ class Singleton(AbstractCreatorProvider[T_co]):
|
|
|
48
48
|
if (override := container.fetch_override(self.provider_id)) is not None:
|
|
49
49
|
return typing.cast(T_co, override)
|
|
50
50
|
|
|
51
|
-
provider_state = container.fetch_provider_state(self.provider_id)
|
|
51
|
+
provider_state = container.fetch_provider_state(self.provider_id, use_threading_lock=True)
|
|
52
52
|
if provider_state.instance is not None:
|
|
53
53
|
return typing.cast(T_co, provider_state.instance)
|
|
54
54
|
|
|
55
|
-
provider_state.
|
|
55
|
+
if provider_state.threading_lock:
|
|
56
|
+
provider_state.threading_lock.acquire()
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
if provider_state.instance is not None:
|
|
60
|
+
return typing.cast(T_co, provider_state.instance)
|
|
61
|
+
|
|
62
|
+
provider_state.instance = self._sync_build_creator(container)
|
|
63
|
+
finally:
|
|
64
|
+
if provider_state.threading_lock:
|
|
65
|
+
provider_state.threading_lock.release()
|
|
66
|
+
|
|
56
67
|
return typing.cast(T_co, provider_state.instance)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: modern-di
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Dependency Injection framework with IOC-container and scopes
|
|
5
5
|
Project-URL: repository, https://github.com/modern-python/modern-di
|
|
6
6
|
Project-URL: docs, https://modern-di.readthedocs.io
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
modern_di/__init__.py,sha256=L01VkzSJiV0d0FPrh1DZ-Wy5mUmoG6X-oLz7xYxtehI,194
|
|
2
|
-
modern_di/container.py,sha256=
|
|
2
|
+
modern_di/container.py,sha256=ySfFh9keq5XZvIEpxEFUPW226aVA_JGC2k3buTxPL9U,4831
|
|
3
3
|
modern_di/graph.py,sha256=X60wtG3Mqus_5YZNiZlQuXoHODBp7rYl_IHJs7GzSQM,1356
|
|
4
|
-
modern_di/provider_state.py,sha256=
|
|
4
|
+
modern_di/provider_state.py,sha256=JBn3Yi2KzFpclVr_Iyiewosqy9MNZk6jrkkF433fJGQ,1358
|
|
5
5
|
modern_di/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
modern_di/scope.py,sha256=e6Olc-CF89clbYDNGciy-F8EqJt1Mw2703zfuJaEY94,113
|
|
7
7
|
modern_di/providers/__init__.py,sha256=r594L4kWv_XCHVZcsfUIGBtlXCmih6NR1Ifa105XoQ4,649
|
|
@@ -12,9 +12,9 @@ modern_di/providers/dict.py,sha256=nCU9iaqteYHDbILAfhrdnbMgS9_emE4MS7Xn2VoUlPo,8
|
|
|
12
12
|
modern_di/providers/factory.py,sha256=NozbrprJlRJPWSNdvKR0kOwPt1Q9i_ZLJTPOfzdDDJo,1359
|
|
13
13
|
modern_di/providers/injected_factory.py,sha256=wK9GG5_d33BdrpimnR6W-zeviZcCS8qe8ZGTmjYtFf4,1135
|
|
14
14
|
modern_di/providers/list.py,sha256=3hx34RfBRmqzh-cT5D6wSTDJPkBGMK_ul4n9gQz-o9M,769
|
|
15
|
-
modern_di/providers/resource.py,sha256=
|
|
15
|
+
modern_di/providers/resource.py,sha256=CsMekkVISklTqN539XqH4iE80vfc6sQMav5OT8fZvyA,4591
|
|
16
16
|
modern_di/providers/selector.py,sha256=RQbHD2-Liw-TGqu6UELbfCzXYuqxiO_Mg1tLyF3mKQo,1419
|
|
17
|
-
modern_di/providers/singleton.py,sha256=
|
|
18
|
-
modern_di-0.
|
|
19
|
-
modern_di-0.
|
|
20
|
-
modern_di-0.
|
|
17
|
+
modern_di/providers/singleton.py,sha256=_hUpCmbHgLAigdhBiu0zypwWwrIGdB6_oZkGfuLxzNE,2372
|
|
18
|
+
modern_di-0.8.0.dist-info/METADATA,sha256=io18Xr4sXvf11cQambypZuh4aB4IZ9XPaMq2SJJTG_c,5440
|
|
19
|
+
modern_di-0.8.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
20
|
+
modern_di-0.8.0.dist-info/RECORD,,
|
|
File without changes
|