python-injection 0.8.4.post2__py3-none-any.whl → 0.9.0__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/__init__.pyi CHANGED
@@ -1,23 +1,23 @@
1
1
  from abc import abstractmethod
2
- from collections.abc import Callable, Iterable
2
+ from collections.abc import Callable
3
3
  from contextlib import ContextDecorator
4
- from enum import Enum
4
+ from enum import StrEnum
5
5
  from types import UnionType
6
6
  from typing import (
7
7
  Any,
8
8
  ContextManager,
9
9
  Final,
10
- Literal,
11
10
  Protocol,
12
- TypeVar,
11
+ Self,
13
12
  final,
14
13
  runtime_checkable,
15
14
  )
16
15
 
17
16
  from .common.invertible import Invertible
18
-
19
- _T = TypeVar("_T")
20
- _T_co = TypeVar("_T_co", covariant=True)
17
+ from .common.tools.type import TypeInfo
18
+ from .core import InjectableFactory
19
+ from .core import ModeStr as InjectableModeStr
20
+ from .core import PriorityStr as ModulePriorityStr
21
21
 
22
22
  default_module: Final[Module] = ...
23
23
 
@@ -41,6 +41,8 @@ class Module:
41
41
 
42
42
  def __init__(self, name: str = ...): ...
43
43
  def __contains__(self, cls: type | UnionType, /) -> bool: ...
44
+ @property
45
+ def is_locked(self) -> bool: ...
44
46
  def inject(self, wrapped: Callable[..., Any] = ..., /):
45
47
  """
46
48
  Decorator applicable to a class or function. Inject function dependencies using
@@ -48,15 +50,15 @@ class Module:
48
50
  will be those of the `__init__` method.
49
51
  """
50
52
 
51
- def injectable(
53
+ def injectable[T](
52
54
  self,
53
- wrapped: Callable[..., Any] = ...,
55
+ wrapped: Callable[..., T] = ...,
54
56
  /,
55
57
  *,
56
- cls: type[Injectable] = ...,
58
+ cls: InjectableFactory[T] = ...,
57
59
  inject: bool = ...,
58
- on: type | Iterable[type] | UnionType = ...,
59
- mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
60
+ on: TypeInfo[T] = ...,
61
+ mode: InjectableMode | InjectableModeStr = ...,
60
62
  ):
61
63
  """
62
64
  Decorator applicable to a class or function. It is used to indicate how the
@@ -64,14 +66,14 @@ class Module:
64
66
  injected each time.
65
67
  """
66
68
 
67
- def singleton(
69
+ def singleton[T](
68
70
  self,
69
- wrapped: Callable[..., Any] = ...,
71
+ wrapped: Callable[..., T] = ...,
70
72
  /,
71
73
  *,
72
74
  inject: bool = ...,
73
- on: type | Iterable[type] | UnionType = ...,
74
- mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
75
+ on: TypeInfo[T] = ...,
76
+ mode: InjectableMode | InjectableModeStr = ...,
75
77
  ):
76
78
  """
77
79
  Decorator applicable to a class or function. It is used to indicate how the
@@ -86,37 +88,37 @@ class Module:
86
88
  registered.
87
89
  """
88
90
 
89
- def set_constant(
91
+ def set_constant[T](
90
92
  self,
91
- instance: _T,
92
- on: type | Iterable[type] | UnionType = ...,
93
+ instance: T,
94
+ on: TypeInfo[T] = ...,
93
95
  *,
94
- mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
95
- ) -> _T:
96
+ mode: InjectableMode | InjectableModeStr = ...,
97
+ ) -> T:
96
98
  """
97
99
  Function for registering a specific instance to be injected. This is useful for
98
100
  registering global variables. The difference with the singleton decorator is
99
101
  that no dependencies are resolved, so the module doesn't need to be locked.
100
102
  """
101
103
 
102
- def resolve(self, cls: type[_T]) -> _T:
104
+ def resolve[T](self, cls: type[T]) -> T:
103
105
  """
104
106
  Function used to retrieve an instance associated with the type passed in
105
107
  parameter or an exception will be raised.
106
108
  """
107
109
 
108
- def get_instance(self, cls: type[_T]) -> _T | None:
110
+ def get_instance[T](self, cls: type[T]) -> T | None:
109
111
  """
110
112
  Function used to retrieve an instance associated with the type passed in
111
113
  parameter or return `None`.
112
114
  """
113
115
 
114
- def get_lazy_instance(
116
+ def get_lazy_instance[T](
115
117
  self,
116
- cls: type[_T],
118
+ cls: type[T],
117
119
  *,
118
120
  cache: bool = ...,
119
- ) -> Invertible[_T | None]:
121
+ ) -> Invertible[T | None]:
120
122
  """
121
123
  Function used to retrieve an instance associated with the type passed in
122
124
  parameter or `None`. Return a `Invertible` object. To access the instance
@@ -126,19 +128,24 @@ class Module:
126
128
  Example: instance = ~lazy_instance
127
129
  """
128
130
 
131
+ def init_modules(self, *modules: Module) -> Self:
132
+ """
133
+ Function to clean modules in use and to use those passed as parameters.
134
+ """
135
+
129
136
  def use(
130
137
  self,
131
138
  module: Module,
132
139
  *,
133
- priority: ModulePriority | Literal["low", "high"] = ...,
134
- ):
140
+ priority: ModulePriority | ModulePriorityStr = ...,
141
+ ) -> Self:
135
142
  """
136
143
  Function for using another module. Using another module replaces the module's
137
144
  dependencies with those of the module used. If the dependency is not found, it
138
145
  will be searched for in the module's dependency container.
139
146
  """
140
147
 
141
- def stop_using(self, module: Module):
148
+ def stop_using(self, module: Module) -> Self:
142
149
  """
143
150
  Function to remove a module in use.
144
151
  """
@@ -147,7 +154,7 @@ class Module:
147
154
  self,
148
155
  module: Module,
149
156
  *,
150
- priority: ModulePriority | Literal["low", "high"] = ...,
157
+ priority: ModulePriority | ModulePriorityStr = ...,
151
158
  ) -> ContextManager | ContextDecorator:
