pico-ioc 2.0.2__py3-none-any.whl → 2.0.4__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 +2 -0
- pico_ioc/_version.py +1 -1
- pico_ioc/api.py +14 -4
- pico_ioc/container.py +57 -54
- pico_ioc/exceptions.py +17 -2
- pico_ioc/factory.py +2 -2
- {pico_ioc-2.0.2.dist-info → pico_ioc-2.0.4.dist-info}/METADATA +1 -1
- pico_ioc-2.0.4.dist-info/RECORD +17 -0
- pico_ioc-2.0.2.dist-info/RECORD +0 -17
- {pico_ioc-2.0.2.dist-info → pico_ioc-2.0.4.dist-info}/WHEEL +0 -0
- {pico_ioc-2.0.2.dist-info → pico_ioc-2.0.4.dist-info}/licenses/LICENSE +0 -0
- {pico_ioc-2.0.2.dist-info → pico_ioc-2.0.4.dist-info}/top_level.txt +0 -0
pico_ioc/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ from .exceptions import (
|
|
|
11
11
|
ValidationError,
|
|
12
12
|
InvalidBindingError,
|
|
13
13
|
EventBusClosedError,
|
|
14
|
+
AsyncResolutionError,
|
|
14
15
|
)
|
|
15
16
|
from .api import (
|
|
16
17
|
component,
|
|
@@ -51,6 +52,7 @@ __all__ = [
|
|
|
51
52
|
"SerializationError",
|
|
52
53
|
"ValidationError",
|
|
53
54
|
"InvalidBindingError",
|
|
55
|
+
"AsyncResolutionError",
|
|
54
56
|
"EventBusClosedError",
|
|
55
57
|
"component",
|
|
56
58
|
"factory",
|
pico_ioc/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '2.0.
|
|
1
|
+
__version__ = '2.0.4'
|
pico_ioc/api.py
CHANGED
|
@@ -409,14 +409,24 @@ def _resolve_args(callable_obj: Callable[..., Any], pico: "PicoContainer") -> Di
|
|
|
409
409
|
try:
|
|
410
410
|
for kind, name, data in plan:
|
|
411
411
|
if kind == "key":
|
|
412
|
-
|
|
413
|
-
|
|
412
|
+
primary_key = data
|
|
413
|
+
tracer.note_param(primary_key, name)
|
|
414
|
+
try:
|
|
415
|
+
kwargs[name] = pico.get(primary_key)
|
|
416
|
+
except ProviderNotFoundError as first_error:
|
|
417
|
+
if primary_key != name:
|
|
418
|
+
try:
|
|
419
|
+
kwargs[name] = pico.get(name)
|
|
420
|
+
except ProviderNotFoundError:
|
|
421
|
+
raise first_error from None
|
|
422
|
+
else:
|
|
423
|
+
raise first_error from None
|
|
414
424
|
else:
|
|
415
425
|
vals = [pico.get(k) for k in data]
|
|
416
426
|
kwargs[name] = vals
|
|
417
427
|
finally:
|
|
418
428
|
tracer.restore_via(prev)
|
|
419
|
-
return
|
|
429
|
+
return kwargsresol
|
|
420
430
|
|
|
421
431
|
def _needs_async_configure(obj: Any) -> bool:
|
|
422
432
|
for _, m in inspect.getmembers(obj, predicate=inspect.ismethod):
|
|
@@ -574,7 +584,7 @@ class Registrar:
|
|
|
574
584
|
deferred.attach(pico, locator)
|
|
575
585
|
for key, md in list(self._metadata.items()):
|
|
576
586
|
if md.lazy:
|
|
577
|
-
original = self._factory.get(key)
|
|
587
|
+
original = self._factory.get(key, origin='lazy')
|
|
578
588
|
def lazy_proxy_provider(_orig=original, _p=pico):
|
|
579
589
|
return UnifiedComponentProxy(container=_p, object_creator=_orig)
|
|
580
590
|
self._factory.bind(key, lazy_proxy_provider)
|
pico_ioc/container.py
CHANGED
|
@@ -4,7 +4,7 @@ import contextvars
|
|
|
4
4
|
from typing import Any, Dict, List, Optional, Tuple, overload, Union
|
|
5
5
|
from contextlib import contextmanager
|
|
6
6
|
from .constants import LOGGER, PICO_META
|
|
7
|
-
from .exceptions import CircularDependencyError, ComponentCreationError, ProviderNotFoundError
|
|
7
|
+
from .exceptions import CircularDependencyError, ComponentCreationError, ProviderNotFoundError, AsyncResolutionError
|
|
8
8
|
from .factory import ComponentFactory
|
|
9
9
|
from .locator import ComponentLocator
|
|
10
10
|
from .scope import ScopedCaches, ScopeManager
|
|
@@ -173,85 +173,88 @@ class PicoContainer:
|
|
|
173
173
|
return k
|
|
174
174
|
return key
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
def get(self, key: type) -> Any: ...
|
|
178
|
-
@overload
|
|
179
|
-
def get(self, key: str) -> Any: ...
|
|
180
|
-
def get(self, key: KeyT) -> Any:
|
|
176
|
+
def _resolve_or_create_internal(self, key: KeyT) -> Tuple[Any, float, bool]:
|
|
181
177
|
key = self._canonical_key(key)
|
|
182
178
|
cache = self._cache_for(key)
|
|
183
179
|
cached = cache.get(key)
|
|
184
180
|
if cached is not None:
|
|
185
181
|
self.context.cache_hit_count += 1
|
|
186
182
|
for o in self._observers: o.on_cache_hit(key)
|
|
187
|
-
return cached
|
|
183
|
+
return cached, 0.0, True
|
|
184
|
+
|
|
188
185
|
import time as _tm
|
|
189
186
|
t0 = _tm.perf_counter()
|
|
190
187
|
chain = list(_resolve_chain.get())
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
|
|
189
|
+
for k_in_chain in chain:
|
|
190
|
+
if k_in_chain == key:
|
|
193
191
|
details = self._tracer.describe_cycle(tuple(chain), key, self._locator)
|
|
194
192
|
raise ComponentCreationError(key, CircularDependencyError(chain, key, details=details))
|
|
193
|
+
|
|
195
194
|
token_chain = _resolve_chain.set(tuple(chain + [key]))
|
|
196
195
|
token_container = self.activate()
|
|
197
196
|
token_tracer = self._tracer.enter(key, via="provider")
|
|
197
|
+
|
|
198
|
+
requester = chain[-1] if chain else None
|
|
199
|
+
instance_or_awaitable = None
|
|
200
|
+
|
|
198
201
|
try:
|
|
199
|
-
provider = self._factory.get(key)
|
|
202
|
+
provider = self._factory.get(key, origin=requester)
|
|
200
203
|
try:
|
|
201
|
-
|
|
204
|
+
instance_or_awaitable = provider()
|
|
202
205
|
except ProviderNotFoundError as e:
|
|
203
206
|
raise
|
|
204
|
-
except Exception as
|
|
205
|
-
raise ComponentCreationError(key,
|
|
206
|
-
|
|
207
|
-
cache.put(key, instance)
|
|
208
|
-
self.context.resolve_count += 1
|
|
207
|
+
except Exception as creation_error:
|
|
208
|
+
raise ComponentCreationError(key, creation_error) from creation_error
|
|
209
|
+
|
|
209
210
|
took_ms = (_tm.perf_counter() - t0) * 1000
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
return instance_or_awaitable, took_ms, False
|
|
212
|
+
|
|
212
213
|
finally:
|
|
213
214
|
self._tracer.leave(token_tracer)
|
|
214
215
|
_resolve_chain.reset(token_chain)
|
|
215
216
|
self.deactivate(token_container)
|
|
217
|
+
|
|
218
|
+
@overload
|
|
219
|
+
def get(self, key: type) -> Any: ...
|
|
220
|
+
@overload
|
|
221
|
+
def get(self, key: str) -> Any: ...
|
|
222
|
+
def get(self, key: KeyT) -> Any:
|
|
223
|
+
instance_or_awaitable, took_ms, was_cached = self._resolve_or_create_internal(key)
|
|
224
|
+
|
|
225
|
+
if was_cached:
|
|
226
|
+
return instance_or_awaitable
|
|
227
|
+
|
|
228
|
+
instance = instance_or_awaitable
|
|
229
|
+
if inspect.isawaitable(instance):
|
|
230
|
+
key_name = getattr(key, '__name__', str(key))
|
|
231
|
+
raise AsyncResolutionError(key)
|
|
232
|
+
|
|
233
|
+
final_instance = self._maybe_wrap_with_aspects(key, instance)
|
|
234
|
+
cache = self._cache_for(key)
|
|
235
|
+
cache.put(key, final_instance)
|
|
236
|
+
self.context.resolve_count += 1
|
|
237
|
+
for o in self._observers: o.on_resolve(key, took_ms)
|
|
238
|
+
|
|
239
|
+
return final_instance
|
|
216
240
|
|
|
217
241
|
async def aget(self, key: KeyT) -> Any:
|
|
218
|
-
|
|
242
|
+
instance_or_awaitable, took_ms, was_cached = self._resolve_or_create_internal(key)
|
|
243
|
+
|
|
244
|
+
if was_cached:
|
|
245
|
+
return instance_or_awaitable
|
|
246
|
+
|
|
247
|
+
instance = instance_or_awaitable
|
|
248
|
+
if inspect.isawaitable(instance_or_awaitable):
|
|
249
|
+
instance = await instance_or_awaitable
|
|
250
|
+
|
|
251
|
+
final_instance = self._maybe_wrap_with_aspects(key, instance)
|
|
219
252
|
cache = self._cache_for(key)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
import time as _tm
|
|
226
|
-
t0 = _tm.perf_counter()
|
|
227
|
-
chain = list(_resolve_chain.get())
|
|
228
|
-
for k in chain:
|
|
229
|
-
if k == key:
|
|
230
|
-
details = self._tracer.describe_cycle(tuple(chain), key, self._locator)
|
|
231
|
-
raise ComponentCreationError(key, CircularDependencyError(chain, key, details=details))
|
|
232
|
-
token_chain = _resolve_chain.set(tuple(chain + [key]))
|
|
233
|
-
token_container = self.activate()
|
|
234
|
-
token_tracer = self._tracer.enter(key, via="provider")
|
|
235
|
-
try:
|
|
236
|
-
provider = self._factory.get(key)
|
|
237
|
-
try:
|
|
238
|
-
instance = provider()
|
|
239
|
-
if inspect.isawaitable(instance):
|
|
240
|
-
instance = await instance
|
|
241
|
-
except ProviderNotFoundError as e:
|
|
242
|
-
raise
|
|
243
|
-
except Exception as e:
|
|
244
|
-
raise ComponentCreationError(key, e) from e
|
|
245
|
-
instance = self._maybe_wrap_with_aspects(key, instance)
|
|
246
|
-
cache.put(key, instance)
|
|
247
|
-
self.context.resolve_count += 1
|
|
248
|
-
took_ms = (_tm.perf_counter() - t0) * 1000
|
|
249
|
-
for o in self._observers: o.on_resolve(key, took_ms)
|
|
250
|
-
return instance
|
|
251
|
-
finally:
|
|
252
|
-
self._tracer.leave(token_tracer)
|
|
253
|
-
_resolve_chain.reset(token_chain)
|
|
254
|
-
self.deactivate(token_container)
|
|
253
|
+
cache.put(key, final_instance)
|
|
254
|
+
self.context.resolve_count += 1
|
|
255
|
+
for o in self._observers: o.on_resolve(key, took_ms)
|
|
256
|
+
|
|
257
|
+
return final_instance
|
|
255
258
|
|
|
256
259
|
def _resolve_type_key(self, key: type):
|
|
257
260
|
if not self._locator:
|
pico_ioc/exceptions.py
CHANGED
|
@@ -4,9 +4,15 @@ class PicoError(Exception):
|
|
|
4
4
|
pass
|
|
5
5
|
|
|
6
6
|
class ProviderNotFoundError(PicoError):
|
|
7
|
-
def __init__(self, key: Any):
|
|
8
|
-
|
|
7
|
+
def __init__(self, key: Any, origin: Any | None = None):
|
|
8
|
+
key_name = getattr(key, '__name__', str(key))
|
|
9
|
+
origin_name = getattr(origin, '__name__', str(origin)) if origin else "init"
|
|
10
|
+
super().__init__(
|
|
11
|
+
f"Provider for key '{key_name}' not found "
|
|
12
|
+
f"(required by: '{origin_name}')"
|
|
13
|
+
)
|
|
9
14
|
self.key = key
|
|
15
|
+
self.origin = origin
|
|
10
16
|
|
|
11
17
|
class CircularDependencyError(PicoError):
|
|
12
18
|
def __init__(self, chain: Iterable[Any], current: Any, details: str | None = None, hint: str | None = None):
|
|
@@ -51,6 +57,15 @@ class InvalidBindingError(ValidationError):
|
|
|
51
57
|
super().__init__("Invalid bindings:\n" + "\n".join(f"- {e}" for e in errors))
|
|
52
58
|
self.errors = errors
|
|
53
59
|
|
|
60
|
+
class AsyncResolutionError(PicoError):
|
|
61
|
+
def __init__(self, key: Any):
|
|
62
|
+
key_name = getattr(key, '__name__', str(key))
|
|
63
|
+
super().__init__(
|
|
64
|
+
f"Synchronous get() received an awaitable for key '{key_name}'. "
|
|
65
|
+
"Use aget() instead."
|
|
66
|
+
)
|
|
67
|
+
self.key = key
|
|
68
|
+
|
|
54
69
|
class EventBusError(PicoError):
|
|
55
70
|
def __init__(self, msg: str):
|
|
56
71
|
super().__init__(msg)
|
pico_ioc/factory.py
CHANGED
|
@@ -28,9 +28,9 @@ class ComponentFactory:
|
|
|
28
28
|
self._providers[key] = provider
|
|
29
29
|
def has(self, key: KeyT) -> bool:
|
|
30
30
|
return key in self._providers
|
|
31
|
-
def get(self, key: KeyT) -> Provider:
|
|
31
|
+
def get(self, key: KeyT, origin: KeyT) -> Provider:
|
|
32
32
|
if key not in self._providers:
|
|
33
|
-
raise ProviderNotFoundError(key)
|
|
33
|
+
raise ProviderNotFoundError(key, origin)
|
|
34
34
|
return self._providers[key]
|
|
35
35
|
|
|
36
36
|
class DeferredProvider:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
pico_ioc/__init__.py,sha256=x7Sg0i4P0aXN3L5WH9IoTkyELPk-NQBdaRq8GuoZqO4,2294
|
|
2
|
+
pico_ioc/_version.py,sha256=YuAuFLBrpl-SUl_UsvW1U1NTjqnldfuD1xUmSLmtaRw,22
|
|
3
|
+
pico_ioc/aop.py,sha256=prFSlZC6vJYUfTbkMvlSc1T9UvvdEHr94Z0HAvjZ1fg,12985
|
|
4
|
+
pico_ioc/api.py,sha256=vcmdU_7HHlsHR50N6230ICICIO-AhQ6M0xs2AzYMVVk,47476
|
|
5
|
+
pico_ioc/config_runtime.py,sha256=z1cHDb5PbM8PMLYRFf5c2dmze8V22xwEzpWcBhtmMpA,11950
|
|
6
|
+
pico_ioc/constants.py,sha256=AhIt0ieDZ9Turxb_YbNzj11wUbBbzjKfWh1BDlSx2Nw,183
|
|
7
|
+
pico_ioc/container.py,sha256=-0o3T2uiKCnYVHTXP4MnUnA-atNeITjz0CtvUc_rY_E,17237
|
|
8
|
+
pico_ioc/event_bus.py,sha256=E8Qb8KZ6K1CuXSbMlG0MNPHkGoWlssLLPzHq1QYdADQ,8346
|
|
9
|
+
pico_ioc/exceptions.py,sha256=PnIY60r8O-eYXEVk7XPghz-hHNx_012ZLw9ikDwQhqQ,3019
|
|
10
|
+
pico_ioc/factory.py,sha256=RMJJrD91HJdb7R3C6y48Fnia7YRQnQuMuYTC2cIAm34,1579
|
|
11
|
+
pico_ioc/locator.py,sha256=PBxZYO_xCOxG7aJZ0adDtINrJass_ZDNYmPD2O_oNqM,2401
|
|
12
|
+
pico_ioc/scope.py,sha256=GDsDJWw7e5Vpiys-M4vQfKMJWSCiorRsT5cPo6z34Mk,5924
|
|
13
|
+
pico_ioc-2.0.4.dist-info/licenses/LICENSE,sha256=N1_nOvHTM6BobYnOTNXiQkroDqCEi6EzfGBv8lWtyZ0,1077
|
|
14
|
+
pico_ioc-2.0.4.dist-info/METADATA,sha256=tvJtJVxclklqQcQ9_k4cB4VPtsog2tWfngm_-z8TEa4,8741
|
|
15
|
+
pico_ioc-2.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
pico_ioc-2.0.4.dist-info/top_level.txt,sha256=_7_RLu616z_dtRw16impXn4Mw8IXe2J4BeX5912m5dQ,9
|
|
17
|
+
pico_ioc-2.0.4.dist-info/RECORD,,
|
pico_ioc-2.0.2.dist-info/RECORD
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
pico_ioc/__init__.py,sha256=mx1AVEZ-m0jntrWlMJGKQn_kaVVrKBY1NVYG8utT6yI,2240
|
|
2
|
-
pico_ioc/_version.py,sha256=kumiGImhzOTlTrRM-6jDo2mNnVHGO_2vxtrhB0nzAiw,22
|
|
3
|
-
pico_ioc/aop.py,sha256=prFSlZC6vJYUfTbkMvlSc1T9UvvdEHr94Z0HAvjZ1fg,12985
|
|
4
|
-
pico_ioc/api.py,sha256=mVS-Y4amJ_k-GPy9RUldpHNWDcEvj5ZLkf5DimdMzB4,47045
|
|
5
|
-
pico_ioc/config_runtime.py,sha256=z1cHDb5PbM8PMLYRFf5c2dmze8V22xwEzpWcBhtmMpA,11950
|
|
6
|
-
pico_ioc/constants.py,sha256=AhIt0ieDZ9Turxb_YbNzj11wUbBbzjKfWh1BDlSx2Nw,183
|
|
7
|
-
pico_ioc/container.py,sha256=5hLPwoVNY_PsN6XYbbZ6_j1I8IBnteCcahus1vCI_JY,17514
|
|
8
|
-
pico_ioc/event_bus.py,sha256=E8Qb8KZ6K1CuXSbMlG0MNPHkGoWlssLLPzHq1QYdADQ,8346
|
|
9
|
-
pico_ioc/exceptions.py,sha256=GT8flzyXeUWetguc8RRkB4p56waTXMdeNhSKQQ8rh4w,2468
|
|
10
|
-
pico_ioc/factory.py,sha256=Q3aLwZ-MWbXKjm8unr871vlWSeVUDmzFQZ1mXzPkY5I,1557
|
|
11
|
-
pico_ioc/locator.py,sha256=PBxZYO_xCOxG7aJZ0adDtINrJass_ZDNYmPD2O_oNqM,2401
|
|
12
|
-
pico_ioc/scope.py,sha256=GDsDJWw7e5Vpiys-M4vQfKMJWSCiorRsT5cPo6z34Mk,5924
|
|
13
|
-
pico_ioc-2.0.2.dist-info/licenses/LICENSE,sha256=N1_nOvHTM6BobYnOTNXiQkroDqCEi6EzfGBv8lWtyZ0,1077
|
|
14
|
-
pico_ioc-2.0.2.dist-info/METADATA,sha256=lLkoQGYiIygMjZ9TauYNSZJjRHyGjuaE4e19oUJemx4,8741
|
|
15
|
-
pico_ioc-2.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
-
pico_ioc-2.0.2.dist-info/top_level.txt,sha256=_7_RLu616z_dtRw16impXn4Mw8IXe2J4BeX5912m5dQ,9
|
|
17
|
-
pico_ioc-2.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|