python-injection 0.17.3.post0__tar.gz → 0.18.0.post0__tar.gz

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.
Files changed (28) hide show
  1. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/PKG-INFO +3 -2
  2. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/README.md +2 -1
  3. python_injection-0.18.0.post0/injection/entrypoint.py +147 -0
  4. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/loaders.py +2 -2
  5. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/pyproject.toml +7 -2
  6. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/.gitignore +0 -0
  7. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/__init__.py +0 -0
  8. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/__init__.pyi +0 -0
  9. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/__init__.py +0 -0
  10. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/__init__.py +0 -0
  11. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/asynchronous.py +0 -0
  12. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/event.py +0 -0
  13. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/invertible.py +0 -0
  14. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/key.py +0 -0
  15. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/lazy.py +0 -0
  16. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/common/type.py +0 -0
  17. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/descriptors.py +0 -0
  18. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/injectables.py +0 -0
  19. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/module.py +0 -0
  20. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/scope.py +0 -0
  21. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/_core/slots.py +0 -0
  22. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/exceptions.py +0 -0
  23. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/ext/__init__.py +0 -0
  24. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/ext/fastapi.py +0 -0
  25. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/ext/fastapi.pyi +0 -0
  26. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/py.typed +0 -0
  27. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/testing/__init__.py +0 -0
  28. {python_injection-0.17.3.post0 → python_injection-0.18.0.post0}/injection/testing/__init__.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-injection
3
- Version: 0.17.3.post0
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
- * [**Utils**](https://github.com/100nm/python-injection/tree/prod/documentation/utils.md)
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)
@@ -62,6 +62,7 @@ if __name__ == "__main__":
62
62
  * [**Scoped dependencies**](https://github.com/100nm/python-injection/tree/prod/documentation/scoped-dependencies.md)
63
63
  * [**Testing**](https://github.com/100nm/python-injection/tree/prod/documentation/testing.md)
64
64
  * [**Advanced usage**](https://github.com/100nm/python-injection/tree/prod/documentation/advanced-usage.md)
65
- * [**Utils**](https://github.com/100nm/python-injection/tree/prod/documentation/utils.md)
65
+ * [**Loaders**](https://github.com/100nm/python-injection/tree/prod/documentation/loaders.md)
66
+ * [**Entrypoint**](https://github.com/100nm/python-injection/tree/prod/documentation/entrypoint.md)
66
67
  * [**Integrations**](https://github.com/100nm/python-injection/tree/prod/documentation/integrations.md)
67
68
  * [**Concrete example**](https://github.com/100nm/python-injection-example)
@@ -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
@@ -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.__iter_modules(package) for package in packages
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 __iter_modules(
70
+ def __iter_modules_from(
71
71
  self,
72
72
  package: PythonModule | str,
73
73
  ) -> Iterator[tuple[str, PythonModule | None]]:
@@ -13,6 +13,11 @@ dev = [
13
13
  "mypy",
14
14
  "ruff",
15
15
  ]
16
+ doc = [
17
+ "fastapi",
18
+ "typer",
19
+ "uvloop",
20
+ ]
16
21
  test = [
17
22
  "fastapi",
18
23
  "httpx",
@@ -24,7 +29,7 @@ test = [
24
29
 
25
30
  [project]
26
31
  name = "python-injection"
27
- version = "0.17.3.post0"
32
+ version = "0.18.0.post0"
28
33
  description = "Fast and easy dependency injection framework."
29
34
  license = { text = "MIT" }
30
35
  readme = "README.md"
@@ -119,5 +124,5 @@ ignore = ["N818"]
119
124
  fixable = ["ALL"]
120
125
 
121
126
  [tool.uv]
122
- default-groups = ["bench", "dev", "test"]
127
+ default-groups = ["bench", "dev", "doc", "test"]
123
128
  package = true