152
159
  """
153
160
  Context manager or decorator for temporary use of a module.
@@ -156,8 +163,8 @@ class Module:
156
163
  def change_priority(
157
164
  self,
158
165
  module: Module,
159
- priority: ModulePriority | Literal["low", "high"],
160
- ):
166
+ priority: ModulePriority | ModulePriorityStr,
167
+ ) -> Self:
161
168
  """
162
169
  Function for changing the priority of a module in use.
163
170
  There are two priority values:
@@ -166,27 +173,26 @@ class Module:
166
173
  * **HIGH**: The module concerned becomes the most important of the modules used.
167
174
  """
168
175
 
169
- def unlock(self):
176
+ def unlock(self) -> Self:
170
177
  """
171
178
  Function to unlock the module by deleting cached instances of singletons.
172
179
  """
173
180
 
174
181
  @final
175
- class ModulePriority(str, Enum):
182
+ class ModulePriority(StrEnum):
176
183
  LOW = ...
177
184
  HIGH = ...
178
185
 
179
186
  @runtime_checkable
180
- class Injectable(Protocol[_T_co]):
181
- def __init__(self, factory: Callable[[], _T_co] = ..., /): ...
187
+ class Injectable[T](Protocol):
182
188
  @property
183
189
  def is_locked(self) -> bool: ...
184
190
  def unlock(self): ...
185
191
  @abstractmethod
186
- def get_instance(self) -> _T_co: ...
192
+ def get_instance(self) -> T: ...
187
193
 
188
194
  @final
189
- class InjectableMode(str, Enum):
195
+ class InjectableMode(StrEnum):
190
196
  FALLBACK = ...
191
197
  NORMAL = ...
192
198
  OVERRIDE = ...
injection/common/event.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from contextlib import ExitStack, contextmanager, suppress
3
3
  from dataclasses import dataclass, field
4
- from typing import ContextManager
4
+ from typing import ContextManager, Self
5
5
  from weakref import WeakSet
6
6
 
7
7
  __all__ = ("Event", "EventChannel", "EventListener")
@@ -36,11 +36,11 @@ class EventChannel:
36
36
 
37
37
  yield
38
38
 
39
- def add_listener(self, listener: EventListener):
39
+ def add_listener(self, listener: EventListener) -> Self:
40
40
  self.__listeners.add(listener)
41
41
  return self
42
42
 
43
- def remove_listener(self, listener: EventListener):
43
+ def remove_listener(self, listener: EventListener) -> Self:
44
44
  with suppress(KeyError):
45
45
  self.__listeners.remove(listener)
46
46
 
@@ -1,23 +1,21 @@
1
1
  from abc import abstractmethod
2
2
  from collections.abc import Callable
3
3
  from dataclasses import dataclass
4
- from typing import Protocol, TypeVar, runtime_checkable
4
+ from typing import Protocol, runtime_checkable
5
5
 
6
6
  __all__ = ("Invertible", "SimpleInvertible")
7
7
 
8
- _T_co = TypeVar("_T_co", covariant=True)
9
-
10
8
 
11
9
  @runtime_checkable
12
- class Invertible(Protocol[_T_co]):
10
+ class Invertible[T](Protocol):
13
11
  @abstractmethod
14
- def __invert__(self) -> _T_co:
12
+ def __invert__(self) -> T:
15
13
  raise NotImplementedError
16
14
 
17
15
 
18
16
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
19
- class SimpleInvertible(Invertible[_T_co]):
20
- callable: Callable[[], _T_co]
17
+ class SimpleInvertible[T](Invertible[T]):
18
+ callable: Callable[[], T]
21
19
 
22
- def __invert__(self) -> _T_co:
20
+ def __invert__(self) -> T:
23
21
  return self.callable()
injection/common/lazy.py CHANGED
@@ -1,31 +1,26 @@
1
1
  from collections.abc import Callable, Iterator, Mapping
2
2
  from types import MappingProxyType
3
- from typing import TypeVar
4
3
 
5
4
  from injection.common.invertible import Invertible
6
5
 
7
6
  __all__ = ("Lazy", "LazyMapping")
8
7
 
9
- _T = TypeVar("_T")
10
- _K = TypeVar("_K")
11
- _V = TypeVar("_V")
12
8
 
13
-
14
- class Lazy(Invertible[_T]):
9
+ class Lazy[T](Invertible[T]):
15
10
  __slots__ = ("__cache", "__is_set")
16
11
 
17
- def __init__(self, factory: Callable[[], _T]):
12
+ def __init__(self, factory: Callable[[], T]):
18
13
  self.__setup_cache(factory)
19
14
 
20
- def __invert__(self) -> _T:
15
+ def __invert__(self) -> T:
21
16
  return next(self.__cache)
22
17
 
23
18
  @property
24
19
  def is_set(self) -> bool:
25
20
  return self.__is_set
26
21
 
27
- def __setup_cache(self, factory: Callable[[], _T]):
28
- def cache_generator() -> Iterator[_T]:
22
+ def __setup_cache(self, factory: Callable[[], T]):
23
+ def cache_generator() -> Iterator[T]:
29
24
  nonlocal factory
30
25
  cached = factory()
31
26
  self.__is_set = True
@@ -38,16 +33,16 @@ class Lazy(Invertible[_T]):
38
33
  self.__is_set = False
39
34
 
40
35
 
41
- class LazyMapping(Mapping[_K, _V]):
36
+ class LazyMapping[K, V](Mapping[K, V]):
42
37
  __slots__ = ("__lazy",)
43
38
 
44
- def __init__(self, iterator: Iterator[tuple[_K, _V]]):
39
+ def __init__(self, iterator: Iterator[tuple[K, V]]):
45
40
  self.__lazy = Lazy(lambda: MappingProxyType(dict(iterator)))
46
41
 
47
- def __getitem__(self, key: _K, /) -> _V:
42
+ def __getitem__(self, key: K, /) -> V:
48
43
  return (~self.__lazy)[key]
49
44
 
50
- def __iter__(self) -> Iterator[_K]:
45
+ def __iter__(self) -> Iterator[K]:
51
46
  yield from ~self.__lazy
52
47
 
53
48
  def __len__(self) -> int:
@@ -3,10 +3,8 @@ from threading import RLock
3
3
 
4
4
  __all__ = ("synchronized",)
5
5
 
6
- __lock = RLock()
7
-
8
6
 
9
7
  @contextmanager
10
8
  def synchronized():
11
- with __lock:
9
+ with RLock():
12
10
  yield
@@ -1,48 +1,73 @@
1
- from collections.abc import Iterable, Iterator
1
+ from collections.abc import Callable, Iterable, Iterator
2
2
  from inspect import get_annotations, isfunction
3
- from types import NoneType, UnionType
4
- from typing import Annotated, Any, Union, get_args, get_origin
3
+ from types import UnionType
4
+ from typing import (
5
+ Annotated,
6
+ Any,
7
+ NamedTuple,
8
+ Self,
9
+ Union,
10
+ get_args,
11
+ get_origin,
12
+ )
5
13
 
6
- __all__ = ("find_types", "format_type", "get_origins")
14
+ __all__ = ("TypeInfo", "TypeReport", "analyze_types", "get_return_types")
7
15
 
16
+ type TypeInfo[T] = type[T] | Callable[..., T] | Iterable[TypeInfo[T]] | UnionType
8
17
 
9
- def format_type(cls: type | Any) -> str:
10
- try:
11
- return f"{cls.__module__}.{cls.__qualname__}"
12
- except AttributeError:
13
- return str(cls)
14
18
 
19
+ class TypeReport[T](NamedTuple):
20
+ origin: type[T]
21
+ args: tuple[Any, ...]
15
22
 
16
- def get_origins(*types: type | Any) -> Iterator[type | Any]:
17
- for tp in types:
18
- origin = get_origin(tp) or tp
23
+ @property
24
+ def cls(self) -> type[T]:
25
+ if self.args:
26
+ return self.origin[*self.args]
27
+
28
+ return self.origin
29
+
30
+ @property
31
+ def no_args(self) -> Self:
32
+ if self.args:
33
+ return type(self)(self.origin, ())
34
+
35
+ return self
19
36
 
20
- if origin in (None, NoneType):
37
+
38
+ def analyze_types(*types: type | Any) -> Iterator[TypeReport[Any]]:
39
+ for tp in types:
40
+ if tp is None:
21
41
  continue
22
42
 
23
- elif origin in (Union, UnionType):
24
- args = get_args(tp)
43
+ origin = get_origin(tp)
44
+
45
+ if origin is Union or isinstance(tp, UnionType):
46
+ inner_types = get_args(tp)
25
47
 
26
- elif origin is Annotated is not tp:
27
- args = get_args(tp)[:1]
48
+ elif origin is Annotated:
49
+ inner_types = get_args(tp)[:1]
28
50
 
29
51
  else:
30
- yield origin
52
+ yield TypeReport(origin or tp, get_args(tp))
31
53
  continue
32
54
 
33
- yield from get_origins(*args)
55
+ yield from analyze_types(*inner_types)
34
56
 
35
57
 
36
- def find_types(*args: Any) -> Iterator[type | UnionType]:
37
- for argument in args:
38
- if isinstance(argument, Iterable) and not isinstance(argument, type | str):
39
- arguments = argument
58
+ def get_return_types(*args: TypeInfo[Any]) -> Iterator[type | UnionType]:
59
+ for arg in args:
60
+ if isinstance(arg, Iterable) and not isinstance(
61
+ get_origin(arg) or arg,
62
+ type | str,
63
+ ):
64
+ inner_args = arg
40
65
 
41
- elif isfunction(argument):
42
- arguments = (get_annotations(argument, eval_str=True).get("return"),)
66
+ elif isfunction(arg):
67
+ inner_args = (get_annotations(arg, eval_str=True).get("return"),)
43
68
 
44
69
  else:
45
- yield argument
70
+ yield arg
46
71
  continue
47
72
 
48
- yield from find_types(*arguments)
73
+ yield from get_return_types(*inner_args)
injection/core/module.py CHANGED
@@ -14,9 +14,10 @@ from collections.abc import (
14
14
  )
15
15
  from contextlib import contextmanager, suppress
16
16
  from dataclasses import dataclass, field
17
- from enum import Enum
17
+ from enum import StrEnum
18
18
  from functools import partialmethod, singledispatchmethod, update_wrapper
19
19
  from inspect import Signature, isclass
20
+ from queue import Queue
20
21
  from types import MethodType, UnionType
21
22
  from typing import (
22
23
  Any,
@@ -26,16 +27,20 @@ from typing import (
26
27
  NamedTuple,
27
28
  NoReturn,
28
29
  Protocol,
29
- TypeVar,
30
+ Self,
30
31
  runtime_checkable,
31
32
  )
32
33
 
33
34
  from injection.common.event import Event, EventChannel, EventListener
34
35
  from injection.common.invertible import Invertible, SimpleInvertible
35
36
  from injection.common.lazy import Lazy, LazyMapping
36
- from injection.common.queue import LimitedQueue
37
37
  from injection.common.tools.threading import synchronized
38
- from injection.common.tools.type import find_types, format_type, get_origins
38
+ from injection.common.tools.type import (
39
+ TypeInfo,
40
+ TypeReport,
41
+ analyze_types,
42
+ get_return_types,
43
+ )
39
44
  from injection.exceptions import (
40
45
  InjectionError,
41
46
  ModuleError,
@@ -44,14 +49,18 @@ from injection.exceptions import (
44
49
  NoInjectable,
45
50
  )
46
51
 
47
- __all__ = ("Injectable", "Mode", "Module", "Priority")
52
+ __all__ = (
53
+ "Injectable",
54
+ "InjectableFactory",
55
+ "Mode",
56
+ "ModeStr",
57
+ "Module",
58
+ "Priority",
59
+ "PriorityStr",
60
+ )
48
61
 
49
62
  _logger = logging.getLogger(__name__)
50
63
 
51
- _T = TypeVar("_T")
52
- _T_co = TypeVar("_T_co", covariant=True)
53
-
54
-
55
64
  """
