python-injection 0.14.6.post2__py3-none-any.whl → 0.15.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/utils.py +88 -54
- {python_injection-0.14.6.post2.dist-info → python_injection-0.15.0.dist-info}/METADATA +1 -1
- {python_injection-0.14.6.post2.dist-info → python_injection-0.15.0.dist-info}/RECORD +4 -4
- {python_injection-0.14.6.post2.dist-info → python_injection-0.15.0.dist-info}/WHEEL +0 -0
injection/utils.py
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
+
import itertools
|
1
2
|
import sys
|
2
|
-
from collections.abc import Callable,
|
3
|
+
from collections.abc import Callable, Iterator, Mapping
|
4
|
+
from dataclasses import dataclass, field
|
3
5
|
from importlib import import_module
|
4
6
|
from importlib.util import find_spec
|
7
|
+
from os.path import isfile
|
5
8
|
from pkgutil import walk_packages
|
9
|
+
from types import MappingProxyType
|
6
10
|
from types import ModuleType as PythonModule
|
7
|
-
from typing import ContextManager
|
11
|
+
from typing import ClassVar, ContextManager, Self
|
8
12
|
|
9
13
|
from injection import Module, mod
|
10
|
-
from injection import __name__ as injection_package_name
|
11
14
|
|
12
|
-
__all__ = ("
|
15
|
+
__all__ = ("PythonModuleLoader", "load_packages", "load_profile")
|
13
16
|
|
14
17
|
|
15
18
|
def load_profile(*names: str) -> ContextManager[Module]:
|
@@ -21,76 +24,107 @@ def load_profile(*names: str) -> ContextManager[Module]:
|
|
21
24
|
return mod().load_profile(*names)
|
22
25
|
|
23
26
|
|
24
|
-
def
|
27
|
+
def load_packages(
|
25
28
|
*packages: PythonModule | str,
|
26
|
-
|
29
|
+
predicate: Callable[[str], bool] = lambda module_name: True,
|
27
30
|
) -> dict[str, PythonModule]:
|
28
31
|
"""
|
29
|
-
Function
|
30
|
-
|
31
|
-
- `from injection `
|
32
|
-
- `from injection.`
|
33
|
-
- `import injection`
|
32
|
+
Function for importing all modules in a Python package.
|
33
|
+
Pass the `predicate` parameter if you want to filter the modules to be imported.
|
34
34
|
"""
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
return PythonModuleLoader(predicate).load(*packages).modules
|
37
|
+
|
38
|
+
|
39
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
40
|
+
class PythonModuleLoader:
|
41
|
+
predicate: Callable[[str], bool]
|
42
|
+
__modules: dict[str, PythonModule | None] = field(
|
43
|
+
default_factory=dict,
|
44
|
+
init=False,
|
45
|
+
)
|
46
|
+
|
47
|
+
# To easily mock `sys.modules` in tests
|
48
|
+
_sys_modules: ClassVar[Mapping[str, PythonModule]] = MappingProxyType(sys.modules)
|
49
|
+
|
50
|
+
@property
|
51
|
+
def modules(self) -> dict[str, PythonModule]:
|
52
|
+
return {
|
53
|
+
name: module
|
54
|
+
for name, module in self.__modules.items()
|
55
|
+
if module is not None
|
56
|
+
}
|
57
|
+
|
58
|
+
def load(self, *packages: PythonModule | str) -> Self:
|
59
|
+
modules = itertools.chain.from_iterable(
|
60
|
+
self.__iter_modules(package) for package in packages
|
41
61
|
)
|
62
|
+
self.__modules.update(modules)
|
63
|
+
return self
|
42
64
|
|
43
|
-
def
|
44
|
-
|
65
|
+
def __is_already_loaded(self, module_name: str) -> bool:
|
66
|
+
return any(
|
67
|
+
module_name in modules for modules in (self.__modules, self._sys_modules)
|
68
|
+
)
|
45
69
|
|
46
|
-
|
47
|
-
|
48
|
-
|
70
|
+
def __iter_modules(
|
71
|
+
self,
|
72
|
+
package: PythonModule | str,
|
73
|
+
) -> Iterator[tuple[str, PythonModule | None]]:
|
74
|
+
if isinstance(package, str):
|
75
|
+
package = import_module(package)
|
49
76
|
|
50
|
-
|
51
|
-
keyword in python_script for keyword in keywords
|
52
|
-
)
|
77
|
+
package_name = package.__name__
|
53
78
|
|
54
|
-
|
79
|
+
try:
|
80
|
+
package_path = package.__path__
|
81
|
+
except AttributeError as exc:
|
82
|
+
raise TypeError(f"`{package_name}` isn't Python package.") from exc
|
55
83
|
|
56
|
-
|
84
|
+
for info in walk_packages(path=package_path, prefix=f"{package_name}."):
|
85
|
+
name = info.name
|
57
86
|
|
87
|
+
if info.ispkg or self.__is_already_loaded(name):
|
88
|
+
continue
|
58
89
|
|
59
|
-
|
60
|
-
|
61
|
-
predicate: Callable[[str], bool] = lambda module_name: True,
|
62
|
-
) -> dict[str, PythonModule]:
|
63
|
-
"""
|
64
|
-
Function for importing all modules in a Python package.
|
65
|
-
Pass the `predicate` parameter if you want to filter the modules to be imported.
|
66
|
-
"""
|
90
|
+
module = import_module(name) if self.predicate(name) else None
|
91
|
+
yield name, module
|
67
92
|
|
68
|
-
|
93
|
+
@classmethod
|
94
|
+
def from_keywords(cls, *keywords: str) -> Self:
|
95
|
+
"""
|
96
|
+
Create loader to import modules from a Python package if one of the keywords is
|
97
|
+
contained in the Python script.
|
98
|
+
"""
|
69
99
|
|
70
|
-
|
71
|
-
|
72
|
-
|
100
|
+
def predicate(module_name: str) -> bool:
|
101
|
+
spec = find_spec(module_name)
|
102
|
+
|
103
|
+
if spec is None:
|
104
|
+
return False
|
73
105
|
|
74
|
-
|
106
|
+
module_path = spec.origin
|
75
107
|
|
76
|
-
|
108
|
+
if module_path is None or not isfile(module_path):
|
109
|
+
return False
|
77
110
|
|
111
|
+
with open(module_path, "r") as script:
|
112
|
+
return any(keyword in line for line in script for keyword in keywords)
|
78
113
|
|
79
|
-
|
80
|
-
package: PythonModule,
|
81
|
-
predicate: Callable[[str], bool],
|
82
|
-
) -> Iterator[tuple[str, PythonModule]]:
|
83
|
-
package_name = package.__name__
|
114
|
+
return cls(predicate)
|
84
115
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
116
|
+
@classmethod
|
117
|
+
def startswith(cls, *prefixes: str) -> Self:
|
118
|
+
def predicate(module_name: str) -> bool:
|
119
|
+
script_name = module_name.split(".")[-1]
|
120
|
+
return any(script_name.startswith(prefix) for prefix in prefixes)
|
89
121
|
|
90
|
-
|
91
|
-
name = info.name
|
122
|
+
return cls(predicate)
|
92
123
|
|
93
|
-
|
94
|
-
|
124
|
+
@classmethod
|
125
|
+
def endswith(cls, *suffixes: str) -> Self:
|
126
|
+
def predicate(module_name: str) -> bool:
|
127
|
+
script_name = module_name.split(".")[-1]
|
128
|
+
return any(script_name.endswith(suffix) for suffix in suffixes)
|
95
129
|
|
96
|
-
|
130
|
+
return cls(predicate)
|
@@ -2,7 +2,7 @@ injection/__init__.py,sha256=a-rBAlBTiH6TzZ-11NGK6diovfibyKmmnf99PZUog1A,1166
|
|
2
2
|
injection/__init__.pyi,sha256=rOl1kA9BkIeXDr7OdBMifAg9xWwvme447yjDZooir18,10398
|
3
3
|
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
4
4
|
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
injection/utils.py,sha256=
|
5
|
+
injection/utils.py,sha256=bLIVA_3N3mTEQ3kGV4YzrWEnokHxUGqWYNKPggOOnpg,4065
|
6
6
|
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
injection/_core/descriptors.py,sha256=7fSHlgAqmgR_Uta8KocBapOt1Xyj2dI7RY9ZdoStTzw,726
|
8
8
|
injection/_core/injectables.py,sha256=idNkQZZ29vd73G_lE-eS5C7zGeVe_ALNkUt8M6YjZrk,5519
|
@@ -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=SiImXDd0-DO1a8S5nbUQRtgDX8iaU_nHcp8DdqwtD2M,896
|
22
22
|
injection/testing/__init__.pyi,sha256=iOii0i9F5n7znltGeGQYI2KXC_if9SAogLh1h03yx-0,540
|
23
|
-
python_injection-0.
|
24
|
-
python_injection-0.
|
25
|
-
python_injection-0.
|
23
|
+
python_injection-0.15.0.dist-info/METADATA,sha256=LA4sTrPDIUAk-dwOH1zKj1Rjk-feJwq1r2eYHpLIJTw,3199
|
24
|
+
python_injection-0.15.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
25
|
+
python_injection-0.15.0.dist-info/RECORD,,
|
File without changes
|