python-injection 0.14.0.post0__py3-none-any.whl → 0.14.2__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__.pyi CHANGED
@@ -10,6 +10,7 @@ from ._core.common.type import InputType as _InputType
10
10
  from ._core.common.type import TypeInfo as _TypeInfo
11
11
  from ._core.module import InjectableFactory as _InjectableFactory
12
12
  from ._core.module import ModeStr, PriorityStr
13
+ from ._core.module import Recipe as _Recipe
13
14
 
14
15
  __MODULE: Final[Module] = ...
15
16
 
@@ -91,7 +92,7 @@ class Module:
91
92
 
92
93
  def injectable[**P, T](
93
94
  self,
94
- wrapped: Callable[P, T] | Callable[P, Awaitable[T]] = ...,
95
+ wrapped: _Recipe[P, T] = ...,
95
96
  /,
96
97
  *,
97
98
  cls: _InjectableFactory[T] = ...,
@@ -107,7 +108,7 @@ class Module:
107
108
 
108
109
  def singleton[**P, T](
109
110
  self,
110
- wrapped: Callable[P, T] | Callable[P, Awaitable[T]] = ...,
111
+ wrapped: _Recipe[P, T] = ...,
111
112
  /,
112
113
  *,
113
114
  inject: bool = ...,
@@ -176,6 +177,12 @@ class Module:
176
177
  /,
177
178
  threadsafe: bool = ...,
178
179
  ) -> Callable[P, T]: ...
180
+ def make_async_factory[T](
181
+ self,
182
+ wrapped: type[T],
183
+ /,
184
+ threadsafe: bool = ...,
185
+ ) -> Callable[..., Awaitable[T]]: ...
179
186
  async def afind_instance[T](self, cls: _InputType[T]) -> T: ...
180
187
  def find_instance[T](self, cls: _InputType[T]) -> T:
181
188
  """
injection/_core/hook.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import itertools
2
- from collections import deque
3
2
  from collections.abc import Callable, Generator, Iterator
4
3
  from dataclasses import dataclass, field
5
4
  from inspect import isclass, isgeneratorfunction
@@ -14,8 +13,8 @@ type HookFunction[**P, T] = Callable[P, T] | HookGeneratorFunction[P, T]
14
13
 
15
14
  @dataclass(eq=False, frozen=True, slots=True)
16
15
  class Hook[**P, T]:
17
- __functions: deque[HookFunction[P, T]] = field(
18
- default_factory=deque,
16
+ __functions: list[HookFunction[P, T]] = field(
17
+ default_factory=list,
19
18
  init=False,
20
19
  repr=False,
21
20
  )
@@ -36,7 +35,7 @@ class Hook[**P, T]:
36
35
  return iter(self.__functions)
37
36
 
38
37
  def add(self, *functions: HookFunction[P, T]) -> Self:
39
- self.__functions.extendleft(functions)
38
+ self.__functions.extend(reversed(functions))
40
39
  return self
41
40
 
42
41
  @classmethod
@@ -65,7 +64,6 @@ class Hook[**P, T]:
65
64
  hook.throw(exc)
66
65
  else:
67
66
  hook.send(value)
68
- return value
69
67
 
70
68
  except StopIteration as stop:
71
69
  return stop.value
injection/_core/module.py CHANGED
@@ -3,10 +3,12 @@ from __future__ import annotations
3
3
  from abc import ABC, abstractmethod
4
4
  from collections import OrderedDict, deque
5
5
  from collections.abc import (
6
+ AsyncGenerator,
6
7
  AsyncIterator,
7
8
  Awaitable,
8
9
  Callable,
9
10
  Collection,
11
+ Generator,
10
12
  Iterable,
11
13
  Iterator,
12
14
  Mapping,
@@ -71,6 +73,7 @@ from injection.exceptions import (
71
73
  ModuleLockError,
72
74
  ModuleNotUsedError,
73
75
  NoInjectable,
76
+ SkipInjectable,
74
77
  )
75
78
 
76
79
  """
@@ -360,6 +363,14 @@ class Priority(StrEnum):
360
363
 
361
364
  type PriorityStr = Literal["low", "high"]
362
365
 
366
+ type ContextManagerLikeRecipe[**P, T] = (
367
+ Callable[P, ContextManager[T]] | Callable[P, AsyncContextManager[T]]
368
+ )
369
+ type GeneratorRecipe[**P, T] = (
370
+ Callable[P, Generator[T, Any, Any]] | Callable[P, AsyncGenerator[T, Any]]
371
+ )
372
+ type Recipe[**P, T] = Callable[P, T] | Callable[P, Awaitable[T]]
373
+
363
374
 
364
375
  @dataclass(eq=False, frozen=True, slots=True)
365
376
  class Module(Broker, EventListener):
@@ -411,7 +422,7 @@ class Module(Broker, EventListener):
411
422
 
412
423
  def injectable[**P, T](
413
424
  self,
414
- wrapped: Callable[P, T] | Callable[P, Awaitable[T]] | None = None,
425
+ wrapped: Recipe[P, T] | None = None,
415
426
  /,
416
427
  *,
417
428
  cls: InjectableFactory[T] = SimpleInjectable,
@@ -420,9 +431,7 @@ class Module(Broker, EventListener):
420
431
  on: TypeInfo[T] = (),
421
432
  mode: Mode | ModeStr = Mode.get_default(),
422
433
  ) -> Any:
423
- def decorator(
424
- wp: Callable[P, T] | Callable[P, Awaitable[T]],
425
- ) -> Callable[P, T] | Callable[P, Awaitable[T]]:
434
+ def decorator(wp: Recipe[P, T]) -> Recipe[P, T]:
426
435
  factory = extract_caller(self.make_injected_function(wp) if inject else wp)
427
436
  hints = on if ignore_type_hint else (wp, on)
428
437
  updater = Updater(
@@ -447,23 +456,10 @@ class Module(Broker, EventListener):
447
456
  mode: Mode | ModeStr = Mode.get_default(),
448
457
  ) -> Any:
449
458
  def decorator(
450
- wrapped: Callable[P, T]
451
- | Callable[P, Awaitable[T]]
452
- | Callable[P, Iterator[T]]
453
- | Callable[P, AsyncIterator[T]],
454
- ) -> (
455
- Callable[P, T]
456
- | Callable[P, Awaitable[T]]
457
- | Callable[P, Iterator[T]]
458
- | Callable[P, AsyncIterator[T]]
459
- ):
459
+ wrapped: Recipe[P, T] | GeneratorRecipe[P, T],
460
+ ) -> Recipe[P, T] | GeneratorRecipe[P, T]:
460
461
  injectable_class: Callable[[Caller[P, Any], str], Injectable[T]]
