python-injection 0.8.0__py3-none-any.whl → 0.8.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.
Potentially problematic release.
This version of python-injection might be problematic. Click here for more details.
- injection/_pkg.pyi +9 -4
- injection/common/event.py +1 -3
- injection/common/invertible.py +23 -0
- injection/common/lazy.py +6 -8
- injection/common/queue.py +7 -12
- injection/common/tools/threading.py +8 -23
- injection/core/module.py +58 -47
- {python_injection-0.8.0.dist-info → python_injection-0.8.1.dist-info}/METADATA +1 -1
- {python_injection-0.8.0.dist-info → python_injection-0.8.1.dist-info}/RECORD +10 -9
- {python_injection-0.8.0.dist-info → python_injection-0.8.1.dist-info}/WHEEL +0 -0
injection/_pkg.pyi
CHANGED
|
@@ -13,7 +13,7 @@ from typing import (
|
|
|
13
13
|
runtime_checkable,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
from injection.common.
|
|
16
|
+
from injection.common.invertible import Invertible
|
|
17
17
|
|
|
18
18
|
_T = TypeVar("_T")
|
|
19
19
|
|
|
@@ -104,11 +104,16 @@ class Module:
|
|
|
104
104
|
will be raised.
|
|
105
105
|
"""
|
|
106
106
|
|
|
107
|
-
def get_lazy_instance(
|
|
107
|
+
def get_lazy_instance(
|
|
108
|
+
self,
|
|
109
|
+
cls: type[_T],
|
|
110
|
+
cache: bool = ...,
|
|
111
|
+
) -> Invertible[_T | None]:
|
|
108
112
|
"""
|
|
109
113
|
Function used to retrieve an instance associated with the type passed in
|
|
110
|
-
parameter or `None`. Return a `
|
|
111
|
-
in
|
|
114
|
+
parameter or `None`. Return a `Invertible` object. To access the instance
|
|
115
|
+
contained in an invertible object, simply use a wavy line (~).
|
|
116
|
+
With `cache=True`, the instance retrieved will always be the same.
|
|
112
117
|
|
|
113
118
|
Example: instance = ~lazy_instance
|
|
114
119
|
"""
|
injection/common/event.py
CHANGED
|
@@ -4,8 +4,6 @@ from dataclasses import dataclass, field
|
|
|
4
4
|
from typing import ContextManager
|
|
5
5
|
from weakref import WeakSet
|
|
6
6
|
|
|
7
|
-
from injection.common.tools.threading import frozen_collection
|
|
8
|
-
|
|
9
7
|
__all__ = ("Event", "EventChannel", "EventListener")
|
|
10
8
|
|
|
11
9
|
|
|
@@ -28,7 +26,7 @@ class EventChannel:
|
|
|
28
26
|
@contextmanager
|
|
29
27
|
def dispatch(self, event: Event) -> ContextManager | ContextDecorator:
|
|
30
28
|
with ExitStack() as stack:
|
|
31
|
-
for listener in
|
|
29
|
+
for listener in tuple(self.__listeners):
|
|
32
30
|
context_manager = listener.on_event(event)
|
|
33
31
|
|
|
34
32
|
if context_manager is None:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Protocol, TypeVar, runtime_checkable
|
|
5
|
+
|
|
6
|
+
__all__ = ("Invertible", "SimpleInvertible")
|
|
7
|
+
|
|
8
|
+
_T = TypeVar("_T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class Invertible(Protocol[_T]):
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def __invert__(self) -> _T:
|
|
15
|
+
raise NotImplementedError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
19
|
+
class SimpleInvertible(Invertible[_T]):
|
|
20
|
+
callable: Callable[[], _T]
|
|
21
|
+
|
|
22
|
+
def __invert__(self) -> _T:
|
|
23
|
+
return self.callable()
|
injection/common/lazy.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from collections.abc import Callable, Iterator, Mapping
|
|
2
2
|
from types import MappingProxyType
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import TypeVar
|
|
4
4
|
|
|
5
|
-
from injection.common.
|
|
5
|
+
from injection.common.invertible import Invertible
|
|
6
6
|
|
|
7
7
|
__all__ = ("Lazy", "LazyMapping")
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ _K = TypeVar("_K")
|
|
|
11
11
|
_V = TypeVar("_V")
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class Lazy(
|
|
14
|
+
class Lazy(Invertible[_T]):
|
|
15
15
|
__slots__ = ("__cache", "__is_set")
|
|
16
16
|
|
|
17
17
|
def __init__(self, factory: Callable[[], _T]):
|
|
@@ -25,18 +25,16 @@ class Lazy(Generic[_T]):
|
|
|
25
25
|
return self.__is_set
|
|
26
26
|
|
|
27
27
|
def __setup_cache(self, factory: Callable[[], _T]):
|
|
28
|
-
def
|
|
29
|
-
with thread_lock:
|
|
30
|
-
self.__is_set = True
|
|
31
|
-
|
|
28
|
+
def cache_generator() -> Iterator[_T]:
|
|
32
29
|
nonlocal factory
|
|
33
30
|
cached = factory()
|
|
31
|
+
self.__is_set = True
|
|
34
32
|
del factory
|
|
35
33
|
|
|
36
34
|
while True:
|
|
37
35
|
yield cached
|
|
38
36
|
|
|
39
|
-
self.__cache =
|
|
37
|
+
self.__cache = cache_generator()
|
|
40
38
|
self.__is_set = False
|
|
41
39
|
|
|
42
40
|
|
injection/common/queue.py
CHANGED
|
@@ -4,8 +4,6 @@ from collections.abc import Iterator
|
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from typing import NoReturn, Protocol, TypeVar
|
|
6
6
|
|
|
7
|
-
from injection.common.tools.threading import thread_lock
|
|
8
|
-
|
|
9
7
|
__all__ = ("LimitedQueue",)
|
|
10
8
|
|
|
11
9
|
_T = TypeVar("_T")
|
|
@@ -34,7 +32,7 @@ class SimpleQueue(Queue[_T]):
|
|
|
34
32
|
return self
|
|
35
33
|
|
|
36
34
|
|
|
37
|
-
class
|
|
35
|
+
class DeadQueue(Queue[_T]):
|
|
38
36
|
__slots__ = ()
|
|
39
37
|
|
|
40
38
|
def __bool__(self) -> bool:
|
|
@@ -44,25 +42,22 @@ class NoQueue(Queue[_T]):
|
|
|
44
42
|
raise StopIteration
|
|
45
43
|
|
|
46
44
|
def add(self, item: _T) -> NoReturn:
|
|
47
|
-
raise TypeError("Queue
|
|
45
|
+
raise TypeError("Queue is dead.")
|
|
48
46
|
|
|
49
47
|
|
|
50
48
|
@dataclass(repr=False, slots=True)
|
|
51
49
|
class LimitedQueue(Queue[_T]):
|
|
52
|
-
|
|
50
|
+
__state: Queue[_T] = field(default_factory=SimpleQueue)
|
|
53
51
|
|
|
54
52
|
def __next__(self) -> _T:
|
|
55
|
-
if not self.__queue:
|
|
56
|
-
raise StopIteration
|
|
57
|
-
|
|
58
53
|
try:
|
|
59
|
-
return next(self.
|
|
54
|
+
return next(self.__state)
|
|
60
55
|
except StopIteration as exc:
|
|
61
|
-
|
|
62
|
-
self.
|
|
56
|
+
if self.__state:
|
|
57
|
+
self.__state = DeadQueue()
|
|
63
58
|
|
|
64
59
|
raise exc
|
|
65
60
|
|
|
66
61
|
def add(self, item: _T):
|
|
67
|
-
self.
|
|
62
|
+
self.__state.add(item)
|
|
68
63
|
return self
|
|
@@ -1,28 +1,13 @@
|
|
|
1
|
-
from
|
|
2
|
-
from functools import wraps
|
|
1
|
+
from contextlib import ContextDecorator, contextmanager
|
|
3
2
|
from threading import RLock
|
|
4
|
-
from typing import
|
|
3
|
+
from typing import ContextManager
|
|
5
4
|
|
|
6
|
-
__all__ = ("
|
|
5
|
+
__all__ = ("synchronized",)
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
thread_lock = RLock()
|
|
7
|
+
__lock = RLock()
|
|
10
8
|
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
with thread_lock:
|
|
17
|
-
return fn(*args, **kwargs)
|
|
18
|
-
|
|
19
|
-
return wrapper
|
|
20
|
-
|
|
21
|
-
return decorator(function) if function else decorator
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def frozen_collection(collection: Collection[_T]) -> Iterator[_T]:
|
|
25
|
-
with thread_lock:
|
|
26
|
-
t = tuple(collection)
|
|
27
|
-
|
|
28
|
-
yield from t
|
|
10
|
+
@contextmanager
|
|
11
|
+
def synchronized() -> ContextManager | ContextDecorator:
|
|
12
|
+
with __lock:
|
|
13
|
+
yield
|
injection/core/module.py
CHANGED
|
@@ -37,13 +37,10 @@ from typing import (
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
from injection.common.event import Event, EventChannel, EventListener
|
|
40
|
+
from injection.common.invertible import Invertible, SimpleInvertible
|
|
40
41
|
from injection.common.lazy import Lazy, LazyMapping
|
|
41
42
|
from injection.common.queue import LimitedQueue
|
|
42
|
-
from injection.common.tools.threading import
|
|
43
|
-
frozen_collection,
|
|
44
|
-
synchronized,
|
|
45
|
-
thread_lock,
|
|
46
|
-
)
|
|
43
|
+
from injection.common.tools.threading import synchronized
|
|
47
44
|
from injection.common.tools.type import find_types, format_type, get_origins
|
|
48
45
|
from injection.exceptions import (
|
|
49
46
|
InjectionError,
|
|
@@ -161,6 +158,13 @@ class Injectable(Protocol[_T]):
|
|
|
161
158
|
raise NotImplementedError
|
|
162
159
|
|
|
163
160
|
|
|
161
|
+
class FallbackInjectable(Injectable[_T], ABC):
|
|
162
|
+
__slots__ = ()
|
|
163
|
+
|
|
164
|
+
def __bool__(self) -> bool:
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
|
|
164
168
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
165
169
|
class BaseInjectable(Injectable[_T], ABC):
|
|
166
170
|
factory: Callable[[], _T]
|
|
@@ -193,7 +197,7 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
193
197
|
with suppress(KeyError):
|
|
194
198
|
return self.cache[self.__INSTANCE_KEY]
|
|
195
199
|
|
|
196
|
-
with
|
|
200
|
+
with synchronized():
|
|
197
201
|
instance = self.factory()
|
|
198
202
|
self.cache[self.__INSTANCE_KEY] = instance
|
|
199
203
|
|
|
@@ -201,12 +205,9 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
201
205
|
|
|
202
206
|
|
|
203
207
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
204
|
-
class ShouldBeInjectable(
|
|
208
|
+
class ShouldBeInjectable(FallbackInjectable[_T]):
|
|
205
209
|
cls: type[_T]
|
|
206
210
|
|
|
207
|
-
def __bool__(self) -> bool:
|
|
208
|
-
return False
|
|
209
|
-
|
|
210
211
|
def get_instance(self) -> NoReturn:
|
|
211
212
|
raise InjectionError(f"`{format_type(self.cls)}` should be an injectable.")
|
|
212
213
|
|
|
@@ -264,36 +265,38 @@ class Container(Broker):
|
|
|
264
265
|
|
|
265
266
|
@property
|
|
266
267
|
def __classes(self) -> frozenset[type]:
|
|
267
|
-
return frozenset(self.__data
|
|
268
|
+
return frozenset(self.__data)
|
|
268
269
|
|
|
269
270
|
@property
|
|
270
271
|
def __injectables(self) -> frozenset[Injectable]:
|
|
271
272
|
return frozenset(self.__data.values())
|
|
272
273
|
|
|
274
|
+
@synchronized()
|
|
273
275
|
def update(self, classes: Iterable[type], injectable: Injectable, override: bool):
|
|
274
276
|
classes = frozenset(get_origins(*classes))
|
|
275
277
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
override = True
|
|
278
|
+
if not injectable:
|
|
279
|
+
classes -= self.__classes
|
|
280
|
+
override = True
|
|
280
281
|
|
|
281
|
-
|
|
282
|
-
|
|
282
|
+
if classes:
|
|
283
|
+
event = ContainerDependenciesUpdated(self, classes, override)
|
|
283
284
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
with self.notify(event):
|
|
286
|
+
if not override:
|
|
287
|
+
self.__check_if_exists(classes)
|
|
287
288
|
|
|
288
|
-
|
|
289
|
+
self.__data.update((cls, injectable) for cls in classes)
|
|
289
290
|
|
|
290
291
|
return self
|
|
291
292
|
|
|
292
|
-
@synchronized
|
|
293
|
+
@synchronized()
|
|
293
294
|
def unlock(self):
|
|
294
295
|
for injectable in self.__injectables:
|
|
295
296
|
injectable.unlock()
|
|
296
297
|
|
|
298
|
+
return self
|
|
299
|
+
|
|
297
300
|
def add_listener(self, listener: EventListener):
|
|
298
301
|
self.__channel.add_listener(listener)
|
|
299
302
|
return self
|
|
@@ -360,7 +363,7 @@ class Module(EventListener, Broker):
|
|
|
360
363
|
|
|
361
364
|
@property
|
|
362
365
|
def __brokers(self) -> Iterator[Broker]:
|
|
363
|
-
yield from
|
|
366
|
+
yield from tuple(self.__modules)
|
|
364
367
|
yield self.__container
|
|
365
368
|
|
|
366
369
|
def injectable(
|
|
@@ -421,7 +424,7 @@ class Module(EventListener, Broker):
|
|
|
421
424
|
|
|
422
425
|
function = InjectedFunction(wp)
|
|
423
426
|
|
|
424
|
-
@function.
|
|
427
|
+
@function.on_setup
|
|
425
428
|
def listen():
|
|
426
429
|
function.update(self)
|
|
427
430
|
self.add_listener(function)
|
|
@@ -442,8 +445,17 @@ class Module(EventListener, Broker):
|
|
|
442
445
|
instance = injectable.get_instance()
|
|
443
446
|
return cast(cls, instance)
|
|
444
447
|
|
|
445
|
-
def get_lazy_instance(
|
|
446
|
-
|
|
448
|
+
def get_lazy_instance(
|
|
449
|
+
self,
|
|
450
|
+
cls: type[_T],
|
|
451
|
+
cache: bool = False,
|
|
452
|
+
) -> Invertible[_T | None]:
|
|
453
|
+
if cache:
|
|
454
|
+
return Lazy(lambda: self.get_instance(cls))
|
|
455
|
+
|
|
456
|
+
function = self.inject(lambda instance=None: instance)
|
|
457
|
+
function.set_owner(cls)
|
|
458
|
+
return SimpleInvertible(function)
|
|
447
459
|
|
|
448
460
|
def update(
|
|
449
461
|
self,
|
|
@@ -502,11 +514,13 @@ class Module(EventListener, Broker):
|
|
|
502
514
|
|
|
503
515
|
return self
|
|
504
516
|
|
|
505
|
-
@synchronized
|
|
517
|
+
@synchronized()
|
|
506
518
|
def unlock(self):
|
|
507
519
|
for broker in self.__brokers:
|
|
508
520
|
broker.unlock()
|
|
509
521
|
|
|
522
|
+
return self
|
|
523
|
+
|
|
510
524
|
def add_listener(self, listener: EventListener):
|
|
511
525
|
self.__channel.add_listener(listener)
|
|
512
526
|
return self
|
|
@@ -640,7 +654,7 @@ class InjectedFunction(EventListener):
|
|
|
640
654
|
self.__dependencies = Dependencies.empty()
|
|
641
655
|
self.__owner = None
|
|
642
656
|
self.__setup_queue = LimitedQueue[Callable[[], Any]]()
|
|
643
|
-
self.
|
|
657
|
+
self.on_setup(
|
|
644
658
|
lambda: self.__set_signature(
|
|
645
659
|
inspect.signature(
|
|
646
660
|
wrapped,
|
|
@@ -665,15 +679,7 @@ class InjectedFunction(EventListener):
|
|
|
665
679
|
return self.__wrapper.__get__(instance, owner)
|
|
666
680
|
|
|
667
681
|
def __set_name__(self, owner: type, name: str):
|
|
668
|
-
|
|
669
|
-
raise TypeError(
|
|
670
|
-
"Function owner must be assigned before dependencies are resolved."
|
|
671
|
-
)
|
|
672
|
-
|
|
673
|
-
if self.__owner:
|
|
674
|
-
raise TypeError("Function owner is already defined.")
|
|
675
|
-
|
|
676
|
-
self.__owner = owner
|
|
682
|
+
self.set_owner(owner)
|
|
677
683
|
|
|
678
684
|
@property
|
|
679
685
|
def signature(self) -> Signature:
|
|
@@ -696,17 +702,24 @@ class InjectedFunction(EventListener):
|
|
|
696
702
|
)
|
|
697
703
|
return Arguments(bound.args, bound.kwargs)
|
|
698
704
|
|
|
699
|
-
def
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
module,
|
|
704
|
-
self.__owner,
|
|
705
|
+
def set_owner(self, owner: type):
|
|
706
|
+
if self.__dependencies.are_resolved:
|
|
707
|
+
raise TypeError(
|
|
708
|
+
"Function owner must be assigned before dependencies are resolved."
|
|
705
709
|
)
|
|
706
710
|
|
|
711
|
+
if self.__owner:
|
|
712
|
+
raise TypeError("Function owner is already defined.")
|
|
713
|
+
|
|
714
|
+
self.__owner = owner
|
|
707
715
|
return self
|
|
708
716
|
|
|
709
|
-
|
|
717
|
+
@synchronized()
|
|
718
|
+
def update(self, module: Module):
|
|
719
|
+
self.__dependencies = Dependencies.resolve(self.signature, module, self.__owner)
|
|
720
|
+
return self
|
|
721
|
+
|
|
722
|
+
def on_setup(self, wrapped: Callable[[], Any] = None, /):
|
|
710
723
|
def decorator(wp):
|
|
711
724
|
self.__setup_queue.add(wp)
|
|
712
725
|
return wp
|
|
@@ -730,7 +743,5 @@ class InjectedFunction(EventListener):
|
|
|
730
743
|
return self
|
|
731
744
|
|
|
732
745
|
def __set_signature(self, signature: Signature):
|
|
733
|
-
|
|
734
|
-
self.__signature__ = signature
|
|
735
|
-
|
|
746
|
+
self.__signature__ = signature
|
|
736
747
|
return self
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
injection/__init__.py,sha256=9_AVJILxKIBiL_6KJSh2RRydgSHXH38uahAGD0S1-dI,20
|
|
2
2
|
injection/_pkg.py,sha256=nMIRLAQG6096bcR2Mz1egxe5FItBUsw8zR9yzjt1HDM,647
|
|
3
|
-
injection/_pkg.pyi,sha256=
|
|
3
|
+
injection/_pkg.pyi,sha256=7ep6m-8XK4H7XIFkael2UMIG01qYw73zjdv5DtEBwes,5327
|
|
4
4
|
injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
injection/common/event.py,sha256=
|
|
6
|
-
injection/common/
|
|
7
|
-
injection/common/
|
|
5
|
+
injection/common/event.py,sha256=uFoGRnxxkohH53JEStn4tN2Pn79HlgGExh7VkXdBwVQ,1316
|
|
6
|
+
injection/common/invertible.py,sha256=CyGp57Ik1pSQ2G7bRvnFWkY0kJkZDD5_19OjMYNvQes,558
|
|
7
|
+
injection/common/lazy.py,sha256=1C34uoG229Gl0DEUcD9-eQrL4K_oIofOLzdQ1SiY6rw,1401
|
|
8
|
+
injection/common/queue.py,sha256=mV0AGxp5aYMr438MxmoIsZcV5jmqi5x_GD2S-utrnzA,1443
|
|
8
9
|
injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
injection/common/tools/threading.py,sha256=
|
|
10
|
+
injection/common/tools/threading.py,sha256=02mBkIir2kQaXt47ewMJnOvhCldXCmPI2V-BW_l_6V8,271
|
|
10
11
|
injection/common/tools/type.py,sha256=-zL0dtoVZme71Mscvav7iEWxY2-JltzNTekbWOCPSFo,1276
|
|
11
12
|
injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
|
|
12
|
-
injection/core/module.py,sha256=
|
|
13
|
+
injection/core/module.py,sha256=hYnNwFNzw-1TaTjPAtmCpKgghF1JTdbkrJ9jO07KQps,19284
|
|
13
14
|
injection/exceptions.py,sha256=nE56jW00ZB1T-Z-dvfPczPShs3CwIc7tIvdYlOXlaXA,653
|
|
14
15
|
injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
16
|
injection/integrations/blacksheep.py,sha256=vcLil1IccS7JtXpuVu7s2LqN5Zravfe_7xpAt5cTIU0,723
|
|
16
17
|
injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
|
|
17
|
-
python_injection-0.8.
|
|
18
|
-
python_injection-0.8.
|
|
19
|
-
python_injection-0.8.
|
|
18
|
+
python_injection-0.8.1.dist-info/METADATA,sha256=Atf1DZFIwYCdMKnK7_xQf_muy_Ma7qQmEAnGYQr5P-M,3433
|
|
19
|
+
python_injection-0.8.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
20
|
+
python_injection-0.8.1.dist-info/RECORD,,
|
|
File without changes
|