python-injection 0.9.0__py3-none-any.whl → 0.9.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.
injection/__init__.py CHANGED
@@ -7,7 +7,6 @@ __all__ = (
7
7
  "InjectableMode",
8
8
  "Module",
9
9
  "ModulePriority",
10
- "default_module",
11
10
  "get_instance",
12
11
  "get_lazy_instance",
13
12
  "inject",
@@ -17,12 +16,14 @@ __all__ = (
17
16
  "singleton",
18
17
  )
19
18
 
20
- default_module = Module(f"{__name__}:default_module")
19
+ _module = Module.default()
21
20
 
22
- get_instance = default_module.get_instance
23
- get_lazy_instance = default_module.get_lazy_instance
24
- inject = default_module.inject
25
- injectable = default_module.injectable
26
- set_constant = default_module.set_constant
27
- should_be_injectable = default_module.should_be_injectable
28
- singleton = default_module.singleton
21
+ get_instance = _module.get_instance
22
+ get_lazy_instance = _module.get_lazy_instance
23
+ inject = _module.inject
24
+ injectable = _module.injectable
25
+ set_constant = _module.set_constant
26
+ should_be_injectable = _module.should_be_injectable
27
+ singleton = _module.singleton
28
+
29
+ del _module
injection/__init__.pyi CHANGED
@@ -6,7 +6,6 @@ from types import UnionType
6
6
  from typing import (
7
7
  Any,
8
8
  ContextManager,
9
- Final,
10
9
  Protocol,
11
10
  Self,
12
11
  final,
@@ -19,15 +18,17 @@ from .core import InjectableFactory
19
18
  from .core import ModeStr as InjectableModeStr
20
19
  from .core import PriorityStr as ModulePriorityStr
21
20
 
22
- default_module: Final[Module] = ...
21
+ _module: Module = ...
23
22
 
24
- get_instance = default_module.get_instance
25
- get_lazy_instance = default_module.get_lazy_instance
26
- inject = default_module.inject
27
- injectable = default_module.injectable
28
- set_constant = default_module.set_constant
29
- should_be_injectable = default_module.should_be_injectable
30
- singleton = default_module.singleton
23
+ get_instance = _module.get_instance
24
+ get_lazy_instance = _module.get_lazy_instance
25
+ inject = _module.inject
26
+ injectable = _module.injectable
27
+ set_constant = _module.set_constant
28
+ should_be_injectable = _module.should_be_injectable
29
+ singleton = _module.singleton
30
+
31
+ del _module
31
32
 
32
33
  @final
33
34
  class Module:
@@ -178,6 +179,18 @@ class Module:
178
179
  Function to unlock the module by deleting cached instances of singletons.
179
180
  """
180
181
 
182
+ @classmethod
183
+ def from_name(cls, name: str) -> Self:
184
+ """
185
+ Class method for getting or creating a module by name.
186
+ """
187
+
188
+ @classmethod
189
+ def default(cls) -> Self:
190
+ """
191
+ Class method for getting the default module.
192
+ """
193
+
181
194
  @final
182
195
  class ModulePriority(StrEnum):
183
196
  LOW = ...
@@ -6,5 +6,7 @@ __all__ = ("synchronized",)
6
6
 
7
7
  @contextmanager
8
8
  def synchronized():
9
- with RLock():
10
- yield
9
+ lock = RLock()
10
+
11
+ with lock:
12
+ yield lock
@@ -21,11 +21,13 @@ class TypeReport[T](NamedTuple):
21
21
  args: tuple[Any, ...]
22
22
 
23
23
  @property
24
- def cls(self) -> type[T]:
25
- if self.args:
26
- return self.origin[*self.args]
24
+ def type(self) -> type[T]:
25
+ origin = self.origin
26
+
27
+ if args := self.args:
28
+ return origin[*args]
27
29
 
28
- return self.origin
30
+ return origin
29
31
 
30
32
  @property
31
33
  def no_args(self) -> Self:
injection/core/module.py CHANGED
@@ -78,10 +78,10 @@ class ContainerDependenciesUpdated(ContainerEvent):
78
78
 
79
79
  def __str__(self) -> str:
80
80
  length = len(self.reports)
81
- formatted_classes = ", ".join(f"`{report.cls}`" for report in self.reports)
81
+ formatted_types = ", ".join(f"`{report.type}`" for report in self.reports)
82
82
  return (
83
- f"{length} container dependenc{'ies' if length > 1 else 'y'} have been "
84
- f"updated{f': {formatted_classes}' if formatted_classes else ''}."
83
+ f"{length} container dependenc{'ies' if length > 1 else 'y'} have "
84
+ f"been updated{f': {formatted_types}' if formatted_types else ''}."
85
85
  )
86
86
 
87
87
 
@@ -174,7 +174,7 @@ class NewInjectable[T](BaseInjectable[T]):
174
174
  class SingletonInjectable[T](BaseInjectable[T]):
175
175
  __slots__ = ("__dict__",)
176
176
 
177
- __INSTANCE_KEY: ClassVar[str] = "$instance"
177
+ __key: ClassVar[str] = "$instance"
178
178
 
179
179
  @property
180
180
  def cache(self) -> MutableMapping[str, Any]:
@@ -182,18 +182,18 @@ class SingletonInjectable[T](BaseInjectable[T]):
182
182
 
183
183
  @property
184
184
  def is_locked(self) -> bool:
185
- return self.__INSTANCE_KEY in self.cache
185
+ return self.__key in self.cache
186
186
 
187
187
  def unlock(self):
188
188
  self.cache.clear()
189
189
 
190
190
  def get_instance(self) -> T:
191
191
  with suppress(KeyError):
192
- return self.cache[self.__INSTANCE_KEY]
192
+ return self.cache[self.__key]
193
193
 
194
194
  with synchronized():
195
195
  instance = self.factory()
196
- self.cache[self.__INSTANCE_KEY] = instance
196
+ self.cache[self.__key] = instance
197
197
 
198
198
  return instance
199
199
 
@@ -340,7 +340,7 @@ class Container(Broker):
340
340
  else:
341
341
  if mode == current_mode and mode != Mode.OVERRIDE:
342
342
  raise RuntimeError(
343
- f"An injectable already exists for the class `{report.cls}`."
343
+ f"An injectable already exists for the class `{report.type}`."
344
344
  )
345
345
 
346
346
  elif rank < current_mode.rank:
@@ -368,16 +368,27 @@ type PriorityStr = Literal["low", "high"]
368
368
  type InjectableFactory[T] = Callable[[Callable[..., T]], Injectable[T]]
369
369
 
370
370
 
371
- @dataclass(repr=False, eq=False, frozen=True, slots=True)
371
+ @dataclass(eq=False, frozen=True, slots=True)
372
372
  class Module(EventListener, Broker):
373
373
  name: str | None = field(default=None)
374
- __channel: EventChannel = field(default_factory=EventChannel, init=False)
375
- __container: Container = field(default_factory=Container, init=False)
374
+ __channel: EventChannel = field(
375
+ default_factory=EventChannel,
376
+ init=False,
377
+ repr=False,
378
+ )
379
+ __container: Container = field(
380
+ default_factory=Container,
381
+ init=False,
382
+ repr=False,
383
+ )
376
384
  __modules: OrderedDict[Module, None] = field(
377
385
  default_factory=OrderedDict,
378
386
  init=False,
387
+ repr=False,
379
388
  )
380
389
 
390
+ __instances: ClassVar[dict[str, Module]] = {}
391
+
381
392
  def __post_init__(self):
382
393
  self.__container.add_listener(self)
383
394
 
@@ -394,9 +405,6 @@ class Module(EventListener, Broker):
394
405
  def __contains__(self, cls: type | UnionType, /) -> bool:
395
406
  return any(cls in broker for broker in self.__brokers)
396
407
 
397
- def __str__(self) -> str:
398
- return self.name or object.__str__(self)
399
-
400
408
  @property
401
409
  def is_locked(self) -> bool:
402
410
  return any(broker.is_locked for broker in self.__brokers)
@@ -468,7 +476,7 @@ class Module(EventListener, Broker):
468
476
 
469
477
  function = InjectedFunction(wp)
470
478
 
471
- @function.on_setup
479
+ @function.on_setup(block=False)
472
480
  def listen():
473
481
  function.update(self)
474
482
  self.add_listener(function)
@@ -611,6 +619,19 @@ class Module(EventListener, Broker):
611
619
  f"`{module}` can't be found in the modules used by `{self}`."
612
620
  ) from exc
613
621
 
622
+ @classmethod
623
+ def from_name(cls, name: str) -> Self:
624
+ with suppress(KeyError):
625
+ return cls.__instances[name]
626
+
627
+ instance = cls(name)
628
+ cls.__instances[name] = instance
629
+ return instance
630
+
631
+ @classmethod
632
+ def default(cls) -> Self:
633
+ return cls.from_name("default")
634
+
614
635
 
615
636
  """
616
637
  InjectedFunction
@@ -641,7 +662,7 @@ class Dependencies:
641
662
 
642
663
  @classmethod
643
664
  def from_mapping(cls, mapping: Mapping[str, Injectable]) -> Self:
644
- return cls(mapping=mapping)
665
+ return cls(mapping)
645
666
 
646
667
  @classmethod
647
668
  def empty(cls) -> Self:
@@ -775,9 +796,9 @@ class InjectedFunction(EventListener):
775
796
  self.__dependencies = Dependencies.resolve(self.signature, module, self.__owner)
776
797
  return self
777
798
 
778
- def on_setup(self, wrapped: Callable[[], Any] = None, /):
799
+ def on_setup(self, wrapped: Callable[[], Any] = None, /, *, block: bool = True):
779
800
  def decorator(wp):
780
- self.__setup_queue.put(wp)
801
+ self.__setup_queue.put(wp, block=block)
781
802
  return wp
782
803
 
783
804
  return decorator(wrapped) if wrapped else decorator
@@ -2,7 +2,7 @@ from typing import Any
2
2
 
3
3
  from rodi import ContainerProtocol
4
4
 
5
- from injection import Module, default_module
5
+ from injection import Module
6
6
 
7
7
  __all__ = ("InjectionServices",)
8
8
 
@@ -14,8 +14,8 @@ class InjectionServices(ContainerProtocol):
14
14
 
15
15
  __slots__ = ("__module",)
16
16
 
17
- def __init__(self, module: Module = default_module):
18
- self.__module = module
17
+ def __init__(self, module: Module = None):
18
+ self.__module = module or Module.default()
19
19
 
20
20
  def __contains__(self, item: Any) -> bool:
21
21
  return item in self.__module
@@ -0,0 +1,38 @@
1
+ from contextlib import contextmanager
2
+
3
+ from injection import Module, ModulePriority
4
+
5
+ __all__ = (
6
+ "set_test_constant",
7
+ "should_be_test_injectable",
8
+ "test_injectable",
9
+ "test_singleton",
10
+ "use_test_injectables",
11
+ )
12
+
13
+
14
+ def get_test_module() -> Module:
15
+ return Module.from_name("testing")
16
+
17
+
18
+ _module = get_test_module()
19
+
20
+ set_test_constant = _module.set_constant
21
+ should_be_test_injectable = _module.should_be_injectable
22
+ test_injectable = _module.injectable
23
+ test_singleton = _module.singleton
24
+
25
+ del _module
26
+
27
+
28
+ @contextmanager
29
+ def use_test_injectables(*, on: Module = None, test_module: Module = None):
30
+ on = on or Module.default()
31
+ test_module = test_module or get_test_module()
32
+
33
+ for module in (on, test_module):
34
+ module.unlock()
35
+
36
+ with on.use_temporarily(test_module, priority=ModulePriority.HIGH):
37
+ yield
38
+ on.unlock()
@@ -0,0 +1,22 @@
1
+ from contextlib import ContextDecorator
2
+ from typing import ContextManager
3
+
4
+ from injection import Module
5
+
6
+ _module: Module = ...
7
+
8
+ set_test_constant = _module.set_constant
9
+ should_be_test_injectable = _module.should_be_injectable
10
+ test_injectable = _module.injectable
11
+ test_singleton = _module.singleton
12
+
13
+ del _module
14
+
15
+ def use_test_injectables(
16
+ *,
17
+ on: Module = ...,
18
+ test_module: Module = ...,
19
+ ) -> ContextManager | ContextDecorator:
20
+ """
21
+ Context manager or decorator for temporary use test module.
22
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
15
15
 
16
16
  # Basic usage
17
17
 
18
- ## Create an injectable
18
+ ## Register an injectable
19
19
 
20
20
  > **Note**: If the class needs dependencies, these will be resolved when the instance is retrieved.
21
21
 
@@ -0,0 +1,21 @@
1
+ injection/__init__.py,sha256=LhfwYFMBw6tJ7XA_C353w61OPt4IuQQgs3zIjrwMIz8,654
2
+ injection/__init__.pyi,sha256=dfIQAR8L8Yr7dM-DODR4czwUd2a7zzFQHzb8mr1Q6P0,6235
3
+ injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ injection/common/event.py,sha256=5Rdb2m3vAMCic8cQAVkStJDbrDrW_lk6kav8wYwmexM,1283
5
+ injection/common/invertible.py,sha256=noNcmJ96IQi0XJms0dyfrx_AvKZnQM0sZyxhc2l6qo0,527
6
+ injection/common/lazy.py,sha256=FEas6ewwOGWvRR8cflmyVvSLZd_-Fxd0QeIdvAM5I9c,1313
7
+ injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ injection/common/tools/threading.py,sha256=HlvP6k_-eZaK8JbB2b9PP171IZVe_0W2oMYsw3ebdKA,187
9
+ injection/common/tools/type.py,sha256=ThM3Z1_gHmsdT4Jp7PlZgJY0lsufc4InPO65485Ugsg,1739
10
+ injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
11
+ injection/core/module.py,sha256=3FqU4XQgTNLI-VwpX1bKcRsLOaPeKSQ0sQuVsL_27I4,21625
12
+ injection/exceptions.py,sha256=RsWWiWwKSMU0vxXQqQSn6CKHLMrGu4SSzYUAy9OJRXk,626
13
+ injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ injection/integrations/blacksheep.py,sha256=82P_owhF3FKIJxxalnSic3AJnoOr1mkojkWMefpO6QI,731
15
+ injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ injection/testing/__init__.py,sha256=xglKmIJgg2j1wt8qWJ_TtGfFyKmn7xE7b8kQhD2yEkQ,864
17
+ injection/testing/__init__.pyi,sha256=_u95cJVHBkt69HUvnu2bbfYWmi7GOH_1qyFyvC1PxBk,518
18
+ injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
19
+ python_injection-0.9.1.dist-info/METADATA,sha256=6vDRDhsWCG-FbwiHs-pvslfia8Fpe2n7hWJPoxJJbhg,3572
20
+ python_injection-0.9.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
21
+ python_injection-0.9.1.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- injection/__init__.py,sha256=Bf6S99E2srD3752xlJf3uAdiGIzY2YHOZafcwEiwY70,739
2
- injection/__init__.pyi,sha256=HEk1HXAmwtq2ZEFLf5A7VWhdpPVWVQpjsLip9JzepEY,6023
3
- injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- injection/common/event.py,sha256=5Rdb2m3vAMCic8cQAVkStJDbrDrW_lk6kav8wYwmexM,1283
5
- injection/common/invertible.py,sha256=noNcmJ96IQi0XJms0dyfrx_AvKZnQM0sZyxhc2l6qo0,527
6
- injection/common/lazy.py,sha256=FEas6ewwOGWvRR8cflmyVvSLZd_-Fxd0QeIdvAM5I9c,1313
7
- injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- injection/common/tools/threading.py,sha256=Urcy59Rp34psRWmzzQyNDu4Zm-DuCkPsp4qO1x_W4qQ,165
9
- injection/common/tools/type.py,sha256=5Ixswd9-sSqJdmh9lTd3jADFHWgD0wZSwecVi1opeO0,1715
10
- injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
11
- injection/core/module.py,sha256=nCa10YQu-o-W0jMt-EnjCuXc1VioqN5CZXK2EoS4zgo,21241
12
- injection/exceptions.py,sha256=RsWWiWwKSMU0vxXQqQSn6CKHLMrGu4SSzYUAy9OJRXk,626
13
- injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- injection/integrations/blacksheep.py,sha256=GQBZCyl_DNDuCJdV56EYaiYCXGDMmP66cEahRzUZpmc,737
15
- injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
17
- python_injection-0.9.0.dist-info/METADATA,sha256=j_ikavu9sPJ2xkKcX6Vr8MmhoNayLEiVNLPQ99TzWNY,3570
18
- python_injection-0.9.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
19
- python_injection-0.9.0.dist-info/RECORD,,