python-injection 0.17.3.post0__py3-none-any.whl → 0.18.0.post0__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/entrypoint.py +147 -0
- injection/loaders.py +2 -2
- {python_injection-0.17.3.post0.dist-info → python_injection-0.18.0.post0.dist-info}/METADATA +3 -2
- {python_injection-0.17.3.post0.dist-info → python_injection-0.18.0.post0.dist-info}/RECORD +5 -4
- {python_injection-0.17.3.post0.dist-info → python_injection-0.18.0.post0.dist-info}/WHEEL +0 -0
injection/entrypoint.py
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from collections.abc import AsyncIterator, Awaitable, Callable, Coroutine, Iterator
|
5
|
+
from contextlib import asynccontextmanager, contextmanager
|
6
|
+
from dataclasses import dataclass, field
|
7
|
+
from functools import wraps
|
8
|
+
from types import MethodType
|
9
|
+
from types import ModuleType as PythonModule
|
10
|
+
from typing import Any, Self, final, overload
|
11
|
+
|
12
|
+
from injection import Module, mod
|
13
|
+
from injection.loaders import PythonModuleLoader
|
14
|
+
|
15
|
+
__all__ = ("AsyncEntrypoint", "Entrypoint", "autocall", "entrypointmaker")
|
16
|
+
|
17
|
+
type AsyncEntrypoint[**P, T] = Entrypoint[P, Coroutine[Any, Any, T]]
|
18
|
+
type EntrypointDecorator[**P, T1, T2] = Callable[[Callable[P, T1]], Callable[P, T2]]
|
19
|
+
type EntrypointSetupMethod[*Ts, **P, T1, T2] = Callable[
|
20
|
+
[Entrypoint[P, T1], *Ts],
|
21
|
+
Entrypoint[P, T2],
|
22
|
+
]
|
23
|
+
|
24
|
+
|
25
|
+
def autocall[**P, T](wrapped: Callable[P, T] | None = None, /) -> Any:
|
26
|
+
def decorator(wp: Callable[P, T]) -> Callable[P, T]:
|
27
|
+
wp() # type: ignore[call-arg]
|
28
|
+
return wp
|
29
|
+
|
30
|
+
return decorator(wrapped) if wrapped else decorator
|
31
|
+
|
32
|
+
|
33
|
+
@overload
|
34
|
+
def entrypointmaker[*Ts, **P, T1, T2](
|
35
|
+
wrapped: EntrypointSetupMethod[*Ts, P, T1, T2],
|
36
|
+
/,
|
37
|
+
*,
|
38
|
+
module: Module = ...,
|
39
|
+
) -> EntrypointDecorator[P, T1, T2]: ...
|
40
|
+
|
41
|
+
|
42
|
+
@overload
|
43
|
+
def entrypointmaker[*Ts, **P, T1, T2](
|
44
|
+
wrapped: None = ...,
|
45
|
+
/,
|
46
|
+
*,
|
47
|
+
module: Module = ...,
|
48
|
+
) -> Callable[
|
49
|
+
[EntrypointSetupMethod[*Ts, P, T1, T2]],
|
50
|
+
EntrypointDecorator[P, T1, T2],
|
51
|
+
]: ...
|
52
|
+
|
53
|
+
|
54
|
+
def entrypointmaker[*Ts, **P, T1, T2](
|
55
|
+
wrapped: EntrypointSetupMethod[*Ts, P, T1, T2] | None = None,
|
56
|
+
/,
|
57
|
+
*,
|
58
|
+
module: Module | None = None,
|
59
|
+
) -> Any:
|
60
|
+
def decorator(
|
61
|
+
wp: EntrypointSetupMethod[*Ts, P, T1, T2],
|
62
|
+
) -> EntrypointDecorator[P, T1, T2]:
|
63
|
+
return Entrypoint._make_decorator(wp, module)
|
64
|
+
|
65
|
+
return decorator(wrapped) if wrapped else decorator
|
66
|
+
|
67
|
+
|
68
|
+
@final
|
69
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
70
|
+
class Entrypoint[**P, T]:
|
71
|
+
function: Callable[P, T]
|
72
|
+
module: Module = field(default_factory=mod)
|
73
|
+
|
74
|
+
def __call__(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
75
|
+
return self.function(*args, **kwargs)
|
76
|
+
|
77
|
+
def async_to_sync[_T](
|
78
|
+
self: AsyncEntrypoint[P, _T],
|
79
|
+
run: Callable[[Coroutine[Any, Any, _T]], _T] = asyncio.run,
|
80
|
+
/,
|
81
|
+
) -> Entrypoint[P, _T]:
|
82
|
+
function = self.function
|
83
|
+
|
84
|
+
@wraps(function)
|
85
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> _T:
|
86
|
+
return run(function(*args, **kwargs))
|
87
|
+
|
88
|
+
return self.__recreate(wrapper)
|
89
|
+
|
90
|
+
def decorate(
|
91
|
+
self,
|
92
|
+
decorator: Callable[[Callable[P, T]], Callable[P, T]],
|
93
|
+
/,
|
94
|
+
) -> Self:
|
95
|
+
return self.__recreate(decorator(self.function))
|
96
|
+
|
97
|
+
def inject(self) -> Self:
|
98
|
+
return self.decorate(self.module.make_injected_function)
|
99
|
+
|
100
|
+
def load_modules(
|
101
|
+
self,
|
102
|
+
/,
|
103
|
+
loader: PythonModuleLoader,
|
104
|
+
*packages: PythonModule | str,
|
105
|
+
) -> Self:
|
106
|
+
return self.setup(lambda: loader.load(*packages))
|
107
|
+
|
108
|
+
def setup(self, function: Callable[..., Any], /) -> Self:
|
109
|
+
@contextmanager
|
110
|
+
def decorator() -> Iterator[Any]:
|
111
|
+
yield function()
|
112
|
+
|
113
|
+
return self.decorate(decorator())
|
114
|
+
|
115
|
+
def async_setup[_T](
|
116
|
+
self: AsyncEntrypoint[P, _T],
|
117
|
+
function: Callable[..., Awaitable[Any]],
|
118
|
+
/,
|
119
|
+
) -> AsyncEntrypoint[P, _T]:
|
120
|
+
@asynccontextmanager
|
121
|
+
async def decorator() -> AsyncIterator[Any]:
|
122
|
+
yield await function()
|
123
|
+
|
124
|
+
return self.decorate(decorator())
|
125
|
+
|
126
|
+
def __recreate[**_P, _T](
|
127
|
+
self: Entrypoint[Any, Any],
|
128
|
+
function: Callable[_P, _T],
|
129
|
+
/,
|
130
|
+
) -> Entrypoint[_P, _T]:
|
131
|
+
return type(self)(function, self.module)
|
132
|
+
|
133
|
+
@classmethod
|
134
|
+
def _make_decorator[*Ts, _T](
|
135
|
+
cls,
|
136
|
+
setup_method: EntrypointSetupMethod[*Ts, P, T, _T],
|
137
|
+
/,
|
138
|
+
module: Module | None = None,
|
139
|
+
) -> EntrypointDecorator[P, T, _T]:
|
140
|
+
module = module or mod()
|
141
|
+
setup_method = module.make_injected_function(setup_method)
|
142
|
+
|
143
|
+
def decorator(function: Callable[P, T]) -> Callable[P, _T]:
|
144
|
+
self = cls(function, module)
|
145
|
+
return MethodType(setup_method, self)().function
|
146
|
+
|
147
|
+
return decorator
|
injection/loaders.py
CHANGED
@@ -57,7 +57,7 @@ class PythonModuleLoader:
|
|
57
57
|
|
58
58
|
def load(self, *packages: PythonModule | str) -> Self:
|
59
59
|
modules = itertools.chain.from_iterable(
|
60
|
-
self.
|
60
|
+
self.__iter_modules_from(package) for package in packages
|
61
61
|
)
|
62
62
|
self.__modules.update(modules)
|
63
63
|
return self
|
@@ -67,7 +67,7 @@ class PythonModuleLoader:
|
|
67
67
|
module_name in modules for modules in (self.__modules, self._sys_modules)
|
68
68
|
)
|
69
69
|
|
70
|
-
def
|
70
|
+
def __iter_modules_from(
|
71
71
|
self,
|
72
72
|
package: PythonModule | str,
|
73
73
|
) -> Iterator[tuple[str, PythonModule | None]]:
|
{python_injection-0.17.3.post0.dist-info → python_injection-0.18.0.post0.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: python-injection
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.18.0.post0
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
5
5
|
Project-URL: Repository, https://github.com/100nm/python-injection
|
6
6
|
Author: remimd
|
@@ -87,6 +87,7 @@ if __name__ == "__main__":
|
|
87
87
|
* [**Scoped dependencies**](https://github.com/100nm/python-injection/tree/prod/documentation/scoped-dependencies.md)
|
88
88
|
* [**Testing**](https://github.com/100nm/python-injection/tree/prod/documentation/testing.md)
|
89
89
|
* [**Advanced usage**](https://github.com/100nm/python-injection/tree/prod/documentation/advanced-usage.md)
|
90
|
-
* [**
|
90
|
+
* [**Loaders**](https://github.com/100nm/python-injection/tree/prod/documentation/loaders.md)
|
91
|
+
* [**Entrypoint**](https://github.com/100nm/python-injection/tree/prod/documentation/entrypoint.md)
|
91
92
|
* [**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations.md)
|
92
93
|
* [**Concrete example**](https://github.com/100nm/python-injection-example)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
injection/__init__.py,sha256=7ZRUlO5EEPWO7IlbYHD-8DOX-cg4Np4nYq5fpw-U56o,1259
|
2
2
|
injection/__init__.pyi,sha256=O2W6ZWcwH58JnDmJNkJ6cdoaO0D2NE7PEVFgfJNTwB0,10717
|
3
|
+
injection/entrypoint.py,sha256=UImrgGmzbkjVZemzzmpr-5hazej3NdI65_RuUbyHuJs,4205
|
3
4
|
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
4
|
-
injection/loaders.py,sha256=
|
5
|
+
injection/loaders.py,sha256=8tS3zVvmxKf5E1ZJBTBjZpHkXETxNCL1dq8rbh2vzy4,4075
|
5
6
|
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
7
|
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
8
|
injection/_core/descriptors.py,sha256=jH0pyIlPurMmU4yXr-HKS_7BJ-9d0XUvEx4pQre3QeI,704
|
@@ -21,6 +22,6 @@ injection/ext/fastapi.py,sha256=layUUer5IWiZX6Mmx1_RCYDLNCtEHtpya5ZL6TTBOkY,968
|
|
21
22
|
injection/ext/fastapi.pyi,sha256=8OZEUjHFB9n7QXv_dtXdDuXW-r2huQEFsJ03gJOOvwQ,125
|
22
23
|
injection/testing/__init__.py,sha256=PsrvNxYvn11if8CJpEYxcTxniqMt2w_7fBaDkY9RA5o,898
|
23
24
|
injection/testing/__init__.pyi,sha256=iOii0i9F5n7znltGeGQYI2KXC_if9SAogLh1h03yx-0,540
|
24
|
-
python_injection-0.
|
25
|
-
python_injection-0.
|
26
|
-
python_injection-0.
|
25
|
+
python_injection-0.18.0.post0.dist-info/METADATA,sha256=UsatTpimP3B5ScV03zZqI5N4Zx7CekJGEc0u82H_33o,3370
|
26
|
+
python_injection-0.18.0.post0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
27
|
+
python_injection-0.18.0.post0.dist-info/RECORD,,
|
File without changes
|