python-injection 0.10.3__tar.gz → 0.10.5__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.
- {python_injection-0.10.3 → python_injection-0.10.5}/PKG-INFO +1 -4
- {python_injection-0.10.3 → python_injection-0.10.5}/README.md +0 -3
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/common/type.py +11 -3
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/hook.py +11 -3
- python_injection-0.10.5/injection/integrations/__init__.py +11 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/integrations/blacksheep.py +2 -6
- python_injection-0.10.5/injection/integrations/fastapi.py +37 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/testing/__init__.py +1 -4
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/testing/__init__.pyi +1 -4
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/utils.py +6 -8
- {python_injection-0.10.3 → python_injection-0.10.5}/pyproject.toml +4 -1
- python_injection-0.10.3/injection/integrations/__init__.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/__init__.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/__init__.pyi +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/__init__.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/common/__init__.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/common/event.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/common/invertible.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/common/lazy.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/common/threading.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/_core/module.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/exceptions.py +0 -0
- {python_injection-0.10.3 → python_injection-0.10.5}/injection/py.typed +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: python-injection
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.5
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
5
5
|
Home-page: https://github.com/100nm/python-injection
|
6
6
|
License: MIT
|
@@ -63,7 +63,6 @@ class Printer:
|
|
63
63
|
self.history.append(message)
|
64
64
|
print(message)
|
65
65
|
|
66
|
-
|
67
66
|
@injectable
|
68
67
|
class Service:
|
69
68
|
def __init__(self, printer: Printer):
|
@@ -72,12 +71,10 @@ class Service:
|
|
72
71
|
def hello(self):
|
73
72
|
self.printer.print("Hello world!")
|
74
73
|
|
75
|
-
|
76
74
|
@inject
|
77
75
|
def main(service: Service):
|
78
76
|
service.hello()
|
79
77
|
|
80
|
-
|
81
78
|
if __name__ == "__main__":
|
82
79
|
main()
|
83
80
|
```
|
@@ -37,7 +37,6 @@ class Printer:
|
|
37
37
|
self.history.append(message)
|
38
38
|
print(message)
|
39
39
|
|
40
|
-
|
41
40
|
@injectable
|
42
41
|
class Service:
|
43
42
|
def __init__(self, printer: Printer):
|
@@ -46,12 +45,10 @@ class Service:
|
|
46
45
|
def hello(self):
|
47
46
|
self.printer.print("Hello world!")
|
48
47
|
|
49
|
-
|
50
48
|
@inject
|
51
49
|
def main(service: Service):
|
52
50
|
service.hello()
|
53
51
|
|
54
|
-
|
55
52
|
if __name__ == "__main__":
|
56
53
|
main()
|
57
54
|
```
|
@@ -1,9 +1,17 @@
|
|
1
1
|
from collections.abc import Awaitable, Callable, Iterable, Iterator
|
2
2
|
from inspect import iscoroutinefunction, isfunction
|
3
3
|
from types import GenericAlias, UnionType
|
4
|
-
from typing import
|
5
|
-
|
6
|
-
|
4
|
+
from typing import (
|
5
|
+
Annotated,
|
6
|
+
Any,
|
7
|
+
TypeAliasType,
|
8
|
+
Union,
|
9
|
+
get_args,
|
10
|
+
get_origin,
|
11
|
+
get_type_hints,
|
12
|
+
)
|
13
|
+
|
14
|
+
type TypeDef[T] = type[T] | TypeAliasType | GenericAlias
|
7
15
|
type InputType[T] = TypeDef[T] | UnionType
|
8
16
|
type TypeInfo[T] = InputType[T] | Callable[..., T] | Iterable[TypeInfo[T]]
|
9
17
|
|
@@ -2,7 +2,7 @@ import itertools
|
|
2
2
|
from collections.abc import Callable, Generator, Iterator
|
3
3
|
from dataclasses import dataclass, field
|
4
4
|
from inspect import isclass, isgeneratorfunction
|
5
|
-
from typing import Self
|
5
|
+
from typing import Any, Self
|
6
6
|
|
7
7
|
from injection.exceptions import HookError
|
8
8
|
|
@@ -48,11 +48,11 @@ class Hook[**P, T]:
|
|
48
48
|
handler: Callable[P, T],
|
49
49
|
function: HookFunction[P, T],
|
50
50
|
) -> Callable[P, T]:
|
51
|
-
if not
|
51
|
+
if not cls.__is_generator_function(function):
|
52
52
|
return function # type: ignore[return-value]
|
53
53
|
|
54
54
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
55
|
-
hook = function(*args, **kwargs)
|
55
|
+
hook: HookGenerator[T] = function(*args, **kwargs) # type: ignore[assignment]
|
56
56
|
|
57
57
|
try:
|
58
58
|
next(hook)
|
@@ -87,6 +87,14 @@ class Hook[**P, T]:
|
|
87
87
|
|
88
88
|
return handler
|
89
89
|
|
90
|
+
@staticmethod
|
91
|
+
def __is_generator_function(obj: Any) -> bool:
|
92
|
+
for o in obj, getattr(obj, "__call__", None):
|
93
|
+
if isgeneratorfunction(o):
|
94
|
+
return True
|
95
|
+
|
96
|
+
return False
|
97
|
+
|
90
98
|
|
91
99
|
def apply_hooks[**P, T](
|
92
100
|
handler: Callable[P, T],
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from importlib.util import find_spec
|
2
|
+
from typing import Literal
|
3
|
+
|
4
|
+
__all__ = ("_is_installed",)
|
5
|
+
|
6
|
+
|
7
|
+
def _is_installed(package: str, needed_for: object, /) -> Literal[True]:
|
8
|
+
if find_spec(package) is None:
|
9
|
+
raise RuntimeError(f"To use `{needed_for}`, {package} must be installed.")
|
10
|
+
|
11
|
+
return True
|
@@ -1,15 +1,11 @@
|
|
1
1
|
from typing import Any, override
|
2
2
|
|
3
3
|
from injection import Module, mod
|
4
|
+
from injection.integrations import _is_installed
|
4
5
|
|
5
6
|
__all__ = ("InjectionServices",)
|
6
7
|
|
7
|
-
|
8
|
-
try:
|
9
|
-
import blacksheep # noqa: F401
|
10
|
-
except ImportError as exc: # pragma: no cover
|
11
|
-
raise ImportError(f"To use `{__name__}`, blacksheep must be installed.") from exc
|
12
|
-
else:
|
8
|
+
if _is_installed("blacksheep", __name__):
|
13
9
|
from rodi import ContainerProtocol
|
14
10
|
|
15
11
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from injection import Module, mod
|
5
|
+
from injection.exceptions import InjectionError
|
6
|
+
from injection.integrations import _is_installed
|
7
|
+
|
8
|
+
__all__ = ("Inject",)
|
9
|
+
|
10
|
+
if _is_installed("fastapi", __name__):
|
11
|
+
from fastapi import Depends
|
12
|
+
|
13
|
+
|
14
|
+
def Inject[T](cls: type[T] | Any, /, module: Module | None = None) -> Any: # noqa: N802
|
15
|
+
"""
|
16
|
+
Declare a FastAPI dependency with `python-injection`.
|
17
|
+
"""
|
18
|
+
|
19
|
+
dependency: InjectionDependency[T] = InjectionDependency(cls, module or mod())
|
20
|
+
return Depends(dependency)
|
21
|
+
|
22
|
+
|
23
|
+
class InjectionDependency[T]:
|
24
|
+
__slots__ = ("__call__",)
|
25
|
+
|
26
|
+
__call__: Callable[[], T]
|
27
|
+
|
28
|
+
def __init__(self, cls: type[T] | Any, module: Module):
|
29
|
+
lazy_instance = module.get_lazy_instance(cls)
|
30
|
+
self.__call__ = lambda: self.__ensure(~lazy_instance, cls)
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def __ensure[_T](instance: _T | None, cls: type[_T] | Any) -> _T:
|
34
|
+
if instance is None:
|
35
|
+
raise InjectionError(f"`{cls}` is an unknown dependency.")
|
36
|
+
|
37
|
+
return instance
|
@@ -1,4 +1,3 @@
|
|
1
|
-
from contextlib import ContextDecorator
|
2
1
|
from typing import ContextManager, Final
|
3
2
|
|
4
3
|
from injection import mod
|
@@ -22,7 +21,5 @@ test_injectable = mod(_TEST_PROFILE_NAME).injectable
|
|
22
21
|
test_singleton = mod(_TEST_PROFILE_NAME).singleton
|
23
22
|
|
24
23
|
|
25
|
-
def load_test_profile(
|
26
|
-
*other_profile_names: str,
|
27
|
-
) -> ContextManager[None] | ContextDecorator:
|
24
|
+
def load_test_profile(*other_profile_names: str) -> ContextManager[None]:
|
28
25
|
return load_profile(_TEST_PROFILE_NAME, *other_profile_names)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
from contextlib import ContextDecorator
|
2
1
|
from typing import ContextManager
|
3
2
|
|
4
3
|
import injection as _
|
@@ -9,9 +8,7 @@ test_constant = _.constant
|
|
9
8
|
test_injectable = _.injectable
|
10
9
|
test_singleton = _.singleton
|
11
10
|
|
12
|
-
def load_test_profile(
|
13
|
-
*additional_names: str,
|
14
|
-
) -> ContextManager[None] | ContextDecorator:
|
11
|
+
def load_test_profile(*other_profile_names: str) -> ContextManager[None]:
|
15
12
|
"""
|
16
13
|
Context manager or decorator for temporary use test module.
|
17
14
|
"""
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from collections.abc import Callable, Iterator
|
2
|
-
from contextlib import
|
2
|
+
from contextlib import contextmanager
|
3
3
|
from importlib import import_module
|
4
4
|
from pkgutil import walk_packages
|
5
5
|
from types import ModuleType as PythonModule
|
@@ -10,11 +10,7 @@ from injection import mod
|
|
10
10
|
__all__ = ("load_packages", "load_profile")
|
11
11
|
|
12
12
|
|
13
|
-
def load_profile(
|
14
|
-
name: str,
|
15
|
-
/,
|
16
|
-
*other_profile_names: str,
|
17
|
-
) -> ContextManager[None] | ContextDecorator:
|
13
|
+
def load_profile(name: str, /, *other_profile_names: str) -> ContextManager[None]:
|
18
14
|
"""
|
19
15
|
Injection module initialization function based on profile name.
|
20
16
|
A profile name is equivalent to an injection module name.
|
@@ -27,12 +23,14 @@ def load_profile(
|
|
27
23
|
|
28
24
|
target = mod().unlock().init_modules(*modules)
|
29
25
|
|
26
|
+
del module, modules
|
27
|
+
|
30
28
|
@contextmanager
|
31
|
-
def
|
29
|
+
def cleaner() -> Iterator[None]:
|
32
30
|
yield
|
33
31
|
target.unlock().init_modules()
|
34
32
|
|
35
|
-
return
|
33
|
+
return cleaner()
|
36
34
|
|
37
35
|
|
38
36
|
def load_packages(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "python-injection"
|
3
|
-
version = "0.10.
|
3
|
+
version = "0.10.5"
|
4
4
|
description = "Fast and easy dependency injection framework."
|
5
5
|
license = "MIT"
|
6
6
|
authors = ["remimd"]
|
@@ -32,6 +32,8 @@ ruff = "*"
|
|
32
32
|
|
33
33
|
[tool.poetry.group.test.dependencies]
|
34
34
|
blacksheep = "*"
|
35
|
+
fastapi = "*"
|
36
|
+
httpx = "*"
|
35
37
|
pydantic = "*"
|
36
38
|
pytest = "*"
|
37
39
|
pytest-asyncio = "*"
|
@@ -83,6 +85,7 @@ warn_required_dynamic_aliases = true
|
|
83
85
|
[tool.pytest.ini_options]
|
84
86
|
python_files = "test_*.py"
|
85
87
|
addopts = "-p no:warnings --tb=short --cov=./ --cov-report term-missing:skip-covered"
|
88
|
+
asyncio_default_fixture_loop_scope = "session"
|
86
89
|
asyncio_mode = "auto"
|
87
90
|
testpaths = "**/tests/"
|
88
91
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|