461
- wrapper: (
462
- Callable[P, T]
463
- | Callable[P, Awaitable[T]]
464
- | Callable[P, ContextManager[T]]
465
- | Callable[P, AsyncContextManager[T]]
466
- )
462
+ wrapper: Recipe[P, T] | ContextManagerLikeRecipe[P, T]
467
463
 
468
464
  if isasyncgenfunction(wrapped):
469
465
  hint = get_yield_hint(wrapped)
@@ -588,6 +584,18 @@ class Module(Broker, EventListener):
588
584
 
589
585
  return SyncInjectedFunction(metadata)
590
586
 
587
+ def make_async_factory[T](
588
+ self,
589
+ wrapped: type[T],
590
+ /,
591
+ threadsafe: bool = False,
592
+ ) -> Callable[..., Awaitable[T]]:
593
+ factory: InjectedFunction[..., T] = self.make_injected_function(
594
+ wrapped,
595
+ threadsafe,
596
+ )
597
+ return factory.__inject_metadata__.acall
598
+
591
599
  async def afind_instance[T](self, cls: InputType[T]) -> T:
592
600
  injectable = self[cls]
593
601
  return await injectable.aget_instance()
@@ -613,7 +621,7 @@ class Module(Broker, EventListener):
613
621
  async def aget_instance(self, cls, default=None): # type: ignore[no-untyped-def]
614
622
  try:
615
623
  return await self.afind_instance(cls)
616
- except KeyError:
624
+ except (KeyError, SkipInjectable):
617
625
  return default
618
626
 
619
627
  @overload
@@ -633,7 +641,7 @@ class Module(Broker, EventListener):
633
641
  def get_instance(self, cls, default=None): # type: ignore[no-untyped-def]
634
642
  try:
635
643
  return self.find_instance(cls)
636
- except KeyError:
644
+ except (KeyError, SkipInjectable):
637
645
  return default
638
646
 
639
647
  @overload
@@ -859,29 +867,28 @@ class Dependencies:
859
867
  lazy_mapping: Lazy[Mapping[str, Injectable[Any]]]
860
868
 
861
869
  def __iter__(self) -> Iterator[tuple[str, Any]]:
862
- for name, injectable in self.mapping.items():
863
- instance = injectable.get_instance()
864
- yield name, instance
870
+ for name, injectable in self.items():
871
+ with suppress(SkipInjectable):
872
+ yield name, injectable.get_instance()
865
873
 
866
874
  async def __aiter__(self) -> AsyncIterator[tuple[str, Any]]:
867
- for name, injectable in self.mapping.items():
868
- instance = await injectable.aget_instance()
869
- yield name, instance
875
+ for name, injectable in self.items():
876
+ with suppress(SkipInjectable):
877
+ yield name, await injectable.aget_instance()
870
878
 
871
879
  @property
872
880
  def are_resolved(self) -> bool:
873
881
  return self.lazy_mapping.is_set
874
882
 
875
- @property
876
- def mapping(self) -> Mapping[str, Injectable[Any]]:
877
- return ~self.lazy_mapping
878
-
879
883
  async def aget_arguments(self) -> dict[str, Any]:
880
884
  return {key: value async for key, value in self}
881
885
 
882
886
  def get_arguments(self) -> dict[str, Any]:
883
887
  return dict(self)
884
888
 
889
+ def items(self) -> Iterator[tuple[str, Injectable[Any]]]:
890
+ return iter((~self.lazy_mapping).items())
891
+
885
892
  @classmethod
