pico-ioc 2.0.0__py3-none-any.whl → 2.0.2__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.
- pico_ioc/__init__.py +3 -12
- pico_ioc/_version.py +1 -1
- pico_ioc/aop.py +55 -21
- pico_ioc/api.py +497 -116
- pico_ioc/container.py +161 -26
- pico_ioc/event_bus.py +3 -4
- pico_ioc/exceptions.py +9 -3
- pico_ioc/scope.py +47 -2
- pico_ioc-2.0.2.dist-info/METADATA +243 -0
- pico_ioc-2.0.2.dist-info/RECORD +17 -0
- pico_ioc-2.0.0.dist-info/METADATA +0 -230
- pico_ioc-2.0.0.dist-info/RECORD +0 -17
- {pico_ioc-2.0.0.dist-info → pico_ioc-2.0.2.dist-info}/WHEEL +0 -0
- {pico_ioc-2.0.0.dist-info → pico_ioc-2.0.2.dist-info}/licenses/LICENSE +0 -0
- {pico_ioc-2.0.0.dist-info → pico_ioc-2.0.2.dist-info}/top_level.txt +0 -0
pico_ioc/__init__.py
CHANGED
|
@@ -17,18 +17,13 @@ from .api import (
|
|
|
17
17
|
factory,
|
|
18
18
|
provides,
|
|
19
19
|
Qualifier,
|
|
20
|
-
qualifier,
|
|
21
|
-
on_missing,
|
|
22
|
-
primary,
|
|
23
|
-
conditional,
|
|
24
|
-
lazy,
|
|
25
20
|
configuration,
|
|
26
21
|
configure,
|
|
27
22
|
cleanup,
|
|
28
|
-
scope,
|
|
29
23
|
ConfigSource,
|
|
30
24
|
EnvSource,
|
|
31
25
|
FileSource,
|
|
26
|
+
FlatDictSource,
|
|
32
27
|
init,
|
|
33
28
|
configured,
|
|
34
29
|
)
|
|
@@ -61,15 +56,9 @@ __all__ = [
|
|
|
61
56
|
"factory",
|
|
62
57
|
"provides",
|
|
63
58
|
"Qualifier",
|
|
64
|
-
"qualifier",
|
|
65
|
-
"on_missing",
|
|
66
|
-
"primary",
|
|
67
|
-
"conditional",
|
|
68
|
-
"lazy",
|
|
69
59
|
"configuration",
|
|
70
60
|
"configure",
|
|
71
61
|
"cleanup",
|
|
72
|
-
"scope",
|
|
73
62
|
"ScopeProtocol",
|
|
74
63
|
"ContextVarScope",
|
|
75
64
|
"ScopeManager",
|
|
@@ -88,6 +77,7 @@ __all__ = [
|
|
|
88
77
|
"EnvSource",
|
|
89
78
|
"FileSource",
|
|
90
79
|
"ConfigSource",
|
|
80
|
+
"FlatDictSource",
|
|
91
81
|
"init",
|
|
92
82
|
"configured",
|
|
93
83
|
"EventBus",
|
|
@@ -102,3 +92,4 @@ __all__ = [
|
|
|
102
92
|
"Discriminator",
|
|
103
93
|
]
|
|
104
94
|
|
|
95
|
+
|
pico_ioc/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '2.0.
|
|
1
|
+
__version__ = '2.0.2'
|
pico_ioc/aop.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# src/pico_ioc/aop.py
|
|
2
2
|
import inspect
|
|
3
3
|
import pickle
|
|
4
|
+
import threading
|
|
4
5
|
from typing import Any, Callable, Dict, List, Tuple, Protocol, Union
|
|
5
6
|
from .exceptions import SerializationError
|
|
6
7
|
|
|
@@ -73,7 +74,7 @@ def health(fn):
|
|
|
73
74
|
return fn
|
|
74
75
|
|
|
75
76
|
class UnifiedComponentProxy:
|
|
76
|
-
__slots__ = ("_target", "_creator", "_container", "_cache")
|
|
77
|
+
__slots__ = ("_target", "_creator", "_container", "_cache", "_lock")
|
|
77
78
|
def __init__(self, *, container: Any, target: Any = None, object_creator: Callable[[], Any] | None = None):
|
|
78
79
|
if container is None:
|
|
79
80
|
raise ValueError("UnifiedComponentProxy requires a non-null container")
|
|
@@ -83,19 +84,44 @@ class UnifiedComponentProxy:
|
|
|
83
84
|
object.__setattr__(self, "_target", target)
|
|
84
85
|
object.__setattr__(self, "_creator", object_creator)
|
|
85
86
|
object.__setattr__(self, "_cache", {})
|
|
86
|
-
|
|
87
|
+
object.__setattr__(self, "_lock", threading.RLock())
|
|
88
|
+
|
|
89
|
+
def __getstate__(self):
|
|
90
|
+
o = self._get_real_object()
|
|
91
|
+
try:
|
|
92
|
+
data = pickle.dumps(o)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
raise SerializationError(f"Proxy target is not serializable: {e}")
|
|
95
|
+
return {"data": data}
|
|
96
|
+
|
|
97
|
+
def __setstate__(self, state):
|
|
98
|
+
object.__setattr__(self, "_container", None)
|
|
99
|
+
object.__setattr__(self, "_creator", None)
|
|
100
|
+
object.__setattr__(self, "_cache", {})
|
|
101
|
+
object.__setattr__(self, "_lock", threading.RLock())
|
|
102
|
+
try:
|
|
103
|
+
obj = pickle.loads(state["data"])
|
|
104
|
+
except Exception as e:
|
|
105
|
+
raise SerializationError(f"Failed to restore proxy: {e}")
|
|
106
|
+
object.__setattr__(self, "_target", obj)
|
|
107
|
+
|
|
87
108
|
def _get_real_object(self) -> Any:
|
|
88
109
|
tgt = object.__getattribute__(self, "_target")
|
|
89
110
|
if tgt is not None:
|
|
90
111
|
return tgt
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
lock = object.__getattribute__(self, "_lock")
|
|
113
|
+
with lock:
|
|
114
|
+
tgt = object.__getattribute__(self, "_target")
|
|
115
|
+
if tgt is not None:
|
|
116
|
+
return tgt
|
|
117
|
+
creator = object.__getattribute__(self, "_creator")
|
|
118
|
+
if not callable(creator):
|
|
119
|
+
raise TypeError("UnifiedComponentProxy object_creator must be callable")
|
|
120
|
+
tgt = creator()
|
|
121
|
+
if tgt is None:
|
|
122
|
+
raise RuntimeError("UnifiedComponentProxy object_creator returned None")
|
|
123
|
+
object.__setattr__(self, "_target", tgt)
|
|
124
|
+
return tgt
|
|
99
125
|
|
|
100
126
|
def _scope_signature(self) -> Tuple[Any, ...]:
|
|
101
127
|
container = object.__getattribute__(self, "_container")
|
|
@@ -122,7 +148,6 @@ class UnifiedComponentProxy:
|
|
|
122
148
|
original_func = bound
|
|
123
149
|
if hasattr(bound, '__func__'):
|
|
124
150
|
original_func = bound.__func__
|
|
125
|
-
|
|
126
151
|
if inspect.iscoroutinefunction(original_func):
|
|
127
152
|
async def aw(*args, **kwargs):
|
|
128
153
|
ctx = MethodCtx(
|
|
@@ -170,16 +195,25 @@ class UnifiedComponentProxy:
|
|
|
170
195
|
interceptors_cls = _gather_interceptors_for_method(type(target), name)
|
|
171
196
|
if not interceptors_cls:
|
|
172
197
|
return attr
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
198
|
+
|
|
199
|
+
lock = object.__getattribute__(self, "_lock")
|
|
200
|
+
with lock:
|
|
201
|
+
cache: Dict[str, Tuple[Tuple[Any, ...], Callable[..., Any], Tuple[type, ...]]] = object.__getattribute__(self, "_cache")
|
|
202
|
+
cur_sig = self._scope_signature()
|
|
203
|
+
cached = cache.get(name)
|
|
204
|
+
if cached is not None:
|
|
205
|
+
sig, wrapped, cls_tuple = cached
|
|
206
|
+
if sig == cur_sig and cls_tuple == interceptors_cls:
|
|
207
|
+
return wrapped
|
|
208
|
+
|
|
209
|
+
cached = cache.get(name)
|
|
210
|
+
if cached is not None:
|
|
211
|
+
sig, wrapped, cls_tuple = cached
|
|
212
|
+
if sig == cur_sig and cls_tuple == interceptors_cls:
|
|
213
|
+
return wrapped
|
|
214
|
+
sig, wrapped, cls_tuple = self._build_wrapped(name, attr, interceptors_cls)
|
|
215
|
+
cache[name] = (sig, wrapped, cls_tuple)
|
|
216
|
+
return wrapped
|
|
183
217
|
|
|
184
218
|
def __setattr__(self, name, value): setattr(self._get_real_object(), name, value)
|
|
185
219
|
def __delattr__(self, name): delattr(self._get_real_object(), name)
|