python-injection 0.18.13__py3-none-any.whl → 0.19.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.
- injection/__init__.py +2 -0
- injection/__init__.pyi +44 -23
- injection/_core/asfunction.py +47 -0
- injection/_core/common/asynchronous.py +20 -14
- injection/_core/common/threading.py +1 -1
- injection/_core/descriptors.py +1 -1
- injection/_core/injectables.py +2 -5
- injection/_core/module.py +34 -24
- injection/_core/scope.py +3 -3
- injection/entrypoint.py +16 -16
- injection/ext/fastapi.py +5 -5
- injection/loaders.py +8 -2
- {python_injection-0.18.13.dist-info → python_injection-0.19.0.dist-info}/METADATA +20 -7
- python_injection-0.19.0.dist-info/RECORD +30 -0
- python_injection-0.18.13.dist-info/RECORD +0 -29
- {python_injection-0.18.13.dist-info → python_injection-0.19.0.dist-info}/WHEEL +0 -0
- {python_injection-0.18.13.dist-info → python_injection-0.19.0.dist-info}/licenses/LICENSE +0 -0
injection/__init__.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from ._core.asfunction import asfunction
|
1
2
|
from ._core.descriptors import LazyInstance
|
2
3
|
from ._core.injectables import Injectable
|
3
4
|
from ._core.module import Mode, Module, Priority, mod
|
@@ -18,6 +19,7 @@ __all__ = (
|
|
18
19
|
"afind_instance",
|
19
20
|
"aget_instance",
|
20
21
|
"aget_lazy_instance",
|
22
|
+
"asfunction",
|
21
23
|
"constant",
|
22
24
|
"define_scope",
|
23
25
|
"find_instance",
|
injection/__init__.pyi
CHANGED
@@ -30,19 +30,35 @@ set_constant = __MODULE.set_constant
|
|
30
30
|
should_be_injectable = __MODULE.should_be_injectable
|
31
31
|
singleton = __MODULE.singleton
|
32
32
|
|
33
|
+
@overload
|
34
|
+
def asfunction[**P, T](
|
35
|
+
wrapped: type[Callable[P, T]],
|
36
|
+
/,
|
37
|
+
*,
|
38
|
+
module: Module = ...,
|
39
|
+
threadsafe: bool | None = ...,
|
40
|
+
) -> Callable[P, T]: ...
|
41
|
+
@overload
|
42
|
+
def asfunction[**P, T](
|
43
|
+
wrapped: None = ...,
|
44
|
+
/,
|
45
|
+
*,
|
46
|
+
module: Module = ...,
|
47
|
+
threadsafe: bool | None = ...,
|
48
|
+
) -> Callable[[type[Callable[P, T]]], Callable[P, T]]: ...
|
33
49
|
@asynccontextmanager
|
34
50
|
def adefine_scope(
|
35
51
|
name: str,
|
36
52
|
/,
|
37
53
|
kind: ScopeKind | ScopeKindStr = ...,
|
38
|
-
threadsafe: bool = ...,
|
54
|
+
threadsafe: bool | None = ...,
|
39
55
|
) -> AsyncIterator[Scope]: ...
|
40
56
|
@contextmanager
|
41
57
|
def define_scope(
|
42
58
|
name: str,
|
43
59
|
/,
|
44
60
|
kind: ScopeKind | ScopeKindStr = ...,
|
45
|
-
threadsafe: bool = ...,
|
61
|
+
threadsafe: bool | None = ...,
|
46
62
|
) -> Iterator[Scope]: ...
|
47
63
|
def mod(name: str = ..., /) -> Module:
|
48
64
|
"""
|
@@ -80,7 +96,7 @@ class LazyInstance[T]:
|
|
80
96
|
default: T = ...,
|
81
97
|
*,
|
82
98
|
module: Module = ...,
|
83
|
-
threadsafe: bool = ...,
|
99
|
+
threadsafe: bool | None = ...,
|
84
100
|
) -> None: ...
|
85
101
|
@overload
|
86
102
|
def __get__(self, instance: object, owner: type | None = ...) -> T: ...
|
@@ -108,14 +124,14 @@ class Module:
|
|
108
124
|
wrapped: Callable[P, T] = ...,
|
109
125
|
/,
|
110
126
|
*,
|
111
|
-
threadsafe: bool = ...,
|
127
|
+
threadsafe: bool | None = ...,
|
112
128
|
) -> Any:
|
113
129
|
"""
|
114
130
|
Decorator applicable to a class or function. Inject function dependencies using
|
115
131
|
parameter type annotations. If applied to a class, the dependencies resolved
|
116
132
|
will be those of the `__init__` method.
|
117
133
|
|
118
|
-
With `threadsafe=True`, the injection logic is wrapped in a `threading.
|
134
|
+
With `threadsafe=True`, the injection logic is wrapped in a `threading.RLock`.
|
119
135
|
"""
|
120
136
|
|
121
137
|
def injectable[**P, T](
|
@@ -149,7 +165,7 @@ class Module:
|
|
149
165
|
always be the same.
|
150
166
|
"""
|
151
167
|
|
152
|
-
def scoped[
|
168
|
+
def scoped[T](
|
153
169
|
self,
|
154
170
|
scope_name: str,
|
155
171
|
/,
|
@@ -171,18 +187,18 @@ class Module:
|
|
171
187
|
registered.
|
172
188
|
"""
|
173
189
|
|
174
|
-
def constant[T](
|
190
|
+
def constant[**P, T](
|
175
191
|
self,
|
176
|
-
wrapped:
|
192
|
+
wrapped: _Recipe[P, T] = ...,
|
177
193
|
/,
|
178
194
|
*,
|
179
195
|
on: _TypeInfo[T] = ...,
|
180
196
|
mode: Mode | ModeStr = ...,
|
181
197
|
) -> Any:
|
182
198
|
"""
|
183
|
-
Decorator applicable to a class. It is used to indicate how the
|
184
|
-
constructed. At injection time, the injected instance will always
|
185
|
-
Unlike `@singleton`, dependencies will not be resolved.
|
199
|
+
Decorator applicable to a class or function. It is used to indicate how the
|
200
|
+
constant is constructed. At injection time, the injected instance will always
|
201
|
+
be the same. Unlike `@singleton`, dependencies will not be resolved.
|
186
202
|
"""
|
187
203
|
|
188
204
|
def set_constant[T](
|
@@ -211,21 +227,26 @@ class Module:
|
|
211
227
|
self,
|
212
228
|
wrapped: Callable[P, T],
|
213
229
|
/,
|
214
|
-
threadsafe: bool = ...,
|
230
|
+
threadsafe: bool | None = ...,
|
215
231
|
) -> Callable[P, T]: ...
|
216
232
|
def make_async_factory[T](
|
217
233
|
self,
|
218
234
|
wrapped: type[T],
|
219
235
|
/,
|
220
|
-
threadsafe: bool = ...,
|
236
|
+
threadsafe: bool | None = ...,
|
221
237
|
) -> Callable[..., Awaitable[T]]: ...
|
222
238
|
async def afind_instance[T](
|
223
239
|
self,
|
224
240
|
cls: _InputType[T],
|
225
241
|
*,
|
226
|
-
threadsafe: bool = ...,
|
242
|
+
threadsafe: bool | None = ...,
|
227
243
|
) -> T: ...
|
228
|
-
def find_instance[T](
|
244
|
+
def find_instance[T](
|
245
|
+
self,
|
246
|
+
cls: _InputType[T],
|
247
|
+
*,
|
248
|
+
threadsafe: bool | None = ...,
|
249
|
+
) -> T:
|
229
250
|
"""
|
230
251
|
Function used to retrieve an instance associated with the type passed in
|
231
252
|
parameter or an exception will be raised.
|
@@ -237,7 +258,7 @@ class Module:
|
|
237
258
|
cls: _InputType[T],
|
238
259
|
default: Default,
|
239
260
|
*,
|
240
|
-
threadsafe: bool = ...,
|
261
|
+
threadsafe: bool | None = ...,
|
241
262
|
) -> T | Default: ...
|
242
263
|
@overload
|
243
264
|
async def aget_instance[T](
|
@@ -245,7 +266,7 @@ class Module:
|
|
245
266
|
cls: _InputType[T],
|
246
267
|
default: T = ...,
|
247
268
|
*,
|
248
|
-
threadsafe: bool = ...,
|
269
|
+
threadsafe: bool | None = ...,
|
249
270
|
) -> T: ...
|
250
271
|
@overload
|
251
272
|
def get_instance[T, Default](
|
@@ -253,7 +274,7 @@ class Module:
|
|
253
274
|
cls: _InputType[T],
|
254
275
|
default: Default,
|
255
276
|
*,
|
256
|
-
threadsafe: bool = ...,
|
277
|
+
threadsafe: bool | None = ...,
|
257
278
|
) -> T | Default:
|
258
279
|
"""
|
259
280
|
Function used to retrieve an instance associated with the type passed in
|
@@ -266,7 +287,7 @@ class Module:
|
|
266
287
|
cls: _InputType[T],
|
267
288
|
default: T = ...,
|
268
289
|
*,
|
269
|
-
threadsafe: bool = ...,
|
290
|
+
threadsafe: bool | None = ...,
|
270
291
|
) -> T: ...
|
271
292
|
@overload
|
272
293
|
def aget_lazy_instance[T, Default](
|
@@ -274,7 +295,7 @@ class Module:
|
|
274
295
|
cls: _InputType[T],
|
275
296
|
default: Default,
|
276
297
|
*,
|
277
|
-
threadsafe: bool = ...,
|
298
|
+
threadsafe: bool | None = ...,
|
278
299
|
) -> Awaitable[T | Default]: ...
|
279
300
|
@overload
|
280
301
|
def aget_lazy_instance[T](
|
@@ -282,7 +303,7 @@ class Module:
|
|
282
303
|
cls: _InputType[T],
|
283
304
|
default: T = ...,
|
284
305
|
*,
|
285
|
-
threadsafe: bool = ...,
|
306
|
+
threadsafe: bool | None = ...,
|
286
307
|
) -> Awaitable[T]: ...
|
287
308
|
@overload
|
288
309
|
def get_lazy_instance[T, Default](
|
@@ -290,7 +311,7 @@ class Module:
|
|
290
311
|
cls: _InputType[T],
|
291
312
|
default: Default,
|
292
313
|
*,
|
293
|
-
threadsafe: bool = ...,
|
314
|
+
threadsafe: bool | None = ...,
|
294
315
|
) -> _Invertible[T | Default]:
|
295
316
|
"""
|
296
317
|
Function used to retrieve an instance associated with the type passed in
|
@@ -306,7 +327,7 @@ class Module:
|
|
306
327
|
cls: _InputType[T],
|
307
328
|
default: T = ...,
|
308
329
|
*,
|
309
|
-
threadsafe: bool = ...,
|
330
|
+
threadsafe: bool | None = ...,
|
310
331
|
) -> _Invertible[T]: ...
|
311
332
|
def init_modules(self, *modules: Module) -> Self:
|
312
333
|
"""
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from functools import wraps
|
3
|
+
from inspect import iscoroutinefunction
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from injection._core.common.asynchronous import Caller
|
7
|
+
from injection._core.module import Module, mod
|
8
|
+
|
9
|
+
|
10
|
+
def asfunction[**P, T](
|
11
|
+
wrapped: type[Callable[P, T]] | None = None,
|
12
|
+
/,
|
13
|
+
*,
|
14
|
+
module: Module | None = None,
|
15
|
+
threadsafe: bool | None = None,
|
16
|
+
) -> Any:
|
17
|
+
module = module or mod()
|
18
|
+
|
19
|
+
def decorator(wp: type[Callable[P, T]]) -> Callable[P, T]:
|
20
|
+
get_method = wp.__call__.__get__
|
21
|
+
method = get_method(NotImplemented)
|
22
|
+
factory: Caller[..., Callable[P, T]] = module.make_injected_function(
|
23
|
+
wp,
|
24
|
+
threadsafe=threadsafe,
|
25
|
+
).__inject_metadata__
|
26
|
+
|
27
|
+
wrapper: Callable[P, T]
|
28
|
+
|
29
|
+
if iscoroutinefunction(method):
|
30
|
+
|
31
|
+
@wraps(method)
|
32
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
|
33
|
+
self = await factory.acall()
|
34
|
+
return await get_method(self)(*args, **kwargs)
|
35
|
+
|
36
|
+
else:
|
37
|
+
|
38
|
+
@wraps(method)
|
39
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
40
|
+
self = factory.call()
|
41
|
+
return get_method(self)(*args, **kwargs)
|
42
|
+
|
43
|
+
wrapper.__name__ = wp.__name__
|
44
|
+
wrapper.__qualname__ = wp.__qualname__
|
45
|
+
return wrapper
|
46
|
+
|
47
|
+
return decorator(wrapped) if wrapped else decorator
|
@@ -1,7 +1,26 @@
|
|
1
1
|
from abc import abstractmethod
|
2
2
|
from collections.abc import Awaitable, Callable, Generator
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import
|
4
|
+
from typing import (
|
5
|
+
Any,
|
6
|
+
AsyncContextManager,
|
7
|
+
NoReturn,
|
8
|
+
Protocol,
|
9
|
+
runtime_checkable,
|
10
|
+
)
|
11
|
+
|
12
|
+
AsyncSemaphore: Callable[[int], AsyncContextManager[Any]]
|
13
|
+
|
14
|
+
try:
|
15
|
+
import anyio
|
16
|
+
|
17
|
+
except ImportError: # pragma: no cover
|
18
|
+
import asyncio
|
19
|
+
|
20
|
+
AsyncSemaphore = asyncio.Semaphore
|
21
|
+
|
22
|
+
else:
|
23
|
+
AsyncSemaphore = anyio.Semaphore
|
5
24
|
|
6
25
|
|
7
26
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
@@ -45,16 +64,3 @@ class SyncCaller[**P, T](Caller[P, T]):
|
|
45
64
|
|
46
65
|
def call(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
47
66
|
return self.callable(*args, **kwargs)
|
48
|
-
|
49
|
-
|
50
|
-
try:
|
51
|
-
import anyio
|
52
|
-
|
53
|
-
def create_semaphore(value: int) -> AsyncContextManager[Any]:
|
54
|
-
return anyio.Semaphore(value)
|
55
|
-
|
56
|
-
except ImportError: # pragma: no cover
|
57
|
-
import asyncio
|
58
|
-
|
59
|
-
def create_semaphore(value: int) -> AsyncContextManager[Any]:
|
60
|
-
return asyncio.Semaphore(value)
|
injection/_core/descriptors.py
CHANGED
@@ -17,7 +17,7 @@ class LazyInstance[T]:
|
|
17
17
|
default: T = NotImplemented,
|
18
18
|
*,
|
19
19
|
module: Module | None = None,
|
20
|
-
threadsafe: bool =
|
20
|
+
threadsafe: bool | None = None,
|
21
21
|
) -> None:
|
22
22
|
module = module or mod()
|
23
23
|
self.__value = module.get_lazy_instance(cls, default, threadsafe=threadsafe)
|
injection/_core/injectables.py
CHANGED
@@ -13,10 +13,7 @@ from typing import (
|
|
13
13
|
runtime_checkable,
|
14
14
|
)
|
15
15
|
|
16
|
-
from injection._core.common.asynchronous import Caller
|
17
|
-
from injection._core.common.asynchronous import (
|
18
|
-
create_semaphore as _create_async_semaphore,
|
19
|
-
)
|
16
|
+
from injection._core.common.asynchronous import AsyncSemaphore, Caller
|
20
17
|
from injection._core.scope import (
|
21
18
|
Scope,
|
22
19
|
get_scope,
|
@@ -64,7 +61,7 @@ class CacheLogic[T]:
|
|
64
61
|
__semaphore: AsyncContextManager[Any]
|
65
62
|
|
66
63
|
def __init__(self) -> None:
|
67
|
-
self.__semaphore =
|
64
|
+
self.__semaphore = AsyncSemaphore(1)
|
68
65
|
|
69
66
|
async def aget_or_create[K](
|
70
67
|
self,
|
injection/_core/module.py
CHANGED
@@ -503,15 +503,15 @@ class Module(Broker, EventListener):
|
|
503
503
|
|
504
504
|
return decorator(wrapped) if wrapped else decorator
|
505
505
|
|
506
|
-
def constant[T](
|
506
|
+
def constant[**P, T](
|
507
507
|
self,
|
508
|
-
wrapped:
|
508
|
+
wrapped: Recipe[P, T] | None = None,
|
509
509
|
/,
|
510
510
|
*,
|
511
511
|
on: TypeInfo[T] = (),
|
512
512
|
mode: Mode | ModeStr = Mode.get_default(),
|
513
513
|
) -> Any:
|
514
|
-
def decorator(wp:
|
514
|
+
def decorator(wp: Recipe[P, T]) -> Recipe[P, T]:
|
515
515
|
lazy_instance = lazy(wp)
|
516
516
|
self.injectable(
|
517
517
|
lambda: ~lazy_instance,
|
@@ -560,7 +560,7 @@ class Module(Broker, EventListener):
|
|
560
560
|
wrapped: Callable[P, T] | None = None,
|
561
561
|
/,
|
562
562
|
*,
|
563
|
-
threadsafe: bool =
|
563
|
+
threadsafe: bool | None = None,
|
564
564
|
) -> Any:
|
565
565
|
def decorator(wp: Callable[P, T]) -> Callable[P, T]:
|
566
566
|
if isclass(wp):
|
@@ -576,7 +576,7 @@ class Module(Broker, EventListener):
|
|
576
576
|
self,
|
577
577
|
wrapped: Callable[P, T],
|
578
578
|
/,
|
579
|
-
threadsafe: bool = ...,
|
579
|
+
threadsafe: bool | None = ...,
|
580
580
|
) -> SyncInjectedFunction[P, T]: ...
|
581
581
|
|
582
582
|
@overload
|
@@ -584,14 +584,14 @@ class Module(Broker, EventListener):
|
|
584
584
|
self,
|
585
585
|
wrapped: Callable[P, Awaitable[T]],
|
586
586
|
/,
|
587
|
-
threadsafe: bool = ...,
|
587
|
+
threadsafe: bool | None = ...,
|
588
588
|
) -> AsyncInjectedFunction[P, T]: ...
|
589
589
|
|
590
590
|
def make_injected_function[**P, T](
|
591
591
|
self,
|
592
592
|
wrapped: Callable[P, T],
|
593
593
|
/,
|
594
|
-
threadsafe: bool =
|
594
|
+
threadsafe: bool | None = None,
|
595
595
|
) -> InjectedFunction[P, T]:
|
596
596
|
metadata = InjectMetadata(wrapped, threadsafe)
|
597
597
|
|
@@ -609,7 +609,7 @@ class Module(Broker, EventListener):
|
|
609
609
|
self,
|
610
610
|
wrapped: type[T],
|
611
611
|
/,
|
612
|
-
threadsafe: bool =
|
612
|
+
threadsafe: bool | None = None,
|
613
613
|
) -> Callable[..., Awaitable[T]]:
|
614
614
|
factory: InjectedFunction[..., T] = self.make_injected_function(
|
615
615
|
wrapped,
|
@@ -621,13 +621,18 @@ class Module(Broker, EventListener):
|
|
621
621
|
self,
|
622
622
|
cls: InputType[T],
|
623
623
|
*,
|
624
|
-
threadsafe: bool =
|
624
|
+
threadsafe: bool | None = None,
|
625
625
|
) -> T:
|
626
626
|
with get_lock(threadsafe):
|
627
627
|
injectable = self[cls]
|
628
628
|
return await injectable.aget_instance()
|
629
629
|
|
630
|
-
def find_instance[T](
|
630
|
+
def find_instance[T](
|
631
|
+
self,
|
632
|
+
cls: InputType[T],
|
633
|
+
*,
|
634
|
+
threadsafe: bool | None = None,
|
635
|
+
) -> T:
|
631
636
|
with get_lock(threadsafe):
|
632
637
|
injectable = self[cls]
|
633
638
|
return injectable.get_instance()
|
@@ -638,7 +643,7 @@ class Module(Broker, EventListener):
|
|
638
643
|
cls: InputType[T],
|
639
644
|
default: Default,
|
640
645
|
*,
|
641
|
-
threadsafe: bool = ...,
|
646
|
+
threadsafe: bool | None = ...,
|
642
647
|
) -> T | Default: ...
|
643
648
|
|
644
649
|
@overload
|
@@ -647,7 +652,7 @@ class Module(Broker, EventListener):
|
|
647
652
|
cls: InputType[T],
|
648
653
|
default: T = ...,
|
649
654
|
*,
|
650
|
-
threadsafe: bool = ...,
|
655
|
+
threadsafe: bool | None = ...,
|
651
656
|
) -> T: ...
|
652
657
|
|
653
658
|
async def aget_instance[T, Default](
|
@@ -655,7 +660,7 @@ class Module(Broker, EventListener):
|
|
655
660
|
cls: InputType[T],
|
656
661
|
default: Default = NotImplemented,
|
657
662
|
*,
|
658
|
-
threadsafe: bool =
|
663
|
+
threadsafe: bool | None = None,
|
659
664
|
) -> T | Default:
|
660
665
|
try:
|
661
666
|
return await self.afind_instance(cls, threadsafe=threadsafe)
|
@@ -668,7 +673,7 @@ class Module(Broker, EventListener):
|
|
668
673
|
cls: InputType[T],
|
669
674
|
default: Default,
|
670
675
|
*,
|
671
|
-
threadsafe: bool = ...,
|
676
|
+
threadsafe: bool | None = ...,
|
672
677
|
) -> T | Default: ...
|
673
678
|
|
674
679
|
@overload
|
@@ -677,7 +682,7 @@ class Module(Broker, EventListener):
|
|
677
682
|
cls: InputType[T],
|
678
683
|
default: T = ...,
|
679
684
|
*,
|
680
|
-
threadsafe: bool = ...,
|
685
|
+
threadsafe: bool | None = ...,
|
681
686
|
) -> T: ...
|
682
687
|
|
683
688
|
def get_instance[T, Default](
|
@@ -685,7 +690,7 @@ class Module(Broker, EventListener):
|
|
685
690
|
cls: InputType[T],
|
686
691
|
default: Default = NotImplemented,
|
687
692
|
*,
|
688
|
-
threadsafe: bool =
|
693
|
+
threadsafe: bool | None = None,
|
689
694
|
) -> T | Default:
|
690
695
|
try:
|
691
696
|
return self.find_instance(cls, threadsafe=threadsafe)
|
@@ -698,7 +703,7 @@ class Module(Broker, EventListener):
|
|
698
703
|
cls: InputType[T],
|
699
704
|
default: Default,
|
700
705
|
*,
|
701
|
-
threadsafe: bool = ...,
|
706
|
+
threadsafe: bool | None = ...,
|
702
707
|
) -> Awaitable[T | Default]: ...
|
703
708
|
|
704
709
|
@overload
|
@@ -707,7 +712,7 @@ class Module(Broker, EventListener):
|
|
707
712
|
cls: InputType[T],
|
708
713
|
default: T = ...,
|
709
714
|
*,
|
710
|
-
threadsafe: bool = ...,
|
715
|
+
threadsafe: bool | None = ...,
|
711
716
|
) -> Awaitable[T]: ...
|
712
717
|
|
713
718
|
def aget_lazy_instance[T, Default](
|
@@ -715,7 +720,7 @@ class Module(Broker, EventListener):
|
|
715
720
|
cls: InputType[T],
|
716
721
|
default: Default = NotImplemented,
|
717
722
|
*,
|
718
|
-
threadsafe: bool =
|
723
|
+
threadsafe: bool | None = None,
|
719
724
|
) -> Awaitable[T | Default]:
|
720
725
|
function = self.make_injected_function(
|
721
726
|
lambda instance=default: instance,
|
@@ -730,7 +735,7 @@ class Module(Broker, EventListener):
|
|
730
735
|
cls: InputType[T],
|
731
736
|
default: Default,
|
732
737
|
*,
|
733
|
-
threadsafe: bool = ...,
|
738
|
+
threadsafe: bool | None = ...,
|
734
739
|
) -> Invertible[T | Default]: ...
|
735
740
|
|
736
741
|
@overload
|
@@ -739,7 +744,7 @@ class Module(Broker, EventListener):
|
|
739
744
|
cls: InputType[T],
|
740
745
|
default: T = ...,
|
741
746
|
*,
|
742
|
-
threadsafe: bool = ...,
|
747
|
+
threadsafe: bool | None = ...,
|
743
748
|
) -> Invertible[T]: ...
|
744
749
|
|
745
750
|
def get_lazy_instance[T, Default](
|
@@ -747,7 +752,7 @@ class Module(Broker, EventListener):
|
|
747
752
|
cls: InputType[T],
|
748
753
|
default: Default = NotImplemented,
|
749
754
|
*,
|
750
|
-
threadsafe: bool =
|
755
|
+
threadsafe: bool | None = None,
|
751
756
|
) -> Invertible[T | Default]:
|
752
757
|
function = self.make_injected_function(
|
753
758
|
lambda instance=default: instance,
|
@@ -1013,7 +1018,12 @@ class InjectMetadata[**P, T](Caller[P, T], EventListener):
|
|
1013
1018
|
__tasks: deque[Callable[..., Any]]
|
1014
1019
|
__wrapped: Callable[P, T]
|
1015
1020
|
|
1016
|
-
def __init__(
|
1021
|
+
def __init__(
|
1022
|
+
self,
|
1023
|
+
wrapped: Callable[P, T],
|
1024
|
+
/,
|
1025
|
+
threadsafe: bool | None = None,
|
1026
|
+
) -> None:
|
1017
1027
|
self.__dependencies = Dependencies.empty()
|
1018
1028
|
self.__lock = get_lock(threadsafe)
|
1019
1029
|
self.__owner = None
|
@@ -1087,7 +1097,7 @@ class InjectMetadata[**P, T](Caller[P, T], EventListener):
|
|
1087
1097
|
return decorator(wrapped) if wrapped else decorator
|
1088
1098
|
|
1089
1099
|
@singledispatchmethod
|
1090
|
-
def on_event(self, event: Event, /) -> ContextManager[None] | None:
|
1100
|
+
def on_event(self, event: Event, /) -> ContextManager[None] | None:
|
1091
1101
|
return None
|
1092
1102
|
|
1093
1103
|
@on_event.register
|
injection/_core/scope.py
CHANGED
@@ -130,7 +130,7 @@ async def adefine_scope(
|
|
130
130
|
name: str,
|
131
131
|
/,
|
132
132
|
kind: ScopeKind | ScopeKindStr = ScopeKind.get_default(),
|
133
|
-
threadsafe: bool =
|
133
|
+
threadsafe: bool | None = None,
|
134
134
|
) -> AsyncIterator[ScopeFacade]:
|
135
135
|
async with AsyncScope() as scope:
|
136
136
|
with _bind_scope(name, scope, kind, threadsafe) as facade:
|
@@ -142,7 +142,7 @@ def define_scope(
|
|
142
142
|
name: str,
|
143
143
|
/,
|
144
144
|
kind: ScopeKind | ScopeKindStr = ScopeKind.get_default(),
|
145
|
-
threadsafe: bool =
|
145
|
+
threadsafe: bool | None = None,
|
146
146
|
) -> Iterator[ScopeFacade]:
|
147
147
|
with SyncScope() as scope:
|
148
148
|
with _bind_scope(name, scope, kind, threadsafe) as facade:
|
@@ -194,7 +194,7 @@ def _bind_scope(
|
|
194
194
|
name: str,
|
195
195
|
scope: Scope,
|
196
196
|
kind: ScopeKind | ScopeKindStr,
|
197
|
-
threadsafe: bool,
|
197
|
+
threadsafe: bool | None,
|
198
198
|
) -> Iterator[ScopeFacade]:
|
199
199
|
lock = get_lock(threadsafe)
|
200
200
|
|
injection/entrypoint.py
CHANGED
@@ -7,7 +7,7 @@ from dataclasses import dataclass, field
|
|
7
7
|
from functools import wraps
|
8
8
|
from types import MethodType
|
9
9
|
from types import ModuleType as PythonModule
|
10
|
-
from typing import Any, Self, final, overload
|
10
|
+
from typing import Any, Concatenate, Self, final, overload
|
11
11
|
|
12
12
|
from injection import Module
|
13
13
|
from injection.loaders import ProfileLoader, PythonModuleLoader
|
@@ -16,9 +16,9 @@ __all__ = ("AsyncEntrypoint", "Entrypoint", "autocall", "entrypointmaker")
|
|
16
16
|
|
17
17
|
type AsyncEntrypoint[**P, T] = Entrypoint[P, Coroutine[Any, Any, T]]
|
18
18
|
type EntrypointDecorator[**P, T1, T2] = Callable[[Callable[P, T1]], Callable[P, T2]]
|
19
|
-
type EntrypointSetupMethod[
|
20
|
-
[Entrypoint[
|
21
|
-
Entrypoint[
|
19
|
+
type EntrypointSetupMethod[**P, **EPP, T1, T2] = Callable[
|
20
|
+
Concatenate[Entrypoint[EPP, T1], P],
|
21
|
+
Entrypoint[EPP, T2],
|
22
22
|
]
|
23
23
|
|
24
24
|
|
@@ -31,35 +31,35 @@ def autocall[**P, T](wrapped: Callable[P, T] | None = None, /) -> Any:
|
|
31
31
|
|
32
32
|
|
33
33
|
@overload
|
34
|
-
def entrypointmaker[
|
35
|
-
wrapped: EntrypointSetupMethod[
|
34
|
+
def entrypointmaker[**SMP, **EPP, T1, T2](
|
35
|
+
wrapped: EntrypointSetupMethod[SMP, EPP, T1, T2],
|
36
36
|
/,
|
37
37
|
*,
|
38
38
|
profile_loader: ProfileLoader = ...,
|
39
|
-
) -> EntrypointDecorator[
|
39
|
+
) -> EntrypointDecorator[EPP, T1, T2]: ...
|
40
40
|
|
41
41
|
|
42
42
|
@overload
|
43
|
-
def entrypointmaker[
|
43
|
+
def entrypointmaker[**SMP, **EPP, T1, T2](
|
44
44
|
wrapped: None = ...,
|
45
45
|
/,
|
46
46
|
*,
|
47
47
|
profile_loader: ProfileLoader = ...,
|
48
48
|
) -> Callable[
|
49
|
-
[EntrypointSetupMethod[
|
50
|
-
EntrypointDecorator[
|
49
|
+
[EntrypointSetupMethod[SMP, EPP, T1, T2]],
|
50
|
+
EntrypointDecorator[EPP, T1, T2],
|
51
51
|
]: ...
|
52
52
|
|
53
53
|
|
54
|
-
def entrypointmaker[
|
55
|
-
wrapped: EntrypointSetupMethod[
|
54
|
+
def entrypointmaker[**SMP, **EPP, T1, T2](
|
55
|
+
wrapped: EntrypointSetupMethod[SMP, EPP, T1, T2] | None = None,
|
56
56
|
/,
|
57
57
|
*,
|
58
58
|
profile_loader: ProfileLoader | None = None,
|
59
59
|
) -> Any:
|
60
60
|
def decorator(
|
61
|
-
wp: EntrypointSetupMethod[
|
62
|
-
) -> EntrypointDecorator[
|
61
|
+
wp: EntrypointSetupMethod[SMP, EPP, T1, T2],
|
62
|
+
) -> EntrypointDecorator[EPP, T1, T2]:
|
63
63
|
return Entrypoint._make_decorator(wp, profile_loader)
|
64
64
|
|
65
65
|
return decorator(wrapped) if wrapped else decorator
|
@@ -143,9 +143,9 @@ class Entrypoint[**P, T]:
|
|
143
143
|
return type(self)(function, self.profile_loader)
|
144
144
|
|
145
145
|
@classmethod
|
146
|
-
def _make_decorator[
|
146
|
+
def _make_decorator[**_P, _T](
|
147
147
|
cls,
|
148
|
-
setup_method: EntrypointSetupMethod[
|
148
|
+
setup_method: EntrypointSetupMethod[_P, P, T, _T],
|
149
149
|
/,
|
150
150
|
profile_loader: ProfileLoader | None = None,
|
151
151
|
) -> EntrypointDecorator[P, T, _T]:
|
injection/ext/fastapi.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from dataclasses import dataclass, field
|
2
2
|
from types import GenericAlias
|
3
|
-
from typing import Annotated, Any, TypeAliasType
|
3
|
+
from typing import Annotated, Any, TypeAlias, TypeAliasType
|
4
4
|
|
5
5
|
from fastapi import Depends
|
6
6
|
|
@@ -12,7 +12,7 @@ __all__ = ("Inject", "InjectThreadSafe")
|
|
12
12
|
@dataclass(eq=False, frozen=True, slots=True)
|
13
13
|
class FastAPIInject:
|
14
14
|
module: Module = field(default_factory=mod)
|
15
|
-
threadsafe: bool = field(default=
|
15
|
+
threadsafe: bool | None = field(default=None, kw_only=True)
|
16
16
|
|
17
17
|
def __call__[T](
|
18
18
|
self,
|
@@ -25,16 +25,16 @@ class FastAPIInject:
|
|
25
25
|
) -> Any:
|
26
26
|
module = module or self.module
|
27
27
|
threadsafe = self.threadsafe if threadsafe is None else threadsafe
|
28
|
-
|
28
|
+
lazy_instance = module.aget_lazy_instance(cls, default, threadsafe=threadsafe)
|
29
29
|
|
30
30
|
async def dependency() -> T:
|
31
|
-
return await
|
31
|
+
return await lazy_instance
|
32
32
|
|
33
33
|
class_name = getattr(cls, "__name__", str(cls))
|
34
34
|
dependency.__name__ = f"inject({class_name})"
|
35
35
|
return Depends(dependency, use_cache=False)
|
36
36
|
|
37
|
-
def __getitem__(self, params:
|
37
|
+
def __getitem__[T, *Ts](self, params: T | tuple[T, *Ts], /) -> TypeAlias:
|
38
38
|
iter_params = iter(params if isinstance(params, tuple) else (params,))
|
39
39
|
cls = next(iter_params)
|
40
40
|
return Annotated[cls, self(cls), *iter_params]
|
injection/loaders.py
CHANGED
@@ -164,8 +164,11 @@ class ProfileLoader:
|
|
164
164
|
|
165
165
|
def load(self, name: str, /) -> LoadedProfile:
|
166
166
|
self.init()
|
167
|
-
|
168
|
-
self.
|
167
|
+
|
168
|
+
if not self.__is_default_module(name):
|
169
|
+
target_module = self.__init_subsets_for(mod(name))
|
170
|
+
self.module.use(target_module, priority=Priority.HIGH)
|
171
|
+
|
169
172
|
return _UserLoadedProfile(self, name)
|
170
173
|
|
171
174
|
def _unload(self, name: str, /) -> None:
|
@@ -182,6 +185,9 @@ class ProfileLoader:
|
|
182
185
|
|
183
186
|
return module
|
184
187
|
|
188
|
+
def __is_default_module(self, module_name: str) -> bool:
|
189
|
+
return module_name == self.module.name
|
190
|
+
|
185
191
|
def __is_initialized(self, module: Module) -> bool:
|
186
192
|
return module.name in self.__initialized_modules
|
187
193
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: python-injection
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.19.0
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
5
5
|
Project-URL: Repository, https://github.com/100nm/python-injection
|
6
6
|
Author: remimd
|
@@ -14,6 +14,8 @@ Classifier: Operating System :: OS Independent
|
|
14
14
|
Classifier: Programming Language :: Python
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
17
19
|
Classifier: Topic :: Software Development :: Libraries
|
18
20
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
19
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
@@ -27,11 +29,10 @@ Description-Content-Type: text/markdown
|
|
27
29
|
# python-injection
|
28
30
|
|
29
31
|
[](https://github.com/100nm/python-injection)
|
30
|
-
[](https://pypi.org/project/python-injection)
|
32
|
+
[](https://pypi.org/project/python-injection)
|
33
|
+
[](https://pypistats.org/packages/python-injection)
|
31
34
|
[](https://github.com/astral-sh/ruff)
|
32
35
|
|
33
|
-
Fast and easy dependency injection framework.
|
34
|
-
|
35
36
|
## Installation
|
36
37
|
|
37
38
|
⚠️ _Requires Python 3.12 or higher_
|
@@ -40,12 +41,22 @@ Fast and easy dependency injection framework.
|
|
40
41
|
pip install python-injection
|
41
42
|
```
|
42
43
|
|
44
|
+
## Features
|
45
|
+
|
46
|
+
* Automatic dependency resolution based on type hints.
|
47
|
+
* Support for multiple dependency lifetimes: `transient`, `singleton`, `constant`, and `scoped`.
|
48
|
+
* Works seamlessly in both `async` and `sync` environments.
|
49
|
+
* Separation of dependency sets using modules.
|
50
|
+
* Runtime switching between different sets of dependencies.
|
51
|
+
* Centralized setup logic using entrypoints.
|
52
|
+
* Built-in type annotation for easy integration with [`FastAPI`](https://github.com/fastapi/fastapi).
|
53
|
+
* Lazy dependency resolution for optimized performance.
|
54
|
+
|
43
55
|
## Motivations
|
44
56
|
|
45
57
|
1. Easy to use
|
46
58
|
2. No impact on class and function definitions
|
47
|
-
3.
|
48
|
-
4. No prerequisites
|
59
|
+
3. No tedious configuration
|
49
60
|
|
50
61
|
## Quick start
|
51
62
|
|
@@ -90,5 +101,7 @@ if __name__ == "__main__":
|
|
90
101
|
* [**Advanced usage**](https://github.com/100nm/python-injection/tree/prod/documentation/advanced-usage.md)
|
91
102
|
* [**Loaders**](https://github.com/100nm/python-injection/tree/prod/documentation/loaders.md)
|
92
103
|
* [**Entrypoint**](https://github.com/100nm/python-injection/tree/prod/documentation/entrypoint.md)
|
93
|
-
* [**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations
|
104
|
+
* [**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations)
|
105
|
+
* [**FastAPI**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations/fastapi.md)
|
106
|
+
* [**What if my framework isn't listed?**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations/unlisted-framework.md)
|
94
107
|
* [**Concrete example**](https://github.com/100nm/python-injection-example)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
injection/__init__.py,sha256=iJm0BbyGZw-Qr5e8d2C3n8-7FiVD-sy4LU_i_n3AgHY,1318
|
2
|
+
injection/__init__.pyi,sha256=EM22sEyb69AeZ0mOPwQpbZKD79tEShsGBoDLz9lyxKg,11472
|
3
|
+
injection/entrypoint.py,sha256=NIILL_w3z2JKyn7S9XXwE0JtH2icuDWnAVpUY1fAMf8,4805
|
4
|
+
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
5
|
+
injection/loaders.py,sha256=gKlJfe9nXCuB8r6j0RF9_2FHC6YplM8GQYsgRqyxYw8,7257
|
6
|
+
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
injection/_core/asfunction.py,sha256=jgFtF22n3Fz50pdXbaCbizZxTX3oRgXWSTh-U9m0kqI,1390
|
9
|
+
injection/_core/descriptors.py,sha256=1OX6JnM8Ux14vW1JSW3FzPgKc2VMTKqJUYBGT3Ypafg,800
|
10
|
+
injection/_core/injectables.py,sha256=fxhiGv7qTCbUunhhd6a3ahosFmgznUFsEvqlwxi4gS4,6098
|
11
|
+
injection/_core/module.py,sha256=T9zvSOjMaPZiC_DCV7BIKOqCrmAHcYcleqgNro0MBgU,32624
|
12
|
+
injection/_core/scope.py,sha256=r094k1Vjvwm0hTf7AQGYrxxvqQgb7_CDVUKaHQ8wyeM,8772
|
13
|
+
injection/_core/slots.py,sha256=g9TG6CbqRzCsjg01iPyfRtTTUCJnnJOwcj9mJabH0dc,37
|
14
|
+
injection/_core/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
+
injection/_core/common/asynchronous.py,sha256=nKY2_PAMFF2haC75Fwp8O9Zd-SDuenE6n3B-KccwXlw,1772
|
16
|
+
injection/_core/common/event.py,sha256=XjzV8gxtGlGvzZs_ykvoC60qmdpd3RN08Eiqz5QUwes,1236
|
17
|
+
injection/_core/common/invertible.py,sha256=gA_vw5nBjgp_w9MrDK5jMO8lhuOQWON8BbPpKzEuIY0,502
|
18
|
+
injection/_core/common/key.py,sha256=ghkZD-Y8Moz6SEPNgMh3xgsZUjDVq-XYAmXaCu5VuCA,80
|
19
|
+
injection/_core/common/lazy.py,sha256=L7C2dB5UBI--W5Kkvntp_jypYhudY3SHqs8rM6jxxsk,965
|
20
|
+
injection/_core/common/threading.py,sha256=kwRXNa9ocndIqeZA9kMHjEa8SBpHFcJARj1bgrWCpxE,225
|
21
|
+
injection/_core/common/type.py,sha256=SCDtmBv9qFvEf5o5tTgCuwMDfuo1fgjSW0bUqA8ACis,2251
|
22
|
+
injection/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
+
injection/ext/fastapi.py,sha256=fiy3-mZIIwGcql3Y5ekFX8_7hALzqXP5u40qbtNE73o,1441
|
24
|
+
injection/ext/fastapi.pyi,sha256=HLs7mfruIEFRrN_Xf8oCvSa4qwHWfwm6HHU_KMedXkE,185
|
25
|
+
injection/testing/__init__.py,sha256=bJ7WXBXrw4rHc91AFVFnOwFLWOlpvX9Oh2SnRQ_NESo,919
|
26
|
+
injection/testing/__init__.pyi,sha256=raGsGlxwbz3jkzJwA_5oCIE1emWINjT2UuwzbnqRb-0,577
|
27
|
+
python_injection-0.19.0.dist-info/METADATA,sha256=1MtpUw2NSEhD1meQOXkMIKGq1WA69MwrrWrIi-PK1Gw,4301
|
28
|
+
python_injection-0.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
29
|
+
python_injection-0.19.0.dist-info/licenses/LICENSE,sha256=oC77BOa9kaaQni5rW-Z-ytz3E5h4EVg248BHg9UFgyg,1063
|
30
|
+
python_injection-0.19.0.dist-info/RECORD,,
|
@@ -1,29 +0,0 @@
|
|
1
|
-
injection/__init__.py,sha256=7ZRUlO5EEPWO7IlbYHD-8DOX-cg4Np4nYq5fpw-U56o,1259
|
2
|
-
injection/__init__.pyi,sha256=65zhAK9LhuUXhftk7YDq2JJZ2zlPtNQ6UfrXaKcQgcA,10940
|
3
|
-
injection/entrypoint.py,sha256=12b0_zHAFxHCerAoJTIHkhqi3mLkgheECYAaCUZv_DU,4751
|
4
|
-
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
5
|
-
injection/loaders.py,sha256=EWnbcFdiHJBIALm1foHpPi5clZiAHpVstgKXV0dofiw,7091
|
6
|
-
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
injection/_core/descriptors.py,sha256=RRng9lx-ET6An1d_244rAv4iLUTQUGvDh1ugLfImuW8,794
|
9
|
-
injection/_core/injectables.py,sha256=Rg1nxDkbcpeX4ELohrNVMguPhN36SNQuD0JKfyfL6bI,6192
|
10
|
-
injection/_core/module.py,sha256=aSUHKLFJ63yx6FgJ6AKpDccaZ-H1YpKQyoaKxb4YXfk,32414
|
11
|
-
injection/_core/scope.py,sha256=NY6YWcIIXqBKTjXVsqsahxbw-bPJiL0CwHp6CyjBGJo,8753
|
12
|
-
injection/_core/slots.py,sha256=g9TG6CbqRzCsjg01iPyfRtTTUCJnnJOwcj9mJabH0dc,37
|
13
|
-
injection/_core/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
injection/_core/common/asynchronous.py,sha256=lx1Ce3o6K00vzrZNRZUuf7wWlUKrdV3M53YZGyxQhOk,1817
|
15
|
-
injection/_core/common/event.py,sha256=XjzV8gxtGlGvzZs_ykvoC60qmdpd3RN08Eiqz5QUwes,1236
|
16
|
-
injection/_core/common/invertible.py,sha256=gA_vw5nBjgp_w9MrDK5jMO8lhuOQWON8BbPpKzEuIY0,502
|
17
|
-
injection/_core/common/key.py,sha256=ghkZD-Y8Moz6SEPNgMh3xgsZUjDVq-XYAmXaCu5VuCA,80
|
18
|
-
injection/_core/common/lazy.py,sha256=L7C2dB5UBI--W5Kkvntp_jypYhudY3SHqs8rM6jxxsk,965
|
19
|
-
injection/_core/common/threading.py,sha256=EsIvJl1brMsi551zzMILsMCXNHYUkQP22Mg4FZhp0hw,211
|
20
|
-
injection/_core/common/type.py,sha256=SCDtmBv9qFvEf5o5tTgCuwMDfuo1fgjSW0bUqA8ACis,2251
|
21
|
-
injection/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
|
-
injection/ext/fastapi.py,sha256=YdCVO1WgZRhv0CFrDnIdsbeRprtcHb-snIL4B4mpLnk,1388
|
23
|
-
injection/ext/fastapi.pyi,sha256=HLs7mfruIEFRrN_Xf8oCvSa4qwHWfwm6HHU_KMedXkE,185
|
24
|
-
injection/testing/__init__.py,sha256=bJ7WXBXrw4rHc91AFVFnOwFLWOlpvX9Oh2SnRQ_NESo,919
|
25
|
-
injection/testing/__init__.pyi,sha256=raGsGlxwbz3jkzJwA_5oCIE1emWINjT2UuwzbnqRb-0,577
|
26
|
-
python_injection-0.18.13.dist-info/METADATA,sha256=O90MunVYtSYyTrFbbDrdhDFKfbp5i9rPsUdnal4jxGs,3398
|
27
|
-
python_injection-0.18.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
28
|
-
python_injection-0.18.13.dist-info/licenses/LICENSE,sha256=oC77BOa9kaaQni5rW-Z-ytz3E5h4EVg248BHg9UFgyg,1063
|
29
|
-
python_injection-0.18.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|