pico-ioc 2.0.2__py3-none-any.whl → 2.0.3__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 +13 -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.3.dist-info}/METADATA +1 -1
- pico_ioc-2.0.3.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.3.dist-info}/WHEEL +0 -0
- {pico_ioc-2.0.2.dist-info → pico_ioc-2.0.3.dist-info}/licenses/LICENSE +0 -0
- {pico_ioc-2.0.2.dist-info → pico_ioc-2.0.3.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.3'
|
pico_ioc/api.py
CHANGED
|
@@ -9,7 +9,6 @@ from dataclasses import is_dataclass, fields, dataclass, MISSING
|
|
|
9
9
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union, get_args, get_origin, Annotated, Protocol, Mapping
|
|
10
10
|
from .constants import LOGGER, PICO_INFRA, PICO_NAME, PICO_KEY, PICO_META
|
|
11
11
|
from .exceptions import (
|
|
12
|
-
ProviderNotFoundError,
|
|
13
12
|
CircularDependencyError,
|
|
14
13
|
ScopeError,
|
|
15
14
|
ConfigurationError,
|
|
@@ -409,8 +408,18 @@ def _resolve_args(callable_obj: Callable[..., Any], pico: "PicoContainer") -> Di
|
|
|
409
408
|
try:
|
|
410
409
|
for kind, name, data in plan:
|
|
411
410
|
if kind == "key":
|
|
412
|
-
|
|
413
|
-
|
|
411
|
+
primary_key = data
|
|
412
|
+
tracer.note_param(primary_key, name)
|
|
413
|
+
try:
|
|
414
|
+
kwargs[name] = pico.get(primary_key)
|
|
415
|
+
except ProviderNotFoundError as first_error:
|
|
416
|
+
if primary_key != name:
|
|
417
|
+
try:
|
|
418
|
+
kwargs[name] = pico.get(name)
|
|
419
|
+
except ProviderNotFoundError:
|
|
420
|
+
raise first_error from None
|
|
421
|
+
else:
|
|
422
|
+
raise first_error from None
|
|
414
423
|
else:
|
|
415
424
|
vals = [pico.get(k) for k in data]
|
|
416
425
|
kwargs[name] = vals
|
|
@@ -574,7 +583,7 @@ class Registrar:
|
|
|
574
583
|
deferred.attach(pico, locator)
|
|
575
584
|
for key, md in list(self._metadata.items()):
|
|
576
585
|
if md.lazy:
|
|
577
|
-
original = self._factory.get(key)
|
|
586
|
+
original = self._factory.get(key, origin='lazy')
|
|
578
587
|
def lazy_proxy_provider(_orig=original, _p=pico):
|
|
579
588
|
return UnifiedComponentProxy(container=_p, object_creator=_orig)
|
|
580
589
|
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=fcRMgwI40iVFwre_3iKQA-N6sHUo3pAErXcKJS63DRg,22
|
|
3
|
+
pico_ioc/aop.py,sha256=prFSlZC6vJYUfTbkMvlSc1T9UvvdEHr94Z0HAvjZ1fg,12985
|
|
4
|
+
pico_ioc/api.py,sha256=rV8_xa6rjARHND_G1vGSNPBplzaaTV3qm3CEp9ntH54,47444
|
|
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.3.dist-info/licenses/LICENSE,sha256=N1_nOvHTM6BobYnOTNXiQkroDqCEi6EzfGBv8lWtyZ0,1077
|
|
14
|
+
pico_ioc-2.0.3.dist-info/METADATA,sha256=h7J1Q3I0qZUUqKPLWGrS59TM-yx74-qXjbYH47rn8t8,8741
|
|
15
|
+
pico_ioc-2.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
pico_ioc-2.0.3.dist-info/top_level.txt,sha256=_7_RLu616z_dtRw16impXn4Mw8IXe2J4BeX5912m5dQ,9
|
|
17
|
+
pico_ioc-2.0.3.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
|