anydi 0.25.0a2__tar.gz → 0.25.2__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.25.0a2 → anydi-0.25.2}/PKG-INFO +1 -1
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_container.py +4 -1
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_utils.py +51 -28
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/_settings.py +1 -1
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/_utils.py +4 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/apps.py +5 -2
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/ninja/_signature.py +2 -2
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/pytest_plugin.py +4 -3
- {anydi-0.25.0a2 → anydi-0.25.2}/pyproject.toml +1 -1
- {anydi-0.25.0a2 → anydi-0.25.2}/LICENSE +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/README.md +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/__init__.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_context.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_logger.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_module.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_scanner.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/_types.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/__init__.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/__init__.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/_container.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/middleware.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/ninja/__init__.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/django/ninja/_operation.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/fastapi.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/starlette/__init__.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/ext/starlette/middleware.py +0 -0
- {anydi-0.25.0a2 → anydi-0.25.2}/anydi/py.typed +0 -0
|
@@ -47,6 +47,7 @@ from ._utils import (
|
|
|
47
47
|
get_full_qualname,
|
|
48
48
|
get_typed_parameters,
|
|
49
49
|
get_typed_return_annotation,
|
|
50
|
+
has_resource_origin,
|
|
50
51
|
is_builtin_type,
|
|
51
52
|
)
|
|
52
53
|
|
|
@@ -723,7 +724,9 @@ class Container:
|
|
|
723
724
|
f"Missing `{get_full_qualname(obj)}` provider return annotation."
|
|
724
725
|
)
|
|
725
726
|
|
|
726
|
-
|
|
727
|
+
origin = get_origin(annotation)
|
|
728
|
+
|
|
729
|
+
if has_resource_origin(origin):
|
|
727
730
|
args = get_args(annotation)
|
|
728
731
|
if args:
|
|
729
732
|
return args[0]
|
|
@@ -6,9 +6,9 @@ import builtins
|
|
|
6
6
|
import functools
|
|
7
7
|
import inspect
|
|
8
8
|
import sys
|
|
9
|
-
from typing import Any, Callable, ForwardRef, TypeVar, cast
|
|
9
|
+
from typing import Any, AsyncIterator, Callable, ForwardRef, Iterator, TypeVar, cast
|
|
10
10
|
|
|
11
|
-
from typing_extensions import
|
|
11
|
+
from typing_extensions import ParamSpec, get_args, get_origin
|
|
12
12
|
|
|
13
13
|
try:
|
|
14
14
|
import anyio # noqa
|
|
@@ -33,27 +33,28 @@ P = ParamSpec("P")
|
|
|
33
33
|
|
|
34
34
|
def get_full_qualname(obj: Any) -> str:
|
|
35
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
36
|
qualname = getattr(obj, "__qualname__", None)
|
|
47
|
-
|
|
37
|
+
module = getattr(obj, "__module__", None)
|
|
38
|
+
|
|
48
39
|
if qualname is None:
|
|
49
40
|
qualname = type(obj).__qualname__
|
|
50
41
|
|
|
51
|
-
if
|
|
52
|
-
|
|
42
|
+
if module is None:
|
|
43
|
+
module = type(obj).__module__
|
|
53
44
|
|
|
54
|
-
if
|
|
45
|
+
if module == builtins.__name__:
|
|
55
46
|
return qualname
|
|
56
|
-
|
|
47
|
+
|
|
48
|
+
origin = get_origin(obj)
|
|
49
|
+
|
|
50
|
+
if origin:
|
|
51
|
+
args = ", ".join(
|
|
52
|
+
get_full_qualname(arg) if not isinstance(arg, str) else f'"{arg}"'
|
|
53
|
+
for arg in get_args(obj)
|
|
54
|
+
)
|
|
55
|
+
return f"{get_full_qualname(origin)}[{args}]"
|
|
56
|
+
|
|
57
|
+
return f"{module}.{qualname}"
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
def is_builtin_type(tp: type[Any]) -> bool:
|
|
@@ -61,17 +62,19 @@ def is_builtin_type(tp: type[Any]) -> bool:
|
|
|
61
62
|
return tp.__module__ == builtins.__name__
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
|
|
65
|
+
def get_typed_annotation(
|
|
66
|
+
annotation: Any,
|
|
67
|
+
globalns: dict[str, Any],
|
|
68
|
+
module: Any = None,
|
|
69
|
+
is_class: bool = False,
|
|
70
|
+
) -> Any:
|
|
71
71
|
"""Get the typed annotation of a parameter."""
|
|
72
72
|
if isinstance(annotation, str):
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
if sys.version_info < (3, 9):
|
|
74
|
+
annotation = ForwardRef(annotation)
|
|
75
|
+
else:
|
|
76
|
+
annotation = ForwardRef(annotation, module=module, is_class=is_class)
|
|
77
|
+
annotation = evaluate_forwardref(annotation, globalns, {})
|
|
75
78
|
return annotation
|
|
76
79
|
|
|
77
80
|
|
|
@@ -82,20 +85,40 @@ def get_typed_return_annotation(obj: Callable[..., Any]) -> Any:
|
|
|
82
85
|
if annotation is inspect.Signature.empty:
|
|
83
86
|
return None
|
|
84
87
|
globalns = getattr(obj, "__globals__", {})
|
|
85
|
-
|
|
88
|
+
module = getattr(obj, "__module__", None)
|
|
89
|
+
is_class = inspect.isclass(obj)
|
|
90
|
+
return get_typed_annotation(annotation, globalns, module=module, is_class=is_class)
|
|
86
91
|
|
|
87
92
|
|
|
88
93
|
def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
|
|
89
94
|
"""Get the typed parameters of a callable object."""
|
|
90
95
|
globalns = getattr(obj, "__globals__", {})
|
|
96
|
+
module = getattr(obj, "__module__", None)
|
|
97
|
+
is_class = inspect.isclass(obj)
|
|
91
98
|
return [
|
|
92
99
|
parameter.replace(
|
|
93
|
-
annotation=get_typed_annotation(
|
|
100
|
+
annotation=get_typed_annotation(
|
|
101
|
+
parameter.annotation,
|
|
102
|
+
globalns,
|
|
103
|
+
module=module,
|
|
104
|
+
is_class=is_class,
|
|
105
|
+
)
|
|
94
106
|
)
|
|
95
107
|
for name, parameter in inspect.signature(obj).parameters.items()
|
|
96
108
|
]
|
|
97
109
|
|
|
98
110
|
|
|
111
|
+
_resource_origins = (
|
|
112
|
+
get_origin(Iterator),
|
|
113
|
+
get_origin(AsyncIterator),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def has_resource_origin(origin: Any) -> bool:
|
|
118
|
+
"""Check if the given origin is a resource origin."""
|
|
119
|
+
return origin in _resource_origins
|
|
120
|
+
|
|
121
|
+
|
|
99
122
|
async def run_async(
|
|
100
123
|
func: Callable[P, T],
|
|
101
124
|
/,
|
|
@@ -94,10 +94,14 @@ def inject_urlpatterns(container: anydi.Container, *, urlconf: str) -> None:
|
|
|
94
94
|
"""Auto-inject the container into views."""
|
|
95
95
|
resolver = get_resolver(urlconf)
|
|
96
96
|
for pattern in iter_urlpatterns(resolver.url_patterns):
|
|
97
|
+
# Skip already injected views
|
|
98
|
+
if hasattr(pattern.callback, "_injected"):
|
|
99
|
+
continue
|
|
97
100
|
# Skip django-ninja views
|
|
98
101
|
if pattern.lookup_str.startswith("ninja."):
|
|
99
102
|
continue # pragma: no cover
|
|
100
103
|
pattern.callback = container.inject(pattern.callback)
|
|
104
|
+
pattern.callback._injected = True # type: ignore[attr-defined]
|
|
101
105
|
|
|
102
106
|
|
|
103
107
|
def iter_urlpatterns(
|
|
@@ -33,7 +33,7 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
|
|
|
33
33
|
)
|
|
34
34
|
except ImportError as exc:
|
|
35
35
|
raise ImproperlyConfigured(
|
|
36
|
-
f"Cannot import container
|
|
36
|
+
f"Cannot import container factory '{container_factory_path}'."
|
|
37
37
|
) from exc
|
|
38
38
|
self.container = container_factory()
|
|
39
39
|
else:
|
|
@@ -75,7 +75,10 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
|
|
|
75
75
|
|
|
76
76
|
# Auto-injecting the container into views
|
|
77
77
|
if urlconf := self.settings["INJECT_URLCONF"]:
|
|
78
|
-
|
|
78
|
+
if isinstance(urlconf, str):
|
|
79
|
+
urlconf = [urlconf]
|
|
80
|
+
for u in urlconf:
|
|
81
|
+
inject_urlpatterns(self.container, urlconf=u)
|
|
79
82
|
|
|
80
83
|
# Scan packages
|
|
81
84
|
for scan_package in self.settings["SCAN_PACKAGES"]:
|
|
@@ -11,7 +11,7 @@ from ninja.signature.details import (
|
|
|
11
11
|
)
|
|
12
12
|
from ninja.signature.utils import get_path_param_names, get_typed_signature
|
|
13
13
|
|
|
14
|
-
from anydi._types import
|
|
14
|
+
from anydi._types import is_marker # noqa
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class ViewSignature(BaseViewSignature):
|
|
@@ -46,7 +46,7 @@ class ViewSignature(BaseViewSignature):
|
|
|
46
46
|
continue
|
|
47
47
|
|
|
48
48
|
# Skip default values that are anydi dependency markers
|
|
49
|
-
if
|
|
49
|
+
if is_marker(arg.default):
|
|
50
50
|
self.dependencies.append((name, arg.annotation))
|
|
51
51
|
continue
|
|
52
52
|
|
|
@@ -6,6 +6,7 @@ from typing import Any, Callable, Iterator, cast
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
8
|
from anydi import Container
|
|
9
|
+
from anydi._utils import get_typed_parameters
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def pytest_configure(config: pytest.Config) -> None:
|
|
@@ -63,14 +64,14 @@ def _anydi_injected_parameter_iterator(
|
|
|
63
64
|
_anydi_unresolved: list[str],
|
|
64
65
|
) -> Callable[[], Iterator[tuple[str, Any]]]:
|
|
65
66
|
def _iterator() -> Iterator[tuple[str, inspect.Parameter]]:
|
|
66
|
-
for
|
|
67
|
+
for parameter in get_typed_parameters(request.function):
|
|
67
68
|
if (
|
|
68
69
|
((interface := parameter.annotation) is parameter.empty)
|
|
69
70
|
or interface in _anydi_unresolved
|
|
70
|
-
or name in request.node.funcargs
|
|
71
|
+
or parameter.name in request.node.funcargs
|
|
71
72
|
):
|
|
72
73
|
continue
|
|
73
|
-
yield name, interface
|
|
74
|
+
yield parameter.name, interface
|
|
74
75
|
|
|
75
76
|
return _iterator
|
|
76
77
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|