anydi 0.24.2__tar.gz → 0.24.3__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.
- {anydi-0.24.2 → anydi-0.24.3}/PKG-INFO +1 -1
- {anydi-0.24.2 → anydi-0.24.3}/anydi/_container.py +11 -6
- {anydi-0.24.2 → anydi-0.24.3}/anydi/_context.py +2 -2
- {anydi-0.24.2 → anydi-0.24.3}/anydi/_scanner.py +4 -4
- {anydi-0.24.2 → anydi-0.24.3}/anydi/_types.py +6 -4
- anydi-0.24.3/anydi/_utils.py +111 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/ext/fastapi.py +2 -2
- {anydi-0.24.2 → anydi-0.24.3}/pyproject.toml +1 -1
- anydi-0.24.2/anydi/_utils.py +0 -113
- {anydi-0.24.2 → anydi-0.24.3}/LICENSE +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/README.md +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/__init__.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/_logger.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/_module.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/ext/__init__.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/ext/pytest_plugin.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/ext/starlette/__init__.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/ext/starlette/middleware.py +0 -0
- {anydi-0.24.2 → anydi-0.24.3}/anydi/py.typed +0 -0
|
@@ -43,7 +43,12 @@ from ._logger import logger
|
|
|
43
43
|
from ._module import Module, ModuleRegistry
|
|
44
44
|
from ._scanner import Scanner
|
|
45
45
|
from ._types import AnyInterface, Interface, Provider, Scope, is_marker
|
|
46
|
-
from ._utils import
|
|
46
|
+
from ._utils import (
|
|
47
|
+
get_full_qualname,
|
|
48
|
+
get_typed_parameters,
|
|
49
|
+
get_typed_return_annotation,
|
|
50
|
+
is_builtin_type,
|
|
51
|
+
)
|
|
47
52
|
|
|
48
53
|
T = TypeVar("T", bound=Any)
|
|
49
54
|
P = ParamSpec("P")
|
|
@@ -327,7 +332,7 @@ class Container:
|
|
|
327
332
|
"""
|
|
328
333
|
related_providers = []
|
|
329
334
|
|
|
330
|
-
for parameter in provider.parameters
|
|
335
|
+
for parameter in provider.parameters:
|
|
331
336
|
if parameter.annotation is inspect._empty: # noqa
|
|
332
337
|
raise TypeError(
|
|
333
338
|
f"Missing provider `{provider}` "
|
|
@@ -366,7 +371,7 @@ class Container:
|
|
|
366
371
|
The auto scope, or None if the auto scope cannot be detected.
|
|
367
372
|
"""
|
|
368
373
|
has_transient, has_request, has_singleton = False, False, False
|
|
369
|
-
for parameter in
|
|
374
|
+
for parameter in get_typed_parameters(obj):
|
|
370
375
|
sub_provider = self._get_or_register_provider(parameter.annotation)
|
|
371
376
|
if not has_transient and sub_provider.scope == "transient":
|
|
372
377
|
has_transient = True
|
|
@@ -711,9 +716,9 @@ class Container:
|
|
|
711
716
|
Raises:
|
|
712
717
|
TypeError: If the provider return annotation is missing or invalid.
|
|
713
718
|
"""
|
|
714
|
-
annotation =
|
|
719
|
+
annotation = get_typed_return_annotation(obj)
|
|
715
720
|
|
|
716
|
-
if annotation is
|
|
721
|
+
if annotation is None:
|
|
717
722
|
raise TypeError(
|
|
718
723
|
f"Missing `{get_full_qualname(obj)}` provider return annotation."
|
|
719
724
|
)
|
|
@@ -741,7 +746,7 @@ class Container:
|
|
|
741
746
|
of the injected parameters.
|
|
742
747
|
"""
|
|
743
748
|
injected_params = {}
|
|
744
|
-
for parameter in
|
|
749
|
+
for parameter in get_typed_parameters(obj):
|
|
745
750
|
if not is_marker(parameter.default):
|
|
746
751
|
continue
|
|
747
752
|
try:
|
|
@@ -97,7 +97,7 @@ class ScopedContext(abc.ABC):
|
|
|
97
97
|
The arguments for the provider.
|
|
98
98
|
"""
|
|
99
99
|
args, kwargs = [], {}
|
|
100
|
-
for parameter in provider.parameters
|
|
100
|
+
for parameter in provider.parameters:
|
|
101
101
|
instance = self.container.resolve(parameter.annotation)
|
|
102
102
|
if parameter.kind == parameter.POSITIONAL_ONLY:
|
|
103
103
|
args.append(instance)
|
|
@@ -117,7 +117,7 @@ class ScopedContext(abc.ABC):
|
|
|
117
117
|
The arguments for the provider.
|
|
118
118
|
"""
|
|
119
119
|
args, kwargs = [], {}
|
|
120
|
-
for parameter in provider.parameters
|
|
120
|
+
for parameter in provider.parameters:
|
|
121
121
|
instance = await self.container.aresolve(parameter.annotation)
|
|
122
122
|
if parameter.kind == parameter.POSITIONAL_ONLY:
|
|
123
123
|
args.append(instance)
|
|
@@ -20,7 +20,7 @@ from typing import (
|
|
|
20
20
|
from typing_extensions import NamedTuple, ParamSpec
|
|
21
21
|
|
|
22
22
|
from ._types import is_marker
|
|
23
|
-
from ._utils import
|
|
23
|
+
from ._utils import get_typed_parameters
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
26
|
from ._container import Container
|
|
@@ -157,10 +157,10 @@ class Scanner:
|
|
|
157
157
|
|
|
158
158
|
# Get by Marker
|
|
159
159
|
if inspect.isclass(member):
|
|
160
|
-
|
|
160
|
+
parameters = get_typed_parameters(member.__init__)
|
|
161
161
|
else:
|
|
162
|
-
|
|
163
|
-
for parameter in
|
|
162
|
+
parameters = get_typed_parameters(member)
|
|
163
|
+
for parameter in parameters:
|
|
164
164
|
if is_marker(parameter.default):
|
|
165
165
|
dependencies.append(
|
|
166
166
|
self._create_dependency(member=member, module=module)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import inspect
|
|
2
4
|
from dataclasses import dataclass
|
|
3
5
|
from functools import cached_property
|
|
4
|
-
from typing import Any, Callable,
|
|
6
|
+
from typing import Any, Callable, Type, TypeVar, Union
|
|
5
7
|
|
|
6
8
|
from typing_extensions import Annotated, Literal, Self, TypeAlias
|
|
7
9
|
|
|
8
|
-
from ._utils import get_full_qualname,
|
|
10
|
+
from ._utils import get_full_qualname, get_typed_parameters
|
|
9
11
|
|
|
10
12
|
Scope = Literal["transient", "singleton", "request"]
|
|
11
13
|
|
|
@@ -58,13 +60,13 @@ class Provider:
|
|
|
58
60
|
return get_full_qualname(self.obj)
|
|
59
61
|
|
|
60
62
|
@cached_property
|
|
61
|
-
def parameters(self) ->
|
|
63
|
+
def parameters(self) -> list[inspect.Parameter]:
|
|
62
64
|
"""Returns the parameters of the provider as a mapping.
|
|
63
65
|
|
|
64
66
|
Returns:
|
|
65
67
|
The parameters of the provider.
|
|
66
68
|
"""
|
|
67
|
-
return
|
|
69
|
+
return get_typed_parameters(self.obj)
|
|
68
70
|
|
|
69
71
|
@cached_property
|
|
70
72
|
def is_class(self) -> bool:
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Shared AnyDI utils module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import builtins
|
|
6
|
+
import functools
|
|
7
|
+
import inspect
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any, Callable, ForwardRef, TypeVar, cast
|
|
10
|
+
|
|
11
|
+
from typing_extensions import Annotated, ParamSpec, get_origin
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import anyio # noqa
|
|
15
|
+
except ImportError:
|
|
16
|
+
anyio = None # type: ignore[assignment]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if sys.version_info < (3, 9): # pragma: nocover
|
|
20
|
+
|
|
21
|
+
def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
|
|
22
|
+
return type_._evaluate(globalns, localns) # noqa
|
|
23
|
+
|
|
24
|
+
else:
|
|
25
|
+
|
|
26
|
+
def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
|
|
27
|
+
return cast(Any, type_)._evaluate(globalns, localns, set()) # noqa
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
T = TypeVar("T")
|
|
31
|
+
P = ParamSpec("P")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_full_qualname(obj: Any) -> str:
|
|
35
|
+
"""Get the fully qualified name of an object."""
|
|
36
|
+
origin = get_origin(obj)
|
|
37
|
+
if origin is Annotated:
|
|
38
|
+
metadata = ", ".join(
|
|
39
|
+
[
|
|
40
|
+
f'"{arg}"' if isinstance(arg, str) else str(arg)
|
|
41
|
+
for arg in obj.__metadata__
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
|
+
return f"Annotated[{get_full_qualname(obj.__args__[0])}, {metadata}]]"
|
|
45
|
+
|
|
46
|
+
qualname = getattr(obj, "__qualname__", None)
|
|
47
|
+
module_name = getattr(obj, "__module__", None)
|
|
48
|
+
if qualname is None:
|
|
49
|
+
qualname = type(obj).__qualname__
|
|
50
|
+
|
|
51
|
+
if module_name is None:
|
|
52
|
+
module_name = type(obj).__module__
|
|
53
|
+
|
|
54
|
+
if module_name == builtins.__name__:
|
|
55
|
+
return qualname
|
|
56
|
+
return f"{module_name}.{qualname}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def is_builtin_type(tp: type[Any]) -> bool:
|
|
60
|
+
"""Check if the given type is a built-in type."""
|
|
61
|
+
return tp.__module__ == builtins.__name__
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def make_forwardref(annotation: str, globalns: dict[str, Any]) -> Any:
|
|
65
|
+
"""Create a forward reference from a string annotation."""
|
|
66
|
+
forward_ref = ForwardRef(annotation)
|
|
67
|
+
return evaluate_forwardref(forward_ref, globalns, globalns)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
|
|
71
|
+
"""Get the typed annotation of a parameter."""
|
|
72
|
+
if isinstance(annotation, str):
|
|
73
|
+
annotation = ForwardRef(annotation)
|
|
74
|
+
annotation = evaluate_forwardref(annotation, globalns, globalns)
|
|
75
|
+
return annotation
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_typed_return_annotation(obj: Callable[..., Any]) -> Any:
|
|
79
|
+
"""Get the typed return annotation of a callable object."""
|
|
80
|
+
signature = inspect.signature(obj)
|
|
81
|
+
annotation = signature.return_annotation
|
|
82
|
+
if annotation is inspect.Signature.empty:
|
|
83
|
+
return None
|
|
84
|
+
globalns = getattr(obj, "__globals__", {})
|
|
85
|
+
return get_typed_annotation(annotation, globalns)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
|
|
89
|
+
"""Get the typed parameters of a callable object."""
|
|
90
|
+
globalns = getattr(obj, "__globals__", {})
|
|
91
|
+
return [
|
|
92
|
+
parameter.replace(
|
|
93
|
+
annotation=get_typed_annotation(parameter.annotation, globalns)
|
|
94
|
+
)
|
|
95
|
+
for name, parameter in inspect.signature(obj).parameters.items()
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def run_async(
|
|
100
|
+
func: Callable[P, T],
|
|
101
|
+
/,
|
|
102
|
+
*args: P.args,
|
|
103
|
+
**kwargs: P.kwargs,
|
|
104
|
+
) -> T:
|
|
105
|
+
"""Runs the given function asynchronously using the `anyio` library."""
|
|
106
|
+
if not anyio:
|
|
107
|
+
raise ImportError(
|
|
108
|
+
"`anyio` library is not currently installed. Please make sure to install "
|
|
109
|
+
"it first, or consider using `anydi[full]` instead."
|
|
110
|
+
)
|
|
111
|
+
return await anyio.to_thread.run_sync(functools.partial(func, *args, **kwargs))
|
|
@@ -13,7 +13,7 @@ from starlette.requests import Request
|
|
|
13
13
|
from typing_extensions import Annotated, get_args, get_origin
|
|
14
14
|
|
|
15
15
|
from anydi import Container
|
|
16
|
-
from anydi._utils import get_full_qualname,
|
|
16
|
+
from anydi._utils import get_full_qualname, get_typed_parameters
|
|
17
17
|
|
|
18
18
|
from .starlette.middleware import RequestScopedMiddleware
|
|
19
19
|
|
|
@@ -47,7 +47,7 @@ def install(app: FastAPI, container: Container) -> None:
|
|
|
47
47
|
call, *params = dependant.cache_key
|
|
48
48
|
if not call:
|
|
49
49
|
continue # pragma: no cover
|
|
50
|
-
for parameter in
|
|
50
|
+
for parameter in get_typed_parameters(call):
|
|
51
51
|
_patch_route_parameter(call, parameter, container)
|
|
52
52
|
|
|
53
53
|
|
anydi-0.24.2/anydi/_utils.py
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"""Shared AnyDI utils module."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import builtins
|
|
6
|
-
import functools
|
|
7
|
-
import inspect
|
|
8
|
-
import sys
|
|
9
|
-
from typing import Any, Callable, TypeVar
|
|
10
|
-
|
|
11
|
-
from typing_extensions import Annotated, ParamSpec, get_origin
|
|
12
|
-
|
|
13
|
-
try:
|
|
14
|
-
import anyio # noqa
|
|
15
|
-
except ImportError:
|
|
16
|
-
anyio = None # type: ignore[assignment]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
T = TypeVar("T")
|
|
20
|
-
P = ParamSpec("P")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def get_full_qualname(obj: Any) -> str:
|
|
24
|
-
"""Get the fully qualified name of an object.
|
|
25
|
-
|
|
26
|
-
This function returns the fully qualified name of the given object,
|
|
27
|
-
which includes both the module name and the object's qualname.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
obj: The object for which to retrieve the fully qualified name.
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
The fully qualified name of the object.
|
|
34
|
-
"""
|
|
35
|
-
origin = get_origin(obj)
|
|
36
|
-
if origin is Annotated:
|
|
37
|
-
metadata = ", ".join(
|
|
38
|
-
[
|
|
39
|
-
f'"{arg}"' if isinstance(arg, str) else str(arg)
|
|
40
|
-
for arg in obj.__metadata__
|
|
41
|
-
]
|
|
42
|
-
)
|
|
43
|
-
return f"Annotated[{get_full_qualname(obj.__args__[0])}, {metadata}]]"
|
|
44
|
-
|
|
45
|
-
qualname = getattr(obj, "__qualname__", None)
|
|
46
|
-
module_name = getattr(obj, "__module__", None)
|
|
47
|
-
if qualname is None:
|
|
48
|
-
qualname = type(obj).__qualname__
|
|
49
|
-
|
|
50
|
-
if module_name is None:
|
|
51
|
-
module_name = type(obj).__module__
|
|
52
|
-
|
|
53
|
-
if module_name == builtins.__name__:
|
|
54
|
-
return qualname
|
|
55
|
-
return f"{module_name}.{qualname}"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def is_builtin_type(tp: type[Any]) -> bool:
|
|
59
|
-
"""
|
|
60
|
-
Check if the given type is a built-in type.
|
|
61
|
-
Args:
|
|
62
|
-
tp (type): The type to check.
|
|
63
|
-
Returns:
|
|
64
|
-
bool: True if the type is a built-in type, False otherwise.
|
|
65
|
-
"""
|
|
66
|
-
return tp.__module__ == builtins.__name__
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@functools.lru_cache(maxsize=None)
|
|
70
|
-
def get_signature(obj: Callable[..., Any]) -> inspect.Signature:
|
|
71
|
-
"""Get the signature of a callable object.
|
|
72
|
-
|
|
73
|
-
This function uses the `inspect.signature` function to retrieve the signature
|
|
74
|
-
of the given callable object. It applies an LRU cache decorator to improve
|
|
75
|
-
performance by caching the signatures of previously inspected objects.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
obj: The callable object to inspect.
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
The signature of the callable object.
|
|
82
|
-
"""
|
|
83
|
-
signature_kwargs: dict[str, Any] = {}
|
|
84
|
-
if sys.version_info >= (3, 10):
|
|
85
|
-
signature_kwargs["eval_str"] = True
|
|
86
|
-
return inspect.signature(obj, **signature_kwargs)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
async def run_async(
|
|
90
|
-
func: Callable[P, T],
|
|
91
|
-
/,
|
|
92
|
-
*args: P.args,
|
|
93
|
-
**kwargs: P.kwargs,
|
|
94
|
-
) -> T:
|
|
95
|
-
"""Runs the given function asynchronously using the `anyio` library.
|
|
96
|
-
|
|
97
|
-
Args:
|
|
98
|
-
func: The function to run asynchronously.
|
|
99
|
-
args: The positional arguments to pass to the function.
|
|
100
|
-
kwargs: The keyword arguments to pass to the function.
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
The result of the function.
|
|
104
|
-
|
|
105
|
-
Raises:
|
|
106
|
-
ImportError: If the `anyio` library is not installed.
|
|
107
|
-
"""
|
|
108
|
-
if not anyio:
|
|
109
|
-
raise ImportError(
|
|
110
|
-
"`anyio` library is not currently installed. Please make sure to install "
|
|
111
|
-
"it first, or consider using `anydi[full]` instead."
|
|
112
|
-
)
|
|
113
|
-
return await anyio.to_thread.run_sync(functools.partial(func, *args, **kwargs))
|
|
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
|