56
65
  Events
57
66
  """
@@ -64,12 +73,12 @@ class ContainerEvent(Event, ABC):
64
73
 
65
74
  @dataclass(frozen=True, slots=True)
66
75
  class ContainerDependenciesUpdated(ContainerEvent):
67
- classes: Collection[type]
76
+ reports: Collection[TypeReport]
68
77
  mode: Mode
69
78
 
70
79
  def __str__(self) -> str:
71
- length = len(self.classes)
72
- formatted_classes = ", ".join(f"`{format_type(cls)}`" for cls in self.classes)
80
+ length = len(self.reports)
81
+ formatted_classes = ", ".join(f"`{report.cls}`" for report in self.reports)
73
82
  return (
74
83
  f"{length} container dependenc{'ies' if length > 1 else 'y'} have been "
75
84
  f"updated{f': {formatted_classes}' if formatted_classes else ''}."
@@ -135,12 +144,9 @@ Injectables
135
144
 
136
145
 
137
146
  @runtime_checkable
138
- class Injectable(Protocol[_T_co]):
147
+ class Injectable[T](Protocol):
139
148
  __slots__ = ()
140
149
 
141
- def __init__(self, __factory: Callable[[], _T_co] = None, /):
142
- pass
143
-
144
150
  @property
145
151
  def is_locked(self) -> bool:
146
152
  return False
@@ -149,23 +155,23 @@ class Injectable(Protocol[_T_co]):
149
155
  return
150
156
 
151
157
  @abstractmethod
152
- def get_instance(self) -> _T_co:
158
+ def get_instance(self) -> T:
153
159
  raise NotImplementedError
154
160
 
155
161
 
156
162
  @dataclass(repr=False, frozen=True, slots=True)
157
- class BaseInjectable(Injectable[_T], ABC):
158
- factory: Callable[[], _T]
163
+ class BaseInjectable[T](Injectable[T], ABC):
164
+ factory: Callable[[], T]
159
165
 
160
166
 
161
- class NewInjectable(BaseInjectable[_T]):
167
+ class NewInjectable[T](BaseInjectable[T]):
162
168
  __slots__ = ()
163
169
 
164
- def get_instance(self) -> _T:
170
+ def get_instance(self) -> T:
165
171
  return self.factory()
166
172
 
167
173
 
168
- class SingletonInjectable(BaseInjectable[_T]):
174
+ class SingletonInjectable[T](BaseInjectable[T]):
169
175
  __slots__ = ("__dict__",)
170
176
 
171
177
  __INSTANCE_KEY: ClassVar[str] = "$instance"
@@ -181,7 +187,7 @@ class SingletonInjectable(BaseInjectable[_T]):
181
187
  def unlock(self):
182
188
  self.cache.clear()
183
189
 
184
- def get_instance(self) -> _T:
190
+ def get_instance(self) -> T:
185
191
  with suppress(KeyError):
186
192
  return self.cache[self.__INSTANCE_KEY]
187
193
 
@@ -193,11 +199,11 @@ class SingletonInjectable(BaseInjectable[_T]):
193
199
 
194
200
 
195
201
  @dataclass(repr=False, frozen=True, slots=True)
196
- class ShouldBeInjectable(Injectable[_T]):
197
- cls: type[_T]
202
+ class ShouldBeInjectable[T](Injectable[T]):
203
+ cls: type[T]
198
204
 
199
205
  def get_instance(self) -> NoReturn:
200
- raise InjectionError(f"`{format_type(self.cls)}` should be an injectable.")
206
+ raise InjectionError(f"`{self.cls}` should be an injectable.")
201
207
 
202
208
 
203
209
  """
