python-injection 0.7.5__tar.gz → 0.8.0__tar.gz

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.

Files changed (19) hide show
  1. {python_injection-0.7.5 → python_injection-0.8.0}/PKG-INFO +1 -1
  2. {python_injection-0.7.5 → python_injection-0.8.0}/injection/common/lazy.py +19 -23
  3. python_injection-0.8.0/injection/common/queue.py +68 -0
  4. {python_injection-0.7.5 → python_injection-0.8.0}/injection/core/module.py +52 -13
  5. {python_injection-0.7.5 → python_injection-0.8.0}/pyproject.toml +1 -1
  6. {python_injection-0.7.5 → python_injection-0.8.0}/documentation/basic-usage.md +0 -0
  7. {python_injection-0.7.5 → python_injection-0.8.0}/injection/__init__.py +0 -0
  8. {python_injection-0.7.5 → python_injection-0.8.0}/injection/_pkg.py +0 -0
  9. {python_injection-0.7.5 → python_injection-0.8.0}/injection/_pkg.pyi +0 -0
  10. {python_injection-0.7.5 → python_injection-0.8.0}/injection/common/__init__.py +0 -0
  11. {python_injection-0.7.5 → python_injection-0.8.0}/injection/common/event.py +0 -0
  12. {python_injection-0.7.5 → python_injection-0.8.0}/injection/common/tools/__init__.py +0 -0
  13. {python_injection-0.7.5 → python_injection-0.8.0}/injection/common/tools/threading.py +0 -0
  14. {python_injection-0.7.5 → python_injection-0.8.0}/injection/common/tools/type.py +0 -0
  15. {python_injection-0.7.5 → python_injection-0.8.0}/injection/core/__init__.py +0 -0
  16. {python_injection-0.7.5 → python_injection-0.8.0}/injection/exceptions.py +0 -0
  17. {python_injection-0.7.5 → python_injection-0.8.0}/injection/integrations/__init__.py +0 -0
  18. {python_injection-0.7.5 → python_injection-0.8.0}/injection/integrations/blacksheep.py +0 -0
  19. {python_injection-0.7.5 → python_injection-0.8.0}/injection/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.7.5
3
+ Version: 0.8.0
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -1,7 +1,6 @@
1
1
  from collections.abc import Callable, Iterator, Mapping
2
- from contextlib import suppress
3
2
  from types import MappingProxyType
4
- from typing import Any, Generic, TypeVar
3
+ from typing import Generic, TypeVar
5
4
 
6
5
  from injection.common.tools.threading import thread_lock
7
6
 
@@ -13,35 +12,32 @@ _V = TypeVar("_V")
13
12
 
14
13
 
15
14
  class Lazy(Generic[_T]):
16
- __slots__ = ("is_set", "__generator")
15
+ __slots__ = ("__cache", "__is_set")
17
16
 
18
17
  def __init__(self, factory: Callable[[], _T]):
19
- def generator() -> Iterator[_T]:
20
- nonlocal factory
21
-
22
- with thread_lock:
23
- value = factory()
24
- self.is_set = True
25
- del factory
18
+ self.__setup_cache(factory)
26
19
 
27
- while True:
28
- yield value
20
+ def __invert__(self) -> _T:
21
+ return next(self.__cache)
29
22
 
30
- self.is_set = False
31
- self.__generator = generator()
23
+ @property
24
+ def is_set(self) -> bool:
25
+ return self.__is_set
32
26
 
33
- def __invert__(self) -> _T:
34
- return next(self.__generator)
27
+ def __setup_cache(self, factory: Callable[[], _T]):
28
+ def new_cache() -> Iterator[_T]:
29
+ with thread_lock:
30
+ self.__is_set = True
35
31
 
36
- def __call__(self) -> _T:
37
- return ~self
32
+ nonlocal factory
33
+ cached = factory()
34
+ del factory
38
35
 
