anydi 0.33.1__py3-none-any.whl → 0.34.1__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.
- anydi/__init__.py +9 -3
- anydi/_container.py +471 -76
- anydi/_context.py +29 -265
- anydi/_provider.py +32 -4
- anydi/_types.py +19 -3
- anydi/_utils.py +49 -12
- anydi/ext/django/middleware.py +4 -4
- anydi/ext/starlette/middleware.py +2 -2
- {anydi-0.33.1.dist-info → anydi-0.34.1.dist-info}/METADATA +2 -3
- {anydi-0.33.1.dist-info → anydi-0.34.1.dist-info}/RECORD +13 -15
- anydi/_module.py +0 -94
- anydi/_scanner.py +0 -171
- {anydi-0.33.1.dist-info → anydi-0.34.1.dist-info}/LICENSE +0 -0
- {anydi-0.33.1.dist-info → anydi-0.34.1.dist-info}/WHEEL +0 -0
- {anydi-0.33.1.dist-info → anydi-0.34.1.dist-info}/entry_points.txt +0 -0
anydi/_module.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""AnyDI decorators module."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import inspect
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
|
7
|
-
|
|
8
|
-
from typing_extensions import Concatenate, NamedTuple, ParamSpec
|
|
9
|
-
|
|
10
|
-
from ._types import Scope
|
|
11
|
-
from ._utils import import_string
|
|
12
|
-
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from ._container import Container
|
|
15
|
-
|
|
16
|
-
T = TypeVar("T")
|
|
17
|
-
M = TypeVar("M", bound="Module")
|
|
18
|
-
P = ParamSpec("P")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ModuleMeta(type):
|
|
22
|
-
"""A metaclass used for the Module base class."""
|
|
23
|
-
|
|
24
|
-
def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> Any:
|
|
25
|
-
attrs["providers"] = [
|
|
26
|
-
(name, getattr(value, "__provider__"))
|
|
27
|
-
for name, value in attrs.items()
|
|
28
|
-
if hasattr(value, "__provider__")
|
|
29
|
-
]
|
|
30
|
-
return super().__new__(cls, name, bases, attrs)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Module(metaclass=ModuleMeta):
|
|
34
|
-
"""A base class for defining AnyDI modules."""
|
|
35
|
-
|
|
36
|
-
providers: list[tuple[str, ProviderDecoratorArgs]]
|
|
37
|
-
|
|
38
|
-
def configure(self, container: Container) -> None:
|
|
39
|
-
"""Configure the AnyDI container with providers and their dependencies."""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class ModuleRegistry:
|
|
43
|
-
def __init__(self, container: Container) -> None:
|
|
44
|
-
self.container = container
|
|
45
|
-
|
|
46
|
-
def register(
|
|
47
|
-
self, module: Module | type[Module] | Callable[[Container], None] | str
|
|
48
|
-
) -> None:
|
|
49
|
-
"""Register a module as a callable, module type, or module instance."""
|
|
50
|
-
|
|
51
|
-
# Callable Module
|
|
52
|
-
if inspect.isfunction(module):
|
|
53
|
-
module(self.container)
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
# Module path
|
|
57
|
-
if isinstance(module, str):
|
|
58
|
-
module = import_string(module)
|
|
59
|
-
|
|
60
|
-
# Class based Module or Module type
|
|
61
|
-
if inspect.isclass(module) and issubclass(module, Module):
|
|
62
|
-
module = module()
|
|
63
|
-
|
|
64
|
-
if isinstance(module, Module):
|
|
65
|
-
module.configure(self.container)
|
|
66
|
-
for provider_name, decorator_args in module.providers:
|
|
67
|
-
obj = getattr(module, provider_name)
|
|
68
|
-
self.container.provider(
|
|
69
|
-
scope=decorator_args.scope,
|
|
70
|
-
override=decorator_args.override,
|
|
71
|
-
)(obj)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class ProviderDecoratorArgs(NamedTuple):
|
|
75
|
-
scope: Scope
|
|
76
|
-
override: bool
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def provider(
|
|
80
|
-
*, scope: Scope, override: bool = False
|
|
81
|
-
) -> Callable[[Callable[Concatenate[M, P], T]], Callable[Concatenate[M, P], T]]:
|
|
82
|
-
"""Decorator for marking a function or method as a provider in a AnyDI module."""
|
|
83
|
-
|
|
84
|
-
def decorator(
|
|
85
|
-
target: Callable[Concatenate[M, P], T],
|
|
86
|
-
) -> Callable[Concatenate[M, P], T]:
|
|
87
|
-
setattr(
|
|
88
|
-
target,
|
|
89
|
-
"__provider__",
|
|
90
|
-
ProviderDecoratorArgs(scope=scope, override=override),
|
|
91
|
-
)
|
|
92
|
-
return target
|
|
93
|
-
|
|
94
|
-
return decorator
|
anydi/_scanner.py
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import importlib
|
|
4
|
-
import inspect
|
|
5
|
-
import pkgutil
|
|
6
|
-
from collections.abc import Iterable
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from types import ModuleType
|
|
9
|
-
from typing import (
|
|
10
|
-
TYPE_CHECKING,
|
|
11
|
-
Any,
|
|
12
|
-
Callable,
|
|
13
|
-
TypeVar,
|
|
14
|
-
Union,
|
|
15
|
-
cast,
|
|
16
|
-
final,
|
|
17
|
-
overload,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
from typing_extensions import NamedTuple, ParamSpec
|
|
21
|
-
|
|
22
|
-
from ._types import is_marker
|
|
23
|
-
from ._utils import get_typed_parameters
|
|
24
|
-
|
|
25
|
-
if TYPE_CHECKING:
|
|
26
|
-
from ._container import Container
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
T = TypeVar("T")
|
|
30
|
-
P = ParamSpec("P")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass(frozen=True)
|
|
34
|
-
class Dependency:
|
|
35
|
-
member: Any
|
|
36
|
-
module: ModuleType
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@final
|
|
40
|
-
class Scanner:
|
|
41
|
-
"""A class for scanning packages or modules for decorated objects
|
|
42
|
-
and injecting dependencies."""
|
|
43
|
-
|
|
44
|
-
def __init__(self, container: Container) -> None:
|
|
45
|
-
self.container = container
|
|
46
|
-
|
|
47
|
-
def scan(
|
|
48
|
-
self,
|
|
49
|
-
/,
|
|
50
|
-
packages: ModuleType | str | Iterable[ModuleType | str],
|
|
51
|
-
*,
|
|
52
|
-
tags: Iterable[str] | None = None,
|
|
53
|
-
) -> None:
|
|
54
|
-
"""Scan packages or modules for decorated members and inject dependencies."""
|
|
55
|
-
dependencies: list[Dependency] = []
|
|
56
|
-
|
|
57
|
-
if isinstance(packages, Iterable) and not isinstance(packages, str):
|
|
58
|
-
scan_packages: Iterable[ModuleType | str] = packages
|
|
59
|
-
else:
|
|
60
|
-
scan_packages = cast(Iterable[Union[ModuleType, str]], [packages])
|
|
61
|
-
|
|
62
|
-
for package in scan_packages:
|
|
63
|
-
dependencies.extend(self._scan_package(package, tags=tags))
|
|
64
|
-
|
|
65
|
-
for dependency in dependencies:
|
|
66
|
-
decorator = self.container.inject()(dependency.member)
|
|
67
|
-
setattr(dependency.module, dependency.member.__name__, decorator)
|
|
68
|
-
|
|
69
|
-
def _scan_package(
|
|
70
|
-
self,
|
|
71
|
-
package: ModuleType | str,
|
|
72
|
-
*,
|
|
73
|
-
tags: Iterable[str] | None = None,
|
|
74
|
-
) -> list[Dependency]:
|
|
75
|
-
"""Scan a package or module for decorated members."""
|
|
76
|
-
tags = tags or []
|
|
77
|
-
if isinstance(package, str):
|
|
78
|
-
package = importlib.import_module(package)
|
|
79
|
-
|
|
80
|
-
package_path = getattr(package, "__path__", None)
|
|
81
|
-
|
|
82
|
-
if not package_path:
|
|
83
|
-
return self._scan_module(package, tags=tags)
|
|
84
|
-
|
|
85
|
-
dependencies: list[Dependency] = []
|
|
86
|
-
|
|
87
|
-
for module_info in pkgutil.walk_packages(
|
|
88
|
-
path=package_path, prefix=package.__name__ + "."
|
|
89
|
-
):
|
|
90
|
-
module = importlib.import_module(module_info.name)
|
|
91
|
-
dependencies.extend(self._scan_module(module, tags=tags))
|
|
92
|
-
|
|
93
|
-
return dependencies
|
|
94
|
-
|
|
95
|
-
def _scan_module(
|
|
96
|
-
self, module: ModuleType, *, tags: Iterable[str]
|
|
97
|
-
) -> list[Dependency]:
|
|
98
|
-
"""Scan a module for decorated members."""
|
|
99
|
-
dependencies: list[Dependency] = []
|
|
100
|
-
|
|
101
|
-
for _, member in inspect.getmembers(module):
|
|
102
|
-
if getattr(member, "__module__", None) != module.__name__ or not callable(
|
|
103
|
-
member
|
|
104
|
-
):
|
|
105
|
-
continue
|
|
106
|
-
|
|
107
|
-
decorator_args: InjectDecoratorArgs = getattr(
|
|
108
|
-
member,
|
|
109
|
-
"__injectable__",
|
|
110
|
-
InjectDecoratorArgs(wrapped=False, tags=[]),
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
if tags and (
|
|
114
|
-
decorator_args.tags
|
|
115
|
-
and not set(decorator_args.tags).intersection(tags)
|
|
116
|
-
or not decorator_args.tags
|
|
117
|
-
):
|
|
118
|
-
continue
|
|
119
|
-
|
|
120
|
-
if decorator_args.wrapped:
|
|
121
|
-
dependencies.append(
|
|
122
|
-
self._create_dependency(member=member, module=module)
|
|
123
|
-
)
|
|
124
|
-
continue
|
|
125
|
-
|
|
126
|
-
# Get by Marker
|
|
127
|
-
for parameter in get_typed_parameters(member):
|
|
128
|
-
if is_marker(parameter.default):
|
|
129
|
-
dependencies.append(
|
|
130
|
-
self._create_dependency(member=member, module=module)
|
|
131
|
-
)
|
|
132
|
-
continue
|
|
133
|
-
|
|
134
|
-
return dependencies
|
|
135
|
-
|
|
136
|
-
def _create_dependency(self, member: Any, module: ModuleType) -> Dependency:
|
|
137
|
-
"""Create a `Dependency` object from the scanned member and module."""
|
|
138
|
-
if hasattr(member, "__wrapped__"):
|
|
139
|
-
member = member.__wrapped__
|
|
140
|
-
return Dependency(member=member, module=module)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
class InjectDecoratorArgs(NamedTuple):
|
|
144
|
-
wrapped: bool
|
|
145
|
-
tags: Iterable[str] | None
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@overload
|
|
149
|
-
def injectable(func: Callable[P, T]) -> Callable[P, T]: ...
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@overload
|
|
153
|
-
def injectable(
|
|
154
|
-
*, tags: Iterable[str] | None = None
|
|
155
|
-
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def injectable(
|
|
159
|
-
func: Callable[P, T] | None = None,
|
|
160
|
-
tags: Iterable[str] | None = None,
|
|
161
|
-
) -> Callable[[Callable[P, T]], Callable[P, T]] | Callable[P, T]:
|
|
162
|
-
"""Decorator for marking a function or method as requiring dependency injection."""
|
|
163
|
-
|
|
164
|
-
def decorator(inner: Callable[P, T]) -> Callable[P, T]:
|
|
165
|
-
setattr(inner, "__injectable__", InjectDecoratorArgs(wrapped=True, tags=tags))
|
|
166
|
-
return inner
|
|
167
|
-
|
|
168
|
-
if func is None:
|
|
169
|
-
return decorator
|
|
170
|
-
|
|
171
|
-
return decorator(func)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|