anydi 0.22.1__py3-none-any.whl → 0.37.4__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 +14 -14
- anydi/_container.py +811 -571
- anydi/_context.py +39 -281
- anydi/_provider.py +232 -0
- anydi/_types.py +49 -96
- anydi/_utils.py +108 -77
- anydi/ext/_utils.py +49 -28
- anydi/ext/django/__init__.py +9 -0
- anydi/ext/django/_container.py +18 -0
- anydi/ext/django/_settings.py +39 -0
- anydi/ext/django/_utils.py +128 -0
- anydi/ext/django/apps.py +85 -0
- anydi/ext/django/middleware.py +28 -0
- anydi/ext/django/ninja/__init__.py +16 -0
- anydi/ext/django/ninja/_operation.py +75 -0
- anydi/ext/django/ninja/_signature.py +64 -0
- anydi/ext/fastapi.py +11 -27
- anydi/ext/faststream.py +58 -0
- anydi/ext/pydantic_settings.py +48 -0
- anydi/ext/pytest_plugin.py +67 -41
- anydi/ext/starlette/middleware.py +2 -16
- {anydi-0.22.1.dist-info → anydi-0.37.4.dist-info}/METADATA +71 -21
- anydi-0.37.4.dist-info/RECORD +29 -0
- {anydi-0.22.1.dist-info → anydi-0.37.4.dist-info}/WHEEL +1 -1
- anydi-0.37.4.dist-info/entry_points.txt +2 -0
- anydi/_logger.py +0 -3
- anydi/_module.py +0 -124
- anydi/_scanner.py +0 -233
- anydi-0.22.1.dist-info/RECORD +0 -20
- anydi-0.22.1.dist-info/entry_points.txt +0 -3
- {anydi-0.22.1.dist-info → anydi-0.37.4.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: anydi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.4
|
|
4
4
|
Summary: Dependency Injection library
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Requires-Python: >=3.8,<4.0
|
|
5
|
+
Project-URL: Repository, https://github.com/antonrh/anydi
|
|
6
|
+
Author-email: Anton Ruhlov <antonruhlov@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: application,async,asyncio,dependencies,dependency injection,di
|
|
11
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
11
|
Classifier: Environment :: Web Environment
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
@@ -16,26 +15,22 @@ Classifier: Intended Audience :: System Administrators
|
|
|
16
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
16
|
Classifier: Operating System :: OS Independent
|
|
18
17
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
-
Classifier: Programming Language :: Python :: 3
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
24
|
Classifier: Topic :: Internet
|
|
27
25
|
Classifier: Topic :: Software Development
|
|
28
26
|
Classifier: Topic :: Software Development :: Libraries
|
|
29
27
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
30
28
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
31
29
|
Classifier: Typing :: Typed
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: mkdocs-material (>=9.1.13,<10.0.0) ; extra == "docs"
|
|
37
|
-
Requires-Dist: typing-extensions (>=4.8.0,<5.0.0)
|
|
38
|
-
Project-URL: Repository, https://github.com/antonrh/anydi
|
|
30
|
+
Requires-Python: ~=3.9
|
|
31
|
+
Requires-Dist: anyio>=3.7.1
|
|
32
|
+
Requires-Dist: typing-extensions<5,>=4.12.1
|
|
33
|
+
Requires-Dist: wrapt<2,>=1.17.0
|
|
39
34
|
Description-Content-Type: text/markdown
|
|
40
35
|
|
|
41
36
|
# AnyDI
|
|
@@ -63,7 +58,7 @@ http://anydi.readthedocs.io/
|
|
|
63
58
|
|
|
64
59
|
---
|
|
65
60
|
|
|
66
|
-
`AnyDI` is a modern, lightweight Dependency Injection library suitable for any synchronous or asynchronous applications with Python 3.
|
|
61
|
+
`AnyDI` is a modern, lightweight Dependency Injection library suitable for any synchronous or asynchronous applications with Python 3.9+, based on type annotations ([PEP 484](https://peps.python.org/pep-0484/)).
|
|
67
62
|
|
|
68
63
|
The key features are:
|
|
69
64
|
|
|
@@ -89,7 +84,7 @@ pip install anydi
|
|
|
89
84
|
*app.py*
|
|
90
85
|
|
|
91
86
|
```python
|
|
92
|
-
from anydi import
|
|
87
|
+
from anydi import auto, Container
|
|
93
88
|
|
|
94
89
|
container = Container()
|
|
95
90
|
|
|
@@ -100,7 +95,7 @@ def message() -> str:
|
|
|
100
95
|
|
|
101
96
|
|
|
102
97
|
@container.inject
|
|
103
|
-
def say_hello(message: str =
|
|
98
|
+
def say_hello(message: str = auto) -> None:
|
|
104
99
|
print(message)
|
|
105
100
|
|
|
106
101
|
|
|
@@ -139,3 +134,58 @@ def say_hello(message: str = Inject()) -> dict[str, str]:
|
|
|
139
134
|
anydi.ext.fastapi.install(app, container)
|
|
140
135
|
```
|
|
141
136
|
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## Django Ninja Example
|
|
140
|
+
|
|
141
|
+
*container.py*
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from anydi import Container
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_container() -> Container:
|
|
148
|
+
container = Container()
|
|
149
|
+
|
|
150
|
+
@container.provider(scope="singleton")
|
|
151
|
+
def message() -> str:
|
|
152
|
+
return "Hello, World!"
|
|
153
|
+
|
|
154
|
+
return container
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
*settings.py*
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
INSTALLED_APPS = [
|
|
161
|
+
...
|
|
162
|
+
"anydi.ext.django",
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
ANYDI = {
|
|
166
|
+
"CONTAINER_FACTORY": "myapp.container.get_container",
|
|
167
|
+
"PATCH_NINJA": True,
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
*urls.py*
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from django.http import HttpRequest
|
|
175
|
+
from django.urls import path
|
|
176
|
+
from ninja import NinjaAPI
|
|
177
|
+
|
|
178
|
+
from anydi import auto
|
|
179
|
+
|
|
180
|
+
api = NinjaAPI()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@api.get("/hello")
|
|
184
|
+
def say_hello(request: HttpRequest, message: str = auto) -> dict[str, str]:
|
|
185
|
+
return {"message": message}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
urlpatterns = [
|
|
189
|
+
path("api/", api.urls),
|
|
190
|
+
]
|
|
191
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
anydi/__init__.py,sha256=OfRg2EfXD65pHTGQKhfkABMwUhw5LvsuTQV_Tv4V4wk,501
|
|
2
|
+
anydi/_container.py,sha256=yH1DsSQFkbfxGaDFs47nr-iXLE_Wwgv6QMTGWrRb8cE,38524
|
|
3
|
+
anydi/_context.py,sha256=7LV_SL4QWkJeiG7_4D9PZ5lmU-MPzhofxC95zCgY9Gc,2651
|
|
4
|
+
anydi/_provider.py,sha256=1IyxHO83NHjsPDHLDIZtW1pJ7i8VpWD3EM4T6duw9zA,7661
|
|
5
|
+
anydi/_types.py,sha256=fdO4xNXtGMxVArmlfDkFYbyR895ixkBTW6V8lMceN7Q,1562
|
|
6
|
+
anydi/_utils.py,sha256=INI0jNIXrJ6LS4zqJymMO2yUEobpxmBGASf4G_vR6AU,4378
|
|
7
|
+
anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
anydi/ext/_utils.py,sha256=U6sRqWzccWUu7eMhbXX1NrwcaxitQF9cO1KxnKF37gw,2566
|
|
10
|
+
anydi/ext/fastapi.py,sha256=AEL3ubu-LxUPHMMt1YIn3En_JZC7nyBKmKxmhka3O3c,2436
|
|
11
|
+
anydi/ext/faststream.py,sha256=qXnNGvAqWWp9kbhbQUE6EF_OPUiYQmtOH211_O7BI_0,1898
|
|
12
|
+
anydi/ext/pydantic_settings.py,sha256=8IXXLuG_OvKbvKlBkBRQUHcXgbTpgQUxeWyoMcRIUQM,1488
|
|
13
|
+
anydi/ext/pytest_plugin.py,sha256=ShGhiZnP1KyMHhnc9Ci1RKAuHVhw628OTS2P2BLEOfc,5001
|
|
14
|
+
anydi/ext/django/__init__.py,sha256=QI1IABCVgSDTUoh7M9WMECKXwB3xvh04HfQ9TOWw1Mk,223
|
|
15
|
+
anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
|
|
16
|
+
anydi/ext/django/_settings.py,sha256=Z0RlAuXoO73oahWeMkK10w8c-4uCBde-DBpeKTV5USY,853
|
|
17
|
+
anydi/ext/django/_utils.py,sha256=q6X6GApBm0oBK8DnoRZhTq2m4tAdKRYL__gVgKn3idg,3977
|
|
18
|
+
anydi/ext/django/apps.py,sha256=mjbf_mDCpNSriGnILzhRIr8wFHLMEK8sUerbmRku6i0,2844
|
|
19
|
+
anydi/ext/django/middleware.py,sha256=5OUdp0OWRozyW338Sq04BDhacaFlyUTTOduS_7EwCTA,854
|
|
20
|
+
anydi/ext/django/ninja/__init__.py,sha256=kW3grUgWp_nkWSG_-39ADHMrZLGNcj9TsJ9OW8iWWrk,546
|
|
21
|
+
anydi/ext/django/ninja/_operation.py,sha256=wSWa7D73XTVlOibmOciv2l6JHPe1ERZcXrqI8W-oO2w,2696
|
|
22
|
+
anydi/ext/django/ninja/_signature.py,sha256=2cSzKxBIxXLqtwNuH6GSlmjVJFftoGmleWfyk_NVEWw,2207
|
|
23
|
+
anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
|
|
25
|
+
anydi-0.37.4.dist-info/METADATA,sha256=LzSi277vUt7DJCFHnycYjlLGKaLM60uf6pJX-VZQZzs,4917
|
|
26
|
+
anydi-0.37.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
27
|
+
anydi-0.37.4.dist-info/entry_points.txt,sha256=Nklo9f3Oe4AkNsEgC4g43nCJ-23QDngZSVDNRMdaILI,43
|
|
28
|
+
anydi-0.37.4.dist-info/licenses/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
|
|
29
|
+
anydi-0.37.4.dist-info/RECORD,,
|
anydi/_logger.py
DELETED
anydi/_module.py
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"""AnyDI decorators module."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import inspect
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type, TypeVar, Union
|
|
7
|
-
|
|
8
|
-
from typing_extensions import Concatenate, NamedTuple, ParamSpec
|
|
9
|
-
|
|
10
|
-
from ._types import Scope
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from ._container import Container
|
|
14
|
-
|
|
15
|
-
T = TypeVar("T")
|
|
16
|
-
M = TypeVar("M", bound="Module")
|
|
17
|
-
P = ParamSpec("P")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ModuleMeta(type):
|
|
21
|
-
"""A metaclass used for the Module base class.
|
|
22
|
-
|
|
23
|
-
This metaclass extracts provider information from the class attributes
|
|
24
|
-
and stores it in the `providers` attribute.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __new__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]) -> Any:
|
|
28
|
-
"""Create a new instance of the ModuleMeta class.
|
|
29
|
-
|
|
30
|
-
This method extracts provider information from the class attributes and
|
|
31
|
-
stores it in the `providers` attribute.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
name: The name of the class.
|
|
35
|
-
bases: The base classes of the class.
|
|
36
|
-
attrs: The attributes of the class.
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
The new instance of the class.
|
|
40
|
-
"""
|
|
41
|
-
attrs["providers"] = [
|
|
42
|
-
(name, getattr(value, "__provider__"))
|
|
43
|
-
for name, value in attrs.items()
|
|
44
|
-
if hasattr(value, "__provider__")
|
|
45
|
-
]
|
|
46
|
-
return super().__new__(cls, name, bases, attrs)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class Module(metaclass=ModuleMeta):
|
|
50
|
-
"""A base class for defining AnyDI modules."""
|
|
51
|
-
|
|
52
|
-
providers: List[Tuple[str, ProviderDecoratorArgs]]
|
|
53
|
-
|
|
54
|
-
def configure(self, container: Container) -> None:
|
|
55
|
-
"""Configure the AnyDI container with providers and their dependencies.
|
|
56
|
-
|
|
57
|
-
This method can be overridden in derived classes to provide the
|
|
58
|
-
configuration logic.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
container: The AnyDI container to be configured.
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class ModuleRegistry:
|
|
66
|
-
def __init__(self, container: Container) -> None:
|
|
67
|
-
self.container = container
|
|
68
|
-
|
|
69
|
-
def register(
|
|
70
|
-
self, module: Union[Module, Type[Module], Callable[[Container], None]]
|
|
71
|
-
) -> None:
|
|
72
|
-
"""Register a module as a callable, module type, or module instance.
|
|
73
|
-
|
|
74
|
-
Args:
|
|
75
|
-
module: The module to register.
|
|
76
|
-
"""
|
|
77
|
-
# Callable Module
|
|
78
|
-
if inspect.isfunction(module):
|
|
79
|
-
module(self.container)
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
# Class based Module or Module type
|
|
83
|
-
if inspect.isclass(module) and issubclass(module, Module):
|
|
84
|
-
module = module()
|
|
85
|
-
if isinstance(module, Module):
|
|
86
|
-
module.configure(self.container)
|
|
87
|
-
for provider_name, decorator_args in module.providers:
|
|
88
|
-
obj = getattr(module, provider_name)
|
|
89
|
-
self.container.provider(
|
|
90
|
-
scope=decorator_args.scope,
|
|
91
|
-
override=decorator_args.override,
|
|
92
|
-
)(obj)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
class ProviderDecoratorArgs(NamedTuple):
|
|
96
|
-
scope: Scope
|
|
97
|
-
override: bool
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def provider(
|
|
101
|
-
*, scope: Scope, override: bool = False
|
|
102
|
-
) -> Callable[[Callable[Concatenate[M, P], T]], Callable[Concatenate[M, P], T]]:
|
|
103
|
-
"""Decorator for marking a function or method as a provider in a AnyDI module.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
scope: The scope in which the provided instance should be managed.
|
|
107
|
-
override: Whether the provider should override existing providers
|
|
108
|
-
with the same interface.
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
A decorator that marks the target function or method as a provider.
|
|
112
|
-
"""
|
|
113
|
-
|
|
114
|
-
def decorator(
|
|
115
|
-
target: Callable[Concatenate[M, P], T],
|
|
116
|
-
) -> Callable[Concatenate[M, P], T]:
|
|
117
|
-
setattr(
|
|
118
|
-
target,
|
|
119
|
-
"__provider__",
|
|
120
|
-
ProviderDecoratorArgs(scope=scope, override=override),
|
|
121
|
-
)
|
|
122
|
-
return target
|
|
123
|
-
|
|
124
|
-
return decorator
|
anydi/_scanner.py
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import importlib
|
|
4
|
-
import inspect
|
|
5
|
-
import pkgutil
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from types import ModuleType
|
|
8
|
-
from typing import (
|
|
9
|
-
TYPE_CHECKING,
|
|
10
|
-
Any,
|
|
11
|
-
Callable,
|
|
12
|
-
Iterable,
|
|
13
|
-
List,
|
|
14
|
-
Optional,
|
|
15
|
-
TypeVar,
|
|
16
|
-
Union,
|
|
17
|
-
cast,
|
|
18
|
-
final,
|
|
19
|
-
overload,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
from typing_extensions import NamedTuple, ParamSpec
|
|
23
|
-
|
|
24
|
-
from ._types import Marker
|
|
25
|
-
from ._utils import get_signature
|
|
26
|
-
|
|
27
|
-
if TYPE_CHECKING:
|
|
28
|
-
from ._container import Container
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
T = TypeVar("T")
|
|
32
|
-
P = ParamSpec("P")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@dataclass(frozen=True)
|
|
36
|
-
class Dependency:
|
|
37
|
-
"""Represents a scanned dependency.
|
|
38
|
-
|
|
39
|
-
Attributes:
|
|
40
|
-
member: The member object that represents the dependency.
|
|
41
|
-
module: The module where the dependency is defined.
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
member: Any
|
|
45
|
-
module: ModuleType
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@final
|
|
49
|
-
class Scanner:
|
|
50
|
-
"""A class for scanning packages or modules for decorated objects
|
|
51
|
-
and injecting dependencies."""
|
|
52
|
-
|
|
53
|
-
def __init__(self, container: Container) -> None:
|
|
54
|
-
self.container = container
|
|
55
|
-
|
|
56
|
-
def scan(
|
|
57
|
-
self,
|
|
58
|
-
/,
|
|
59
|
-
packages: Union[Union[ModuleType, str], Iterable[Union[ModuleType, str]]],
|
|
60
|
-
*,
|
|
61
|
-
tags: Optional[Iterable[str]] = None,
|
|
62
|
-
) -> None:
|
|
63
|
-
"""Scan packages or modules for decorated members and inject dependencies.
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
packages: A single package or module to scan,
|
|
67
|
-
or an iterable of packages or modules to scan.
|
|
68
|
-
tags: Optional list of tags to filter the scanned members. Only members
|
|
69
|
-
with at least one matching tag will be scanned. Defaults to None.
|
|
70
|
-
"""
|
|
71
|
-
dependencies: List[Dependency] = []
|
|
72
|
-
|
|
73
|
-
if isinstance(packages, Iterable) and not isinstance(packages, str):
|
|
74
|
-
scan_packages: Iterable[Union[ModuleType, str]] = packages
|
|
75
|
-
else:
|
|
76
|
-
scan_packages = cast(Iterable[Union[ModuleType, str]], [packages])
|
|
77
|
-
|
|
78
|
-
for package in scan_packages:
|
|
79
|
-
dependencies.extend(self._scan_package(package, tags=tags))
|
|
80
|
-
|
|
81
|
-
for dependency in dependencies:
|
|
82
|
-
decorator = self.container.inject()(dependency.member)
|
|
83
|
-
setattr(dependency.module, dependency.member.__name__, decorator)
|
|
84
|
-
|
|
85
|
-
def _scan_package(
|
|
86
|
-
self,
|
|
87
|
-
package: Union[ModuleType, str],
|
|
88
|
-
*,
|
|
89
|
-
tags: Optional[Iterable[str]] = None,
|
|
90
|
-
) -> List[Dependency]:
|
|
91
|
-
"""Scan a package or module for decorated members.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
package: The package or module to scan.
|
|
95
|
-
tags: Optional list of tags to filter the scanned members. Only members
|
|
96
|
-
with at least one matching tag will be scanned. Defaults to None.
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
A list of scanned dependencies.
|
|
100
|
-
"""
|
|
101
|
-
tags = tags or []
|
|
102
|
-
if isinstance(package, str):
|
|
103
|
-
package = importlib.import_module(package)
|
|
104
|
-
|
|
105
|
-
package_path = getattr(package, "__path__", None)
|
|
106
|
-
|
|
107
|
-
if not package_path:
|
|
108
|
-
return self._scan_module(package, tags=tags)
|
|
109
|
-
|
|
110
|
-
dependencies: List[Dependency] = []
|
|
111
|
-
|
|
112
|
-
for module_info in pkgutil.walk_packages(
|
|
113
|
-
path=package_path, prefix=package.__name__ + "."
|
|
114
|
-
):
|
|
115
|
-
module = importlib.import_module(module_info.name)
|
|
116
|
-
dependencies.extend(self._scan_module(module, tags=tags))
|
|
117
|
-
|
|
118
|
-
return dependencies
|
|
119
|
-
|
|
120
|
-
def _scan_module(
|
|
121
|
-
self, module: ModuleType, *, tags: Iterable[str]
|
|
122
|
-
) -> List[Dependency]:
|
|
123
|
-
"""Scan a module for decorated members.
|
|
124
|
-
|
|
125
|
-
Args:
|
|
126
|
-
module: The module to scan.
|
|
127
|
-
tags: List of tags to filter the scanned members. Only members with at
|
|
128
|
-
least one matching tag will be scanned.
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
A list of scanned dependencies.
|
|
132
|
-
"""
|
|
133
|
-
dependencies: List[Dependency] = []
|
|
134
|
-
|
|
135
|
-
for _, member in inspect.getmembers(module):
|
|
136
|
-
if getattr(member, "__module__", None) != module.__name__ or not callable(
|
|
137
|
-
member
|
|
138
|
-
):
|
|
139
|
-
continue
|
|
140
|
-
|
|
141
|
-
decorator_args: InjectDecoratorArgs = getattr(
|
|
142
|
-
member,
|
|
143
|
-
"__injectable__",
|
|
144
|
-
InjectDecoratorArgs(wrapped=False, tags=[]),
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
if tags and (
|
|
148
|
-
decorator_args.tags
|
|
149
|
-
and not set(decorator_args.tags).intersection(tags)
|
|
150
|
-
or not decorator_args.tags
|
|
151
|
-
):
|
|
152
|
-
continue
|
|
153
|
-
|
|
154
|
-
if decorator_args.wrapped:
|
|
155
|
-
dependencies.append(
|
|
156
|
-
self._create_dependency(member=member, module=module)
|
|
157
|
-
)
|
|
158
|
-
continue
|
|
159
|
-
|
|
160
|
-
# Get by Marker
|
|
161
|
-
if inspect.isclass(member):
|
|
162
|
-
signature = get_signature(member.__init__)
|
|
163
|
-
else:
|
|
164
|
-
signature = get_signature(member)
|
|
165
|
-
for parameter in signature.parameters.values():
|
|
166
|
-
if isinstance(parameter.default, Marker):
|
|
167
|
-
dependencies.append(
|
|
168
|
-
self._create_dependency(member=member, module=module)
|
|
169
|
-
)
|
|
170
|
-
continue
|
|
171
|
-
|
|
172
|
-
return dependencies
|
|
173
|
-
|
|
174
|
-
def _create_dependency(self, member: Any, module: ModuleType) -> Dependency:
|
|
175
|
-
"""Create a `Dependency` object from the scanned member and module.
|
|
176
|
-
|
|
177
|
-
Args:
|
|
178
|
-
member: The scanned member.
|
|
179
|
-
module: The module containing the scanned member.
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
A `ScannedDependency` object.
|
|
183
|
-
"""
|
|
184
|
-
if hasattr(member, "__wrapped__"):
|
|
185
|
-
member = member.__wrapped__
|
|
186
|
-
return Dependency(member=member, module=module)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
class InjectDecoratorArgs(NamedTuple):
|
|
190
|
-
wrapped: bool
|
|
191
|
-
tags: Optional[Iterable[str]]
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@overload
|
|
195
|
-
def injectable(obj: Callable[P, T]) -> Callable[P, T]: ...
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
@overload
|
|
199
|
-
def injectable(
|
|
200
|
-
*, tags: Optional[Iterable[str]] = None
|
|
201
|
-
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def injectable(
|
|
205
|
-
obj: Optional[Callable[P, T]] = None,
|
|
206
|
-
tags: Optional[Iterable[str]] = None,
|
|
207
|
-
) -> Union[
|
|
208
|
-
Callable[
|
|
209
|
-
[Callable[P, T]],
|
|
210
|
-
Callable[P, T],
|
|
211
|
-
],
|
|
212
|
-
Callable[P, T],
|
|
213
|
-
]:
|
|
214
|
-
"""Decorator for marking a function or method as requiring dependency injection.
|
|
215
|
-
|
|
216
|
-
Args:
|
|
217
|
-
obj: The target function or method to be decorated.
|
|
218
|
-
tags: Optional tags to associate with the injection point.
|
|
219
|
-
|
|
220
|
-
Returns:
|
|
221
|
-
If `obj` is provided, returns the decorated target function or method.
|
|
222
|
-
If `obj` is not provided, returns a decorator that can be used to mark
|
|
223
|
-
a function or method as requiring dependency injection.
|
|
224
|
-
"""
|
|
225
|
-
|
|
226
|
-
def decorator(obj: Callable[P, T]) -> Callable[P, T]:
|
|
227
|
-
setattr(obj, "__injectable__", InjectDecoratorArgs(wrapped=True, tags=tags))
|
|
228
|
-
return obj
|
|
229
|
-
|
|
230
|
-
if obj is None:
|
|
231
|
-
return decorator
|
|
232
|
-
|
|
233
|
-
return decorator(obj)
|
anydi-0.22.1.dist-info/RECORD
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
anydi/__init__.py,sha256=p2GQq45mulZRlC8oJ7bvOpjVFCeycsgI2IcNJjs_LhU,562
|
|
2
|
-
anydi/_container.py,sha256=1mUCynjqgcQVIC1oSZUszfzxW4jvlaQofULoFcJFZiQ,28219
|
|
3
|
-
anydi/_context.py,sha256=btGJzvTMkj5v95rAw6kjOclISKcSugC4wzrHlWlCk_I,10258
|
|
4
|
-
anydi/_logger.py,sha256=UpubJUnW83kffFxkhUlObm2DmZX1Pjqoz9YFKS-JOPg,52
|
|
5
|
-
anydi/_module.py,sha256=1fBo9-RWxo7TeyP0Y2uJokT-NXP2pjik6CXNoeo3l-8,3712
|
|
6
|
-
anydi/_scanner.py,sha256=YYPqzAQRgxxMpOSOpE7EqA_hccC6VjKi9Y8y_5HbtlA,6781
|
|
7
|
-
anydi/_types.py,sha256=AuHR2hvqaMazL55xj8I9YI6nyBAD16uQznNcmZoQsPE,3240
|
|
8
|
-
anydi/_utils.py,sha256=haRwC6YTTFf6CNTfwsRQ4NZsNByoUkEmE4-rh1-VYLs,3152
|
|
9
|
-
anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
anydi/ext/_utils.py,sha256=upgkXFs3EKYRHX9NktOzuJDPVDglCjdb3B593V-LcU8,1949
|
|
11
|
-
anydi/ext/fastapi.py,sha256=QET3UQLSR2ZNirRuZp1o6eaetJG9yjsFvOuNwTil3Jw,2859
|
|
12
|
-
anydi/ext/pytest_plugin.py,sha256=ste4ItDodvGsvkADShWdEhZapxlRG3vG-S4dmkfP7-I,4028
|
|
13
|
-
anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
anydi/ext/starlette/middleware.py,sha256=Ni0BQaPjs_Ha6zcLZYYJ3-XkslTCnL9aCSa06rnRDMI,1139
|
|
15
|
-
anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
anydi-0.22.1.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
|
|
17
|
-
anydi-0.22.1.dist-info/METADATA,sha256=4duWnwgK4IWCqLkvqGFeRBK-8JgdylC1aEK80p7PpmA,4371
|
|
18
|
-
anydi-0.22.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
19
|
-
anydi-0.22.1.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
|
|
20
|
-
anydi-0.22.1.dist-info/RECORD,,
|
|
File without changes
|