39
- def __setattr__(self, name: str, value: Any, /):
40
- with suppress(AttributeError):
41
- if self.is_set:
42
- raise TypeError(f"`{self}` is frozen.")
36
+ while True:
37
+ yield cached
43
38
 
44
- return super().__setattr__(name, value)
39
+ self.__cache = new_cache()
40
+ self.__is_set = False
45
41
 
46
42
 
47
43
  class LazyMapping(Mapping[_K, _V]):
@@ -0,0 +1,68 @@
1
+ from abc import abstractmethod
2
+ from collections import deque
3
+ from collections.abc import Iterator
4
+ from dataclasses import dataclass, field
5
+ from typing import NoReturn, Protocol, TypeVar
6
+
7
+ from injection.common.tools.threading import thread_lock
8
+
9
+ __all__ = ("LimitedQueue",)
10
+
11
+ _T = TypeVar("_T")
12
+
13
+
14
+ class Queue(Iterator[_T], Protocol):
15
+ __slots__ = ()
16
+
17
+ @abstractmethod
18
+ def add(self, item: _T):
19
+ raise NotImplementedError
20
+
21
+
22
+ @dataclass(repr=False, frozen=True, slots=True)
23
+ class SimpleQueue(Queue[_T]):
24
+ __items: deque[_T] = field(default_factory=deque, init=False)
25
+
26
+ def __next__(self) -> _T:
27
+ try:
28
+ return self.__items.popleft()
29
+ except IndexError as exc:
30
+ raise StopIteration from exc
31
+
32
+ def add(self, item: _T):
33
+ self.__items.append(item)
34
+ return self
35
+
36
+
37
+ class NoQueue(Queue[_T]):
38
+ __slots__ = ()
39
+
40
+ def __bool__(self) -> bool:
41
+ return False
42
+
43
+ def __next__(self) -> NoReturn:
44
+ raise StopIteration
45
+
46
+ def add(self, item: _T) -> NoReturn:
47
+ raise TypeError("Queue doesn't exist.")
48
+
49
+
50
+ @dataclass(repr=False, slots=True)
51
+ class LimitedQueue(Queue[_T]):
52
+ __queue: Queue[_T] = field(default_factory=SimpleQueue)
53
+
54
+ def __next__(self) -> _T:
55
+ if not self.__queue:
56
+ raise StopIteration
57
+
58
+ try:
59
+ return next(self.__queue)
60
+ except StopIteration as exc:
61
+ with thread_lock:
62
+ self.__queue = NoQueue()
63
+
64
+ raise exc
65
+
66
+ def add(self, item: _T):
67
+ self.__queue.add(item)
68
+ return self
@@ -38,6 +38,7 @@ from typing import (
38
38
 
39
39
  from injection.common.event import Event, EventChannel, EventListener
40
40
  from injection.common.lazy import Lazy, LazyMapping
41
+ from injection.common.queue import LimitedQueue
41
42
  from injection.common.tools.threading import (
42
43
  frozen_collection,
43
44
  synchronized,
@@ -418,9 +419,14 @@ class Module(EventListener, Broker):
418
419
  wp.__init__ = self.inject(wp.__init__)
419
420
  return wp
420
421
 
421
- wrapper = InjectedFunction(wp).update(self)
422
- self.add_listener(wrapper)
423
- return wrapper
422
+ function = InjectedFunction(wp)
423
+
424
+ @function.setup
425
+ def listen():
426
+ function.update(self)
427
+ self.add_listener(function)
428
+
429
+ return function
424
430
 
425
431
  return decorator(wrapped) if wrapped else decorator
426
432
 
@@ -612,22 +618,36 @@ class Arguments(NamedTuple):
612
618
 
613
619
 
614
620
  class InjectedFunction(EventListener):
615
- __slots__ = ("__dict__", "__wrapper", "__dependencies", "__owner")
621
+ __slots__ = (
622
+ "__dict__",
623
+ "__signature__",
624
+ "__dependencies",
625
+ "__owner",
626
+ "__setup_queue",
627
+ "__wrapper",
628
+ )
616
629
 
617
630
  def __init__(self, wrapped: Callable[..., Any], /):
618
631
  update_wrapper(self, wrapped)
619
- self.__signature__ = Lazy[Signature](
620
- lambda: inspect.signature(wrapped, eval_str=True)
621
- )
622
632
 
623
633
  @wraps(wrapped)
624
634
  def wrapper(*args, **kwargs):
635
+ self.__consume_setup_queue()
625
636
  args, kwargs = self.bind(args, kwargs)
626
637
  return wrapped(*args, **kwargs)
627
638
 
628
639
  self.__wrapper = wrapper
629
640
  self.__dependencies = Dependencies.empty()
630
641
  self.__owner = None
642
+ self.__setup_queue = LimitedQueue[Callable[[], Any]]()
643
+ self.setup(
644
+ lambda: self.__set_signature(
645
+ inspect.signature(
646
+ wrapped,
647
+ eval_str=True,
648
+ )
649
+ )
650
+ )
631
651
 
632
652
  def __repr__(self) -> str:
633
653
  return repr(self.__wrapper)
@@ -638,7 +658,7 @@ class InjectedFunction(EventListener):
638
658
  def __call__(self, /, *args, **kwargs) -> Any:
639
659
  return self.__wrapper(*args, **kwargs)
640
660
 
641
- def __get__(self, instance: object | None, owner: type):
661
+ def __get__(self, instance: object = None, owner: type = None):
642
662
  if instance is None:
643
663
  return self
644
664
 
@@ -647,7 +667,7 @@ class InjectedFunction(EventListener):
647
667
  def __set_name__(self, owner: type, name: str):
648
668
  if self.__dependencies.are_resolved:
649
669
  raise TypeError(
650
- "`__set_name__` is called after dependencies have been resolved."
670
+ "Function owner must be assigned before dependencies are resolved."
651
671
  )
652
672
 
653
673
  if self.__owner:
@@ -657,7 +677,7 @@ class InjectedFunction(EventListener):
657
677
 
658
678
  @property
659
679
  def signature(self) -> Signature:
660
- return self.__signature__()
680
+ return self.__signature__
661
681
 
662
682
  def bind(
663
683
  self,
@@ -671,9 +691,9 @@ class InjectedFunction(EventListener):
671
691
  return Arguments(args, kwargs)
672
692
 
673
693
  bound = self.signature.bind_partial(*args, **kwargs)
674
- dependencies = self.__dependencies.arguments
675
- bound.arguments = dependencies | bound.arguments
676
-
694
+ bound.arguments = (
695
+ bound.arguments | self.__dependencies.arguments | bound.arguments
696
+ )
677
697
  return Arguments(bound.args, bound.kwargs)
678
698
 
679
699
  def update(self, module: Module):
@@ -686,6 +706,13 @@ class InjectedFunction(EventListener):
686
706
 
687
707
  return self
688
708
 
709
+ def setup(self, wrapped: Callable[[], Any] = None, /):
710
+ def decorator(wp):
711
+ self.__setup_queue.add(wp)
712
+ return wp
713
+
714
+ return decorator(wrapped) if wrapped else decorator
715
+
689
716
  @singledispatchmethod
690
717
  def on_event(self, event: Event, /):
691
718
  pass
@@ -695,3 +722,15 @@ class InjectedFunction(EventListener):
695
722
  def _(self, event: ModuleEvent, /) -> ContextManager:
696
723
  yield
697
724
  self.update(event.on_module)
725
+
726
+ def __consume_setup_queue(self):
727
+ for function in self.__setup_queue:
728
+ function()
729
+
730
+ return self
731
+
732
+ def __set_signature(self, signature: Signature):
733
+ with thread_lock:
734
+ self.__signature__ = signature
735
+
736
+ return self
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-injection"
3
- version = "0.7.5"
3
+ version = "0.8.0"
4
4
  description = "Fast and easy dependency injection framework."
5
5
  authors = ["remimd"]
6
6
  keywords = ["dependencies", "inject", "injection"]