pico-ioc 2.0.0__py3-none-any.whl → 2.0.1__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 CHANGED
@@ -17,15 +17,9 @@ 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,
@@ -61,15 +55,9 @@ __all__ = [
61
55
  "factory",
62
56
  "provides",
63
57
  "Qualifier",
64
- "qualifier",
65
- "on_missing",
66
- "primary",
67
- "conditional",
68
- "lazy",
69
58
  "configuration",
70
59
  "configure",
71
60
  "cleanup",
72
- "scope",
73
61
  "ScopeProtocol",
74
62
  "ContextVarScope",
75
63
  "ScopeManager",
@@ -102,3 +90,4 @@ __all__ = [
102
90
  "Discriminator",
103
91
  ]
104
92
 
93
+
pico_ioc/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '2.0.0'
1
+ __version__ = '2.0.1'
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
- creator = object.__getattribute__(self, "_creator")
92
- if not callable(creator):
93
- raise TypeError("UnifiedComponentProxy object_creator must be callable")
94
- tgt = creator()
95
- if tgt is None:
96
- raise RuntimeError("UnifiedComponentProxy object_creator returned None")
97
- object.__setattr__(self, "_target", tgt)
98
- return tgt
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
- cache: Dict[str, Tuple[Tuple[Any, ...], Callable[..., Any], Tuple[type, ...]]] = object.__getattribute__(self, "_cache")
174
- cur_sig = self._scope_signature()
175
- cached = cache.get(name)
176
- if cached is not None:
177
- sig, wrapped, cls_tuple = cached
178
- if sig == cur_sig and cls_tuple == interceptors_cls:
179
- return wrapped
180
- sig, wrapped, cls_tuple = self._build_wrapped(name, attr, interceptors_cls)
181
- cache[name] = (sig, wrapped, cls_tuple)
182
- return wrapped
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)