python-injection 0.18.5__py3-none-any.whl → 0.18.6__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 +0 -2
- injection/_core/module.py +0 -11
- injection/entrypoint.py +22 -17
- injection/loaders.py +98 -14
- injection/testing/__init__.py +5 -5
- injection/testing/__init__.pyi +4 -3
- {python_injection-0.18.5.dist-info → python_injection-0.18.6.dist-info}/METADATA +1 -1
- {python_injection-0.18.5.dist-info → python_injection-0.18.6.dist-info}/RECORD +10 -10
- {python_injection-0.18.5.dist-info → python_injection-0.18.6.dist-info}/WHEEL +0 -0
- {python_injection-0.18.5.dist-info → python_injection-0.18.6.dist-info}/licenses/LICENSE +0 -0
injection/__init__.pyi
CHANGED
@@ -343,8 +343,6 @@ class Module:
|
|
343
343
|
Function to unlock the module by deleting cached instances of singletons.
|
344
344
|
"""
|
345
345
|
|
346
|
-
@contextmanager
|
347
|
-
def load_profile(self, *names: str) -> Iterator[Self]: ...
|
348
346
|
async def all_ready(self) -> None: ...
|
349
347
|
def add_logger(self, logger: Logger) -> Self: ...
|
350
348
|
@classmethod
|
injection/_core/module.py
CHANGED
@@ -817,17 +817,6 @@ class Module(Broker, EventListener):
|
|
817
817
|
for broker in self.__brokers:
|
818
818
|
broker.unsafe_unlocking()
|
819
819
|
|
820
|
-
def load_profile(self, *names: str) -> ContextManager[Self]:
|
821
|
-
modules = (self.from_name(name) for name in names)
|
822
|
-
self.unlock().init_modules(*modules)
|
823
|
-
|
824
|
-
@contextmanager
|
825
|
-
def unload() -> Iterator[Self]:
|
826
|
-
yield self
|
827
|
-
self.unlock().init_modules()
|
828
|
-
|
829
|
-
return unload()
|
830
|
-
|
831
820
|
async def all_ready(self) -> None:
|
832
821
|
for broker in self.__brokers:
|
833
822
|
await broker.all_ready()
|
injection/entrypoint.py
CHANGED
@@ -9,8 +9,8 @@ from types import MethodType
|
|
9
9
|
from types import ModuleType as PythonModule
|
10
10
|
from typing import Any, Self, final, overload
|
11
11
|
|
12
|
-
from injection import Module
|
13
|
-
from injection.loaders import PythonModuleLoader
|
12
|
+
from injection import Module
|
13
|
+
from injection.loaders import ProfileLoader, PythonModuleLoader
|
14
14
|
|
15
15
|
__all__ = ("AsyncEntrypoint", "Entrypoint", "autocall", "entrypointmaker")
|
16
16
|
|
@@ -35,7 +35,7 @@ def entrypointmaker[*Ts, **P, T1, T2](
|
|
35
35
|
wrapped: EntrypointSetupMethod[*Ts, P, T1, T2],
|
36
36
|
/,
|
37
37
|
*,
|
38
|
-
|
38
|
+
profile_loader: ProfileLoader = ...,
|
39
39
|
) -> EntrypointDecorator[P, T1, T2]: ...
|
40
40
|
|
41
41
|
|
@@ -44,7 +44,7 @@ def entrypointmaker[*Ts, **P, T1, T2](
|
|
44
44
|
wrapped: None = ...,
|
45
45
|
/,
|
46
46
|
*,
|
47
|
-
|
47
|
+
profile_loader: ProfileLoader = ...,
|
48
48
|
) -> Callable[
|
49
49
|
[EntrypointSetupMethod[*Ts, P, T1, T2]],
|
50
50
|
EntrypointDecorator[P, T1, T2],
|
@@ -55,12 +55,12 @@ def entrypointmaker[*Ts, **P, T1, T2](
|
|
55
55
|
wrapped: EntrypointSetupMethod[*Ts, P, T1, T2] | None = None,
|
56
56
|
/,
|
57
57
|
*,
|
58
|
-
|
58
|
+
profile_loader: ProfileLoader | None = None,
|
59
59
|
) -> Any:
|
60
60
|
def decorator(
|
61
61
|
wp: EntrypointSetupMethod[*Ts, P, T1, T2],
|
62
62
|
) -> EntrypointDecorator[P, T1, T2]:
|
63
|
-
return Entrypoint._make_decorator(wp,
|
63
|
+
return Entrypoint._make_decorator(wp, profile_loader)
|
64
64
|
|
65
65
|
return decorator(wrapped) if wrapped else decorator
|
66
66
|
|
@@ -69,11 +69,15 @@ def entrypointmaker[*Ts, **P, T1, T2](
|
|
69
69
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
70
70
|
class Entrypoint[**P, T]:
|
71
71
|
function: Callable[P, T]
|
72
|
-
|
72
|
+
profile_loader: ProfileLoader = field(default_factory=ProfileLoader)
|
73
73
|
|
74
74
|
def __call__(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
75
75
|
return self.function(*args, **kwargs)
|
76
76
|
|
77
|
+
@property
|
78
|
+
def __module(self) -> Module:
|
79
|
+
return self.profile_loader.module
|
80
|
+
|
77
81
|
def async_to_sync[_T](
|
78
82
|
self: AsyncEntrypoint[P, _T],
|
79
83
|
run: Callable[[Coroutine[Any, Any, _T]], _T] = asyncio.run,
|
@@ -95,7 +99,7 @@ class Entrypoint[**P, T]:
|
|
95
99
|
return self.__recreate(decorator(self.function))
|
96
100
|
|
97
101
|
def inject(self) -> Self:
|
98
|
-
return self.decorate(self.
|
102
|
+
return self.decorate(self.__module.make_injected_function)
|
99
103
|
|
100
104
|
def load_modules(
|
101
105
|
self,
|
@@ -105,13 +109,13 @@ class Entrypoint[**P, T]:
|
|
105
109
|
) -> Self:
|
106
110
|
return self.setup(lambda: loader.load(*packages))
|
107
111
|
|
108
|
-
def load_profile(self,
|
112
|
+
def load_profile(self, name: str, /) -> Self:
|
109
113
|
@contextmanager
|
110
|
-
def decorator(
|
111
|
-
with
|
114
|
+
def decorator(loader: ProfileLoader) -> Iterator[None]:
|
115
|
+
with loader.load(name):
|
112
116
|
yield
|
113
117
|
|
114
|
-
return self.decorate(decorator(self.
|
118
|
+
return self.decorate(decorator(self.profile_loader))
|
115
119
|
|
116
120
|
def setup(self, function: Callable[..., Any], /) -> Self:
|
117
121
|
@contextmanager
|
@@ -136,20 +140,21 @@ class Entrypoint[**P, T]:
|
|
136
140
|
function: Callable[_P, _T],
|
137
141
|
/,
|
138
142
|
) -> Entrypoint[_P, _T]:
|
139
|
-
return type(self)(function, self.
|
143
|
+
return type(self)(function, self.profile_loader)
|
140
144
|
|
141
145
|
@classmethod
|
142
146
|
def _make_decorator[*Ts, _T](
|
143
147
|
cls,
|
144
148
|
setup_method: EntrypointSetupMethod[*Ts, P, T, _T],
|
145
149
|
/,
|
146
|
-
|
150
|
+
profile_loader: ProfileLoader | None = None,
|
147
151
|
) -> EntrypointDecorator[P, T, _T]:
|
148
|
-
|
149
|
-
setup_method = module.make_injected_function(setup_method)
|
152
|
+
profile_loader = profile_loader or ProfileLoader()
|
153
|
+
setup_method = profile_loader.module.make_injected_function(setup_method)
|
150
154
|
|
151
155
|
def decorator(function: Callable[P, T]) -> Callable[P, _T]:
|
152
|
-
|
156
|
+
profile_loader.init()
|
157
|
+
self = cls(function, profile_loader)
|
153
158
|
return MethodType(setup_method, self)().function
|
154
159
|
|
155
160
|
return decorator
|
injection/loaders.py
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import itertools
|
2
4
|
import sys
|
3
|
-
from
|
5
|
+
from abc import abstractmethod
|
6
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
4
7
|
from dataclasses import dataclass, field
|
5
8
|
from importlib import import_module
|
6
9
|
from importlib.util import find_spec
|
7
10
|
from os.path import isfile
|
8
11
|
from pkgutil import walk_packages
|
9
|
-
from types import MappingProxyType
|
12
|
+
from types import MappingProxyType, TracebackType
|
10
13
|
from types import ModuleType as PythonModule
|
11
|
-
from typing import ClassVar,
|
12
|
-
|
13
|
-
from injection import Module, mod
|
14
|
-
|
15
|
-
__all__ = ("PythonModuleLoader", "load_packages", "load_profile")
|
14
|
+
from typing import ClassVar, Protocol, Self, runtime_checkable
|
16
15
|
|
16
|
+
from injection import Module, Priority, mod
|
17
17
|
|
18
|
-
|
19
|
-
""
|
20
|
-
|
21
|
-
|
22
|
-
""
|
23
|
-
|
24
|
-
|
18
|
+
__all__ = (
|
19
|
+
"LoadedProfile",
|
20
|
+
"ProfileLoader",
|
21
|
+
"PythonModuleLoader",
|
22
|
+
"load_packages",
|
23
|
+
"load_profile",
|
24
|
+
)
|
25
25
|
|
26
26
|
|
27
27
|
def load_packages(
|
@@ -36,6 +36,15 @@ def load_packages(
|
|
36
36
|
return PythonModuleLoader(predicate).load(*packages).modules
|
37
37
|
|
38
38
|
|
39
|
+
def load_profile(name: str, /, loader: ProfileLoader | None = None) -> LoadedProfile:
|
40
|
+
"""
|
41
|
+
Injection module initialization function based on a profile name.
|
42
|
+
A profile name is equivalent to an injection module name.
|
43
|
+
"""
|
44
|
+
|
45
|
+
return (loader or ProfileLoader()).load(name)
|
46
|
+
|
47
|
+
|
39
48
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
40
49
|
class PythonModuleLoader:
|
41
50
|
predicate: Callable[[str], bool]
|
@@ -128,3 +137,78 @@ class PythonModuleLoader:
|
|
128
137
|
return any(script_name.endswith(suffix) for suffix in suffixes)
|
129
138
|
|
130
139
|
return cls(predicate)
|
140
|
+
|
141
|
+
|
142
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
143
|
+
class ProfileLoader:
|
144
|
+
dependencies: Mapping[str, Sequence[str]] = field(default=MappingProxyType({}))
|
145
|
+
module: Module = field(default_factory=mod, kw_only=True)
|
146
|
+
__initialized_modules: set[str] = field(default_factory=set, init=False)
|
147
|
+
|
148
|
+
def init(self) -> Self:
|
149
|
+
self.__init_module_dependencies(self.module)
|
150
|
+
return self
|
151
|
+
|
152
|
+
def load(self, name: str, /) -> LoadedProfile:
|
153
|
+
self.init()
|
154
|
+
target_module = self.__init_module_dependencies(mod(name))
|
155
|
+
self.module.use(target_module, priority=Priority.HIGH)
|
156
|
+
return _UserLoadedProfile(self, name)
|
157
|
+
|
158
|
+
def _unload(self, name: str, /) -> None:
|
159
|
+
self.module.unlock().stop_using(mod(name))
|
160
|
+
|
161
|
+
def __init_module_dependencies(self, module: Module) -> Module:
|
162
|
+
if not self.__is_initialized(module):
|
163
|
+
target_modules = tuple(
|
164
|
+
self.__init_module_dependencies(mod(profile_name))
|
165
|
+
for profile_name in self.dependencies.get(module.name, ())
|
166
|
+
)
|
167
|
+
module.unlock().init_modules(*target_modules)
|
168
|
+
self.__mark_initialized(module)
|
169
|
+
|
170
|
+
return module
|
171
|
+
|
172
|
+
def __is_initialized(self, module: Module) -> bool:
|
173
|
+
return module.name in self.__initialized_modules
|
174
|
+
|
175
|
+
def __mark_initialized(self, module: Module) -> None:
|
176
|
+
self.__initialized_modules.add(module.name)
|
177
|
+
|
178
|
+
|
179
|
+
@runtime_checkable
|
180
|
+
class LoadedProfile(Protocol):
|
181
|
+
__slots__ = ()
|
182
|
+
|
183
|
+
def __enter__(self) -> Self:
|
184
|
+
return self
|
185
|
+
|
186
|
+
def __exit__(
|
187
|
+
self,
|
188
|
+
exc_type: type[BaseException] | None,
|
189
|
+
exc_value: BaseException | None,
|
190
|
+
traceback: TracebackType | None,
|
191
|
+
) -> None:
|
192
|
+
self.unload()
|
193
|
+
|
194
|
+
@abstractmethod
|
195
|
+
def reload(self) -> Self:
|
196
|
+
raise NotImplementedError
|
197
|
+
|
198
|
+
@abstractmethod
|
199
|
+
def unload(self) -> Self:
|
200
|
+
raise NotImplementedError
|
201
|
+
|
202
|
+
|
203
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
204
|
+
class _UserLoadedProfile(LoadedProfile):
|
205
|
+
loader: ProfileLoader
|
206
|
+
name: str
|
207
|
+
|
208
|
+
def reload(self) -> Self:
|
209
|
+
self.loader.load(self.name)
|
210
|
+
return self
|
211
|
+
|
212
|
+
def unload(self) -> Self:
|
213
|
+
self.loader._unload(self.name)
|
214
|
+
return self
|
injection/testing/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Final
|
2
2
|
|
3
|
-
from injection import
|
4
|
-
from injection.loaders import load_profile
|
3
|
+
from injection import mod
|
4
|
+
from injection.loaders import LoadedProfile, ProfileLoader, load_profile
|
5
5
|
|
6
6
|
__all__ = (
|
7
7
|
"load_test_profile",
|
@@ -25,5 +25,5 @@ test_scoped = mod(_TEST_PROFILE_NAME).scoped
|
|
25
25
|
test_singleton = mod(_TEST_PROFILE_NAME).singleton
|
26
26
|
|
27
27
|
|
28
|
-
def load_test_profile(
|
29
|
-
return load_profile(_TEST_PROFILE_NAME,
|
28
|
+
def load_test_profile(loader: ProfileLoader | None = None) -> LoadedProfile:
|
29
|
+
return load_profile(_TEST_PROFILE_NAME, loader)
|
injection/testing/__init__.pyi
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Final
|
2
2
|
|
3
3
|
from injection import Module
|
4
|
+
from injection.loaders import LoadedProfile, ProfileLoader
|
4
5
|
|
5
6
|
__MODULE: Final[Module] = ...
|
6
7
|
|
@@ -12,7 +13,7 @@ test_injectable = __MODULE.injectable
|
|
12
13
|
test_scoped = __MODULE.scoped
|
13
14
|
test_singleton = __MODULE.singleton
|
14
15
|
|
15
|
-
def load_test_profile(
|
16
|
+
def load_test_profile(loader: ProfileLoader = ...) -> LoadedProfile:
|
16
17
|
"""
|
17
|
-
Context manager
|
18
|
+
Context manager for temporary use test module.
|
18
19
|
"""
|
@@ -1,13 +1,13 @@
|
|
1
1
|
injection/__init__.py,sha256=7ZRUlO5EEPWO7IlbYHD-8DOX-cg4Np4nYq5fpw-U56o,1259
|
2
|
-
injection/__init__.pyi,sha256=
|
3
|
-
injection/entrypoint.py,sha256=
|
2
|
+
injection/__init__.pyi,sha256=33A511o3QKQAiQOHL8OQtFPdF-ObM1BqWPmswXhxj7c,10634
|
3
|
+
injection/entrypoint.py,sha256=12b0_zHAFxHCerAoJTIHkhqi3mLkgheECYAaCUZv_DU,4751
|
4
4
|
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
5
|
-
injection/loaders.py,sha256=
|
5
|
+
injection/loaders.py,sha256=_0V64d45WoT0OVWML8ZQts4gMXCbT7LkCS3KLl6STMI,6509
|
6
6
|
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
injection/_core/descriptors.py,sha256=jH0pyIlPurMmU4yXr-HKS_7BJ-9d0XUvEx4pQre3QeI,704
|
9
9
|
injection/_core/injectables.py,sha256=Rg1nxDkbcpeX4ELohrNVMguPhN36SNQuD0JKfyfL6bI,6192
|
10
|
-
injection/_core/module.py,sha256=
|
10
|
+
injection/_core/module.py,sha256=zpYLLhtXPLb6hE1U_A5AJ-ffffT_aPrq4DuVmJoxz3A,31931
|
11
11
|
injection/_core/scope.py,sha256=OBzVY1mUApryqIZKQtwHz7wiuY13MfouyaHp50DpWeQ,8300
|
12
12
|
injection/_core/slots.py,sha256=g9TG6CbqRzCsjg01iPyfRtTTUCJnnJOwcj9mJabH0dc,37
|
13
13
|
injection/_core/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -20,9 +20,9 @@ injection/_core/common/type.py,sha256=SCDtmBv9qFvEf5o5tTgCuwMDfuo1fgjSW0bUqA8ACi
|
|
20
20
|
injection/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
21
|
injection/ext/fastapi.py,sha256=layUUer5IWiZX6Mmx1_RCYDLNCtEHtpya5ZL6TTBOkY,968
|
22
22
|
injection/ext/fastapi.pyi,sha256=8OZEUjHFB9n7QXv_dtXdDuXW-r2huQEFsJ03gJOOvwQ,125
|
23
|
-
injection/testing/__init__.py,sha256=
|
24
|
-
injection/testing/__init__.pyi,sha256=
|
25
|
-
python_injection-0.18.
|
26
|
-
python_injection-0.18.
|
27
|
-
python_injection-0.18.
|
28
|
-
python_injection-0.18.
|
23
|
+
injection/testing/__init__.py,sha256=bJ7WXBXrw4rHc91AFVFnOwFLWOlpvX9Oh2SnRQ_NESo,919
|
24
|
+
injection/testing/__init__.pyi,sha256=raGsGlxwbz3jkzJwA_5oCIE1emWINjT2UuwzbnqRb-0,577
|
25
|
+
python_injection-0.18.6.dist-info/METADATA,sha256=J8D8DrVAJfAoGen7LNrtT303UXR5yUT5fYw6P6ufVsQ,3397
|
26
|
+
python_injection-0.18.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
27
|
+
python_injection-0.18.6.dist-info/licenses/LICENSE,sha256=oC77BOa9kaaQni5rW-Z-ytz3E5h4EVg248BHg9UFgyg,1063
|
28
|
+
python_injection-0.18.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|