@@ -210,7 +216,7 @@ class Broker(Protocol):
210
216
  __slots__ = ()
211
217
 
212
218
  @abstractmethod
213
- def __getitem__(self, cls: type[_T] | UnionType, /) -> Injectable[_T]:
219
+ def __getitem__[T](self, cls: type[T] | UnionType, /) -> Injectable[T]:
214
220
  raise NotImplementedError
215
221
 
216
222
  @abstractmethod
@@ -223,7 +229,7 @@ class Broker(Protocol):
223
229
  raise NotImplementedError
224
230
 
225
231
  @abstractmethod
226
- def unlock(self):
232
+ def unlock(self) -> Self:
227
233
  raise NotImplementedError
228
234
 
229
235
 
@@ -232,7 +238,7 @@ Container
232
238
  """
233
239
 
234
240
 
235
- class Mode(str, Enum):
241
+ class Mode(StrEnum):
236
242
  FALLBACK = "fallback"
237
243
  NORMAL = "normal"
238
244
  OVERRIDE = "override"
@@ -242,36 +248,37 @@ class Mode(str, Enum):
242
248
  return tuple(type(self)).index(self)
243
249
 
244
250
  @classmethod
245
- def get_default(cls):
251
+ def get_default(cls) -> Mode:
246
252
  return cls.NORMAL
247
253
 
248
254
 
249
- ModeStr = Literal["fallback", "normal", "override"]
255
+ type ModeStr = Literal["fallback", "normal", "override"]
250
256
 
251
257
 
252
- class Record(NamedTuple):
253
- injectable: Injectable
258
+ class Record[T](NamedTuple):
259
+ injectable: Injectable[T]
254
260
  mode: Mode
255
261
 
256
262
 
257
263
  @dataclass(repr=False, frozen=True, slots=True)
258
264
  class Container(Broker):
259
- __records: dict[type, Record] = field(default_factory=dict, init=False)
265
+ __records: dict[TypeReport, Record] = field(default_factory=dict, init=False)
260
266
  __channel: EventChannel = field(default_factory=EventChannel, init=False)
261
267
 
262
- def __getitem__(self, cls: type[_T] | UnionType, /) -> Injectable[_T]:
263
- for cls in get_origins(cls):
264
- try:
265
- injectable, _ = self.__records[cls]
266
- except KeyError:
267
- continue
268
+ def __getitem__[T](self, cls: type[T] | UnionType, /) -> Injectable[T]:
269
+ for report in analyze_types(cls):
270
+ for scoped_report in dict.fromkeys((report, report.no_args)):
271
+ try:
272
+ injectable, _ = self.__records[scoped_report]
273
+ except KeyError:
274
+ continue
268
275
 
269
- return injectable
276
+ return injectable
270
277
 
271
278
  raise NoInjectable(cls)
272
279
 
273
280
  def __contains__(self, cls: type | UnionType, /) -> bool:
274
- return any(cls in self.__records for cls in get_origins(cls))
281
+ return any(report in self.__records for report in analyze_types(cls))
275
282
 
276
283
  @property
277
284
  def is_locked(self) -> bool:
@@ -282,16 +289,16 @@ class Container(Broker):
282
289
  return frozenset(injectable for injectable, _ in self.__records.values())
283
290
 
284
291
  @synchronized()
285
- def update(
292
+ def update[T](
286
293
  self,
287
- classes: Iterable[type | UnionType],
288
- injectable: Injectable,
294
+ classes: Iterable[type[T] | UnionType],
295
+ injectable: Injectable[T],
289
296
  mode: Mode | ModeStr,
290
- ):
297
+ ) -> Self:
291
298
  mode = Mode(mode)
292
299
  records = {
293
- cls: Record(injectable, mode)
294
- for cls in self.__classes_to_update(classes, mode)
300
+ report: Record(injectable, mode)
301
+ for report in self.__prepare_reports_for_updating(classes, mode)
295
302
  }
296
303
 
297
304
  if records:
@@ -303,43 +310,43 @@ class Container(Broker):
303
310
  return self
304
311
 
305
312
  @synchronized()
306
- def unlock(self):
313
+ def unlock(self) -> Self:
307
314
  for injectable in self.__injectables:
308
315
  injectable.unlock()
309
316
 
310
317
  return self
311
318
 
312
- def add_listener(self, listener: EventListener):
319
+ def add_listener(self, listener: EventListener) -> Self:
313
320
  self.__channel.add_listener(listener)
314
321
  return self
315
322
 
316
- def notify(self, event: Event):
323
+ def notify(self, event: Event) -> ContextManager:
317
324
  return self.__channel.dispatch(event)
318
325
 
319
- def __classes_to_update(
326
+ def __prepare_reports_for_updating(
320
327
  self,
321
328
  classes: Iterable[type | UnionType],
322
329
  mode: Mode,
323
- ) -> Iterator[type]:
330
+ ) -> Iterator[TypeReport]:
324
331
  rank = mode.rank
325
332
 
326
- for cls in frozenset(get_origins(*classes)):
333
+ for report in frozenset(analyze_types(*classes)):
327
334
  try:
328
- _, current_mode = self.__records[cls]
335
+ _, current_mode = self.__records[report]
329
336
 
330
337
  except KeyError:
331
338
  pass
332
339
 
333
340
  else:
334
- if mode == current_mode:
341
+ if mode == current_mode and mode != Mode.OVERRIDE:
335
342
  raise RuntimeError(
336
- f"An injectable already exists for the class `{format_type(cls)}`."
343
+ f"An injectable already exists for the class `{report.cls}`."
337
344
  )
338
345
 
339
346
  elif rank < current_mode.rank:
340
347
  continue
341
348
 
342
- yield cls
349
+ yield report
343
350
 
344
351
 
345
352
  """