886
893
  def from_iterable(cls, iterable: Iterable[tuple[str, Injectable[Any]]]) -> Self:
887
894
  lazy_mapping = Lazy(lambda: dict(iterable))
injection/exceptions.py CHANGED
@@ -10,6 +10,7 @@ __all__ = (
10
10
  "ScopeAlreadyDefinedError",
11
11
  "ScopeError",
12
12
  "ScopeUndefinedError",
13
+ "SkipInjectable",
13
14
  )
14
15
 
15
16
 
@@ -30,6 +31,9 @@ class NoInjectable[T](KeyError, InjectionError):
30
31
  return self.__class
31
32
 
32
33
 
34
+ class SkipInjectable(InjectionError): ...
35
+
36
+
33
37
  class ModuleError(InjectionError): ...
34
38
 
35
39
 
@@ -42,7 +46,7 @@ class ModuleNotUsedError(KeyError, ModuleError): ...
42
46
  class ScopeError(InjectionError): ...
43
47
 
44
48
 
45
- class ScopeUndefinedError(LookupError, ScopeError): ...
49
+ class ScopeUndefinedError(LookupError, SkipInjectable, ScopeError): ...
46
50
 
47
51
 
48
52
  class ScopeAlreadyDefinedError(ScopeError): ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-injection
3
- Version: 0.14.0.post0
3
+ Version: 0.14.2
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Project-URL: Repository, https://github.com/100nm/python-injection
6
6
  Author: remimd
@@ -1,13 +1,13 @@
1
1
  injection/__init__.py,sha256=X0vIAoN4MDlhR7YIkup1qHgqbOkwZ3PWgdSi7h-udaM,1049
2
- injection/__init__.pyi,sha256=T95sTJol0Tmzlajb5rqJd020CBMYrHXvFDPUmbazD8I,9916
3
- injection/exceptions.py,sha256=T__732aXxWWUz0sKc39ySteyolCS5tpqQC0oCnzUF2E,917
2
+ injection/__init__.pyi,sha256=6B5GdRNaAJ15Q-QAL8nNpTBVIKEUPJAwWIlCSjOkkJ8,10058
3
+ injection/exceptions.py,sha256=dp9XYpWHh8R76wbWDJdjmpTHGQp8KuCeveTqarXY_6Y,999
4
4
  injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  injection/utils.py,sha256=Lr0qHaq3_1AkUoAaw2XmMNie_alwYTeTrRlO22FeCnk,2762
6
6
  injection/_core/__init__.py,sha256=XERocCxCZBxPGIaOR37yeiQOZyvjHQ6a4rgRmlkUSuU,1367
7
7
  injection/_core/descriptors.py,sha256=7fSHlgAqmgR_Uta8KocBapOt1Xyj2dI7RY9ZdoStTzw,726
8
- injection/_core/hook.py,sha256=bXTMd3NiltV2yEweSJWOjNybKQwX_L7N4oE1v24BQK8,3156
8
+ injection/_core/hook.py,sha256=rJyaSwFAHM8qizWANPUSt5KzWdjxc5oJ2U8hcGeZjU8,3097
9
9
  injection/_core/injectables.py,sha256=GIumNp0TXf8Voxe1sCPhcqq2gyw4E_hl7I45IJ_tyHE,4512
10
- injection/_core/module.py,sha256=xd55CGCpDjNc9ZW_iwVLpejgKIAJaRTVK860ZqmIPGY,30978
10
+ injection/_core/module.py,sha256=1eGb4IHVCJByHUnAG83-oPgp8VbZty8x1YZ6OFT0zEU,31232
11
11
  injection/_core/scope.py,sha256=pwOqVKOUM_ZteNScgp-hLqyOHg5Ew75jWlfWLLD2PU8,5437
12
12
  injection/_core/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  injection/_core/common/asynchronous.py,sha256=9bQDVRE6eqo9K0d5H9RzyFalf0WGoGP7cDrKDGbvZPI,1500
@@ -20,6 +20,6 @@ injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
20
20
  injection/integrations/fastapi.py,sha256=YHSs85_3m6TUVtOwUcV157b3UZJQIw_aXWAg199a-YE,594
21
21
  injection/testing/__init__.py,sha256=Bh3JqEXw62-JnUnspOXr2jpjGXZJ_K_K9CgfD7dPZeQ,793
22
22
  injection/testing/__init__.pyi,sha256=1h9zNGghxlo4D1jAx5s8EHk54mn7o-qO7WgJa6hwX6U,484
23
- python_injection-0.14.0.post0.dist-info/METADATA,sha256=AfarVqsSo7GAgTtjjhEUG2xFoincSsfvLOfGDwf9ZDs,3205
24
- python_injection-0.14.0.post0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
- python_injection-0.14.0.post0.dist-info/RECORD,,
23
+ python_injection-0.14.2.dist-info/METADATA,sha256=9IhlCQv8o9QERwHq1RZwXB83mmrz4stiLxnoKKhX6sI,3199
24
+ python_injection-0.14.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ python_injection-0.14.2.dist-info/RECORD,,