@@ -347,16 +354,18 @@ Module
347
354
  """
348
355
 
349
356
 
350
- class Priority(str, Enum):
357
+ class Priority(StrEnum):
351
358
  LOW = "low"
352
359
  HIGH = "high"
353
360
 
354
361
  @classmethod
355
- def get_default(cls):
362
+ def get_default(cls) -> Priority:
356
363
  return cls.LOW
357
364
 
358
365
 
359
- PriorityStr = Literal["low", "high"]
366
+ type PriorityStr = Literal["low", "high"]
367
+
368
+ type InjectableFactory[T] = Callable[[Callable[..., T]], Injectable[T]]
360
369
 
361
370
 
362
371
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
@@ -372,14 +381,14 @@ class Module(EventListener, Broker):
372
381
  def __post_init__(self):
373
382
  self.__container.add_listener(self)
374
383
 
375
- def __getitem__(self, cls: type[_T] | UnionType, /) -> Injectable[_T]:
384
+ def __getitem__[T](self, cls: type[T] | UnionType, /) -> Injectable[T]:
376
385
  for broker in self.__brokers:
377
386
  with suppress(KeyError):
378
387
  return broker[cls]
379
388
 
380
389
  raise NoInjectable(cls)
381
390
 
382
- def __setitem__(self, cls: type | UnionType, injectable: Injectable, /):
391
+ def __setitem__[T](self, cls: type[T] | UnionType, injectable: Injectable[T], /):
383
392
  self.update((cls,), injectable)
384
393
 
385
394
  def __contains__(self, cls: type | UnionType, /) -> bool:
@@ -397,20 +406,20 @@ class Module(EventListener, Broker):
397
406
  yield from tuple(self.__modules)
398
407
  yield self.__container
399
408
 
400
- def injectable(
409
+ def injectable[T](
401
410
  self,
402
- wrapped: Callable[..., Any] = None,
411
+ wrapped: Callable[..., T] = None,
403
412
  /,
404
413
  *,
405
- cls: type[Injectable] = NewInjectable,
414
+ cls: InjectableFactory[T] = NewInjectable,
406
415
  inject: bool = True,
407
- on: type | Iterable[type] | UnionType = (),
416
+ on: TypeInfo[T] = (),
408
417
  mode: Mode | ModeStr = Mode.get_default(),
409
418
  ):
410
419
  def decorator(wp):
411
420
  factory = self.inject(wp, return_factory=True) if inject else wp
412
421
  injectable = cls(factory)
413
- classes = find_types(wp, on)
422
+ classes = get_return_types(wp, on)
414
423
  self.update(classes, injectable, mode)
415
424
  return wp
416
425
 
@@ -429,18 +438,18 @@ class Module(EventListener, Broker):
429
438
 
430
439
  return decorator(wrapped) if wrapped else decorator
431
440
 
432
- def set_constant(
441
+ def set_constant[T](
433
442
  self,
434
- instance: _T,
435
- on: type | Iterable[type] | UnionType = (),
443
+ instance: T,
444
+ on: TypeInfo[T] = (),
436
445
  *,
437
446
  mode: Mode | ModeStr = Mode.get_default(),
438
- ) -> _T:
447
+ ) -> T:
439
448
  cls = type(instance)
440
449
  self.injectable(
441
450
  lambda: instance,
442
451
  inject=False,
443
- on=(cls, on), # type: ignore
452
+ on=(cls, on),
444
453
  mode=mode,
445
454
  )
446
455
  return instance
@@ -468,22 +477,22 @@ class Module(EventListener, Broker):
468
477
 
469
478
  return decorator(wrapped) if wrapped else decorator
470
479
 
471
- def resolve(self, cls: type[_T]) -> _T:
480
+ def resolve[T](self, cls: type[T]) -> T:
472
481
  injectable = self[cls]
473
482
  return injectable.get_instance()
474
483
 
475
- def get_instance(self, cls: type[_T]) -> _T | None:
484
+ def get_instance[T](self, cls: type[T]) -> T | None:
476
485
  try:
477
486
  return self.resolve(cls)
478
487
  except KeyError:
479
488
  return None
480
489
 
481
- def get_lazy_instance(
490
+ def get_lazy_instance[T](
482
491
  self,
483
- cls: type[_T],
492
+ cls: type[T],
484
493
  *,
485
494
  cache: bool = False,
486
- ) -> Invertible[_T | None]:
495
+ ) -> Invertible[T | None]:
487
496
  if cache:
488
497
  return Lazy(lambda: self.get_instance(cls))
489
498
 
@@ -491,21 +500,30 @@ class Module(EventListener, Broker):
491
500
  function.set_owner(cls)
492
501
  return SimpleInvertible(function)
493
502
 
494
- def update(
503
+ def update[T](
495
504
  self,
496
- classes: Iterable[type | UnionType],
497
- injectable: Injectable,
505
+ classes: Iterable[type[T] | UnionType],
506
+ injectable: Injectable[T],
498
507
  mode: Mode | ModeStr = Mode.get_default(),
499
- ):
508
+ ) -> Self:
500
509
  self.__container.update(classes, injectable, mode)
501
510
  return self
502
511
 
512
+ def init_modules(self, *modules: Module) -> Self:
513
+ for module in tuple(self.__modules):
514
+ self.stop_using(module)
515
+
516
+ for module in modules:
517
+ self.use(module)
518
+
519
+ return self
520
+
503
521
  def use(
504
522
  self,
505
523
  module: Module,
506
524
  *,
507
525
  priority: Priority | PriorityStr = Priority.get_default(),
508
- ):
526
+ ) -> Self:
509
527
  if module is self:
510
528
  raise ModuleError("Module can't be used by itself.")
511
529
 
@@ -522,7 +540,7 @@ class Module(EventListener, Broker):
522
540
 
523
541
  return self
524
542
 
525
- def stop_using(self, module: Module):
543
+ def stop_using(self, module: Module) -> Self:
526
544
  event = ModuleRemoved(self, module)
527
545
 
528
546
  with suppress(KeyError):
@@ -543,7 +561,7 @@ class Module(EventListener, Broker):
543
561
  yield
544
562
  self.stop_using(module)
545
563
 
546
- def change_priority(self, module: Module, priority: Priority | PriorityStr):
564
+ def change_priority(self, module: Module, priority: Priority | PriorityStr) -> Self:
547
565
  priority = Priority(priority)
548
566
  event = ModulePriorityUpdated(self, module, priority)
549
567
 
@@ -553,17 +571,17 @@ class Module(EventListener, Broker):
553
571
  return self
554
572
 
555
573
  @synchronized()
556
- def unlock(self):
574
+ def unlock(self) -> Self:
557
575
  for broker in self.__brokers:
558
576
  broker.unlock()
559
577
 
560
578
  return self
561
579
 
562
- def add_listener(self, listener: EventListener):
580
+ def add_listener(self, listener: EventListener) -> Self:
563
581
  self.__channel.add_listener(listener)
564
582
  return self
565
583
 
566
- def remove_listener(self, listener: EventListener):
584
+ def remove_listener(self, listener: EventListener) -> Self:
567
585
  self.__channel.remove_listener(listener)
568
586
  return self
569
587
 
@@ -622,15 +640,15 @@ class Dependencies:
622
640
  return OrderedDict(self)
623
641
 
624
642
  @classmethod
625
- def from_mapping(cls, mapping: Mapping[str, Injectable]):
643
+ def from_mapping(cls, mapping: Mapping[str, Injectable]) -> Self:
626
644
  return cls(mapping=mapping)
627
645
 
628
646
  @classmethod
629
- def empty(cls):
647
+ def empty(cls) -> Self:
630
648
  return cls.from_mapping({})
631
649
 
632
650
  @classmethod
633
- def resolve(cls, signature: Signature, module: Module, owner: type = None):
651
+ def resolve(cls, signature: Signature, module: Module, owner: type = None) -> Self:
634
652
  dependencies = LazyMapping(cls.__resolver(signature, module, owner))
635
653
  return cls.from_mapping(dependencies)
636
654
 
@@ -684,8 +702,10 @@ class InjectedFunction(EventListener):
684
702
  update_wrapper(self, wrapped, updated=())
685
703
  self.__dependencies = Dependencies.empty()
686
704
  self.__owner = None
687
- self.__setup_queue = LimitedQueue[Callable[[], Any]]()
688
- self.on_setup(self.__set_signature)
705
+
706
+ queue = Queue[Callable[[], Any]]()
707
+ queue.put_nowait(self.__set_signature)
708
+ self.__setup_queue = queue
689
709
 
690
710
  def __repr__(self) -> str: # pragma: no cover
691
711
  return repr(self.wrapped)
@@ -694,13 +714,17 @@ class InjectedFunction(EventListener):
694
714
  return str(self.wrapped)
695
715
 
696
716
  def __call__(self, /, *args, **kwargs) -> Any:
697
- for function in self.__setup_queue:
698
- function()
717
+ queue = self.__setup_queue
718
+
719
+ while not queue.empty():
720
+ setup = queue.get()
721
+ setup()
722
+ queue.task_done()
699
723
 
700
724
  arguments = self.bind(args, kwargs)
701
725
  return self.wrapped(*arguments.args, **arguments.kwargs)
702
726
 
703
- def __get__(self, instance: object = None, owner: type = None):
727
+ def __get__(self, instance: object = None, owner: type = None) -> Self | MethodType:
704
728
  if instance is None:
705
729
  return self
706
730
 
@@ -734,7 +758,7 @@ class InjectedFunction(EventListener):
734
758
  )
735
759
  return Arguments(bound.args, bound.kwargs)
736
760
 
737
- def set_owner(self, owner: type):
761
+ def set_owner(self, owner: type) -> Self:
738
762
  if self.__dependencies.are_resolved:
739
763
  raise TypeError(
740
764
  "Function owner must be assigned before dependencies are resolved."
@@ -747,19 +771,19 @@ class InjectedFunction(EventListener):
747
771
  return self
748
772
 
749
773
  @synchronized()
750
- def update(self, module: Module):
774
+ def update(self, module: Module) -> Self:
751
775
  self.__dependencies = Dependencies.resolve(self.signature, module, self.__owner)
752
776
  return self
753
777
 
754
778
  def on_setup(self, wrapped: Callable[[], Any] = None, /):
755
779
  def decorator(wp):
756
- self.__setup_queue.add(wp)
780
+ self.__setup_queue.put(wp)
757
781
  return wp
758
782
 
759
783
  return decorator(wrapped) if wrapped else decorator
760
784
 
761
785
  @singledispatchmethod
762
- def on_event(self, event: Event, /) -> ContextManager | None: # type: ignore
786
+ def on_event(self, event: Event, /) -> None: # type: ignore
763
787
  return None
764
788
 
765
789
  @on_event.register
@@ -768,7 +792,7 @@ class InjectedFunction(EventListener):
768
792
  yield
769
793
  self.update(event.on_module)
770
794
 
771
- def __set_signature(self):
795
+ def __set_signature(self) -> Self:
772
796
  self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
773
797
  return self
774
798
 
@@ -781,12 +805,15 @@ class InjectedFunction(EventListener):
781
805
  self.__update_vars(variables)
782
806
 
783
807
  def __update_vars(self, variables: Mapping[str, Any]):
784
- def is_dunder(var: str) -> bool:
785
- return var.startswith("__") and var.endswith("__")
786
-
787
- restricted_vars = frozenset(var for var in dir(self) if not is_dunder(var))
808
+ restricted_vars = frozenset(
809
+ var for var in dir(self) if not self.__is_dunder(var)
810
+ )
788
811
  vars(self).update(
789
812
  (var, value)
790
813
  for var, value in variables.items()
791
814
  if var not in restricted_vars
792
815
  )
816
+
817
+ @staticmethod
818
+ def __is_dunder(var: str) -> bool:
819
+ return var.startswith("__") and var.endswith("__")
injection/exceptions.py CHANGED
@@ -1,7 +1,5 @@
1
1
  from typing import Any
2
2
 
3
- from injection.common.tools.type import format_type
4
-
5
3
  __all__ = (
6
4
  "InjectionError",
7
5
  "NoInjectable",
@@ -15,15 +13,15 @@ class InjectionError(Exception):
15
13
  pass
16
14
 
17
15
 
18
- class NoInjectable(KeyError, InjectionError):
16
+ class NoInjectable[T](KeyError, InjectionError):
19
17
  __slots__ = ("__class",)
20
18
 
21
- def __init__(self, cls: type | Any):
22
- super().__init__(f"No injectable for `{format_type(cls)}`.")
19
+ def __init__(self, cls: type[T] | Any):
20
+ super().__init__(f"No injectable for `{cls}`.")
23
21
  self.__class = cls
24
22
 
25
23
  @property
26
- def cls(self) -> type:
24
+ def cls(self) -> type[T]:
27
25
  return self.__class
28
26
 
29
27
 
@@ -1,4 +1,4 @@
1
- from typing import Any, TypeVar
1
+ from typing import Any
2
2
 
3
3
  from rodi import ContainerProtocol
4
4
 
@@ -6,8 +6,6 @@ from injection import Module, default_module
6
6
 
7
7
  __all__ = ("InjectionServices",)
8
8
 
9
- _T = TypeVar("_T")
10
-
11
9
 
12
10
  class InjectionServices(ContainerProtocol):
13
11
  """
@@ -26,5 +24,5 @@ class InjectionServices(ContainerProtocol):
26
24
  self.__module.injectable(obj_type)
27
25
  return self
28
26
 
29
- def resolve(self, obj_type: type[_T] | Any, *args, **kwargs) -> _T:
27
+ def resolve[T](self, obj_type: type[T] | Any, *args, **kwargs) -> T:
30
28
  return self.__module.resolve(obj_type)
@@ -1,16 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.8.4.post2
3
+ Version: 0.9.0
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
7
7
  Keywords: dependencies,inject,injection
8
8
  Author: remimd
9
- Requires-Python: >=3.10,<4
9
+ Requires-Python: >=3.12,<4
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
12
  Classifier: Programming Language :: Python :: 3.12
15
13
  Project-URL: Repository, https://github.com/100nm/python-injection
16
14
  Description-Content-Type: text/markdown
@@ -0,0 +1,19 @@
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,,
injection/common/queue.py DELETED
@@ -1,63 +0,0 @@
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
- __all__ = ("LimitedQueue",)
8
-
9
- _T = TypeVar("_T")
10
-
11
-
12
- class Queue(Iterator[_T], Protocol):
13
- __slots__ = ()
14
-
15
- @abstractmethod
16
- def add(self, item: _T):
17
- raise NotImplementedError
18
-
19
-
20
- @dataclass(repr=False, frozen=True, slots=True)
21
- class SimpleQueue(Queue[_T]):
22
- __items: deque[_T] = field(default_factory=deque, init=False)
23
-
24
- def __next__(self) -> _T:
25
- try:
26
- return self.__items.popleft()
27
- except IndexError as exc:
28
- raise StopIteration from exc
29
-
30
- def add(self, item: _T):
31
- self.__items.append(item)
32
- return self
33
-
34
-
35
- class DeadQueue(Queue[_T]):
36
- __slots__ = ()
37
-
38
- def __bool__(self) -> bool:
39
- return False
40
-
41
- def __next__(self) -> NoReturn:
42
- raise StopIteration
43
-
44
- def add(self, item: _T) -> NoReturn:
45
- raise TypeError("Queue is dead.")
46
-
47
-
48
- @dataclass(repr=False, slots=True)
49
- class LimitedQueue(Queue[_T]):
50
- __state: Queue[_T] = field(default_factory=SimpleQueue)
51
-
52
- def __next__(self) -> _T:
53
- try:
54
- return next(self.__state)
55
- except StopIteration as exc:
56
- if self.__state:
57
- self.__state = DeadQueue()
58
-
59
- raise exc
60
-
61
- def add(self, item: _T):
62
- self.__state.add(item)
63
- return self
@@ -1,20 +0,0 @@
1
- injection/__init__.py,sha256=Bf6S99E2srD3752xlJf3uAdiGIzY2YHOZafcwEiwY70,739
2
- injection/__init__.pyi,sha256=fNTW5TUZQmaxPooaa_vJ_nyR_-DqZ13hSWHh_TsdeOo,5913
3
- injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- injection/common/event.py,sha256=TvkFv-5zF_oUTPhh5U0BKD5HanvCJKHA0H7yceMRy5c,1261
5
- injection/common/invertible.py,sha256=BZnkDg_NZDsTXGca5w5Wg_nYE3oRO_Wlodi2XtE55ck,595
6
- injection/common/lazy.py,sha256=1C34uoG229Gl0DEUcD9-eQrL4K_oIofOLzdQ1SiY6rw,1401
7
- injection/common/queue.py,sha256=mV0AGxp5aYMr438MxmoIsZcV5jmqi5x_GD2S-utrnzA,1443
8
- injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- injection/common/tools/threading.py,sha256=RAtzBFLVNJMflWIHxrP83fjafnFq8_JLgFoYQg8nVyE,182
10
- injection/common/tools/type.py,sha256=05fD5UkUI1kPoFWEjQz4j266SYfJQMK-Ti88JXNxb_M,1276
11
- injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
12
- injection/core/module.py,sha256=jofZzYk_-1Rsfj-fDrPs-64RfVXvnMCWcKk2NSCsyTk,20507
13
- injection/exceptions.py,sha256=f2lVSTAx-Nv89s0skn15y-sCkr656ROuWYs-XlrcEn8,683
14
- injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- injection/integrations/blacksheep.py,sha256=dFXzrxkJRy9z44CcwG0ROYshT3zUZTVyuQkRy390RvU,765
16
- injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
18
- python_injection-0.8.4.post2.dist-info/METADATA,sha256=V-_8VSQ0z_JX5t32nfkbXOuJA-9b7ykveITH1S54gHQ,3678
19
- python_injection-0.8.4.post2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
20
- python_injection-0.8.4.post2.dist-info/RECORD,,