anydi 0.42.0__tar.gz → 0.43.0__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.42.0 → anydi-0.43.0}/PKG-INFO +1 -1
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_container.py +7 -10
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_decorators.py +49 -7
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_module.py +4 -3
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_scan.py +25 -25
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/_utils.py +1 -41
- {anydi-0.42.0 → anydi-0.43.0}/pyproject.toml +3 -2
- {anydi-0.42.0 → anydi-0.43.0}/tests/test_container.py +31 -47
- {anydi-0.42.0 → anydi-0.43.0}/tests/test_decorators.py +21 -11
- anydi-0.43.0/tests/test_scan.py +68 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/test_testing.py +3 -5
- {anydi-0.42.0 → anydi-0.43.0}/uv.lock +15 -1
- anydi-0.42.0/tests/test_scan.py +0 -41
- {anydi-0.42.0 → anydi-0.43.0}/.editorconfig +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/.github/workflows/ci.yml +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/.gitignore +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/.readthedocs.yaml +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/LICENSE +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/Makefile +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/README.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_async.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_context.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_provider.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_scope.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/_typing.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/_utils.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/_container.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/_settings.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/apps.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/middleware.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/ninja/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/ninja/_operation.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/django/ninja/_signature.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/fastapi.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/faststream.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/pydantic_settings.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/pytest_plugin.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/starlette/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/ext/starlette/middleware.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/py.typed +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/anydi/testing.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/examples/basic.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/extensions/django.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/extensions/fastapi.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/extensions/faststream.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/extensions/pydantic_settings.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/index.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/docs/usage.md +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/mkdocs.yml +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/override.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/conftest.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/api/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/api/router.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/api/test_router.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/api/urls.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/conftest.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/container.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/scan/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/services.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/settings.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/test_views.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/urls.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/django/views.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fastapi/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fastapi/app.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fastapi/conftest.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fastapi/test_auto_register.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fastapi/test_ext.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fastapi/test_routes.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/faststream/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/faststream/test_ext.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/faststream/test_subscribers.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/fixtures.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/starlette/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/starlette/app.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/starlette/conftest.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/starlette/test_routes.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/test_pydantic.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/ext/test_pytest_plugin.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/fixtures.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a1/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a1/handlers.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a2/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a2/a21/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a2/a21/handlers.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a3/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/a/a3/handlers.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/b/__init__.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/scan_app/b/handlers.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/test_module.py +0 -0
- {anydi-0.42.0 → anydi-0.43.0}/tests/test_utils.py +0 -0
|
@@ -11,12 +11,13 @@ import uuid
|
|
|
11
11
|
from collections import defaultdict
|
|
12
12
|
from collections.abc import AsyncIterator, Iterable, Iterator
|
|
13
13
|
from contextvars import ContextVar
|
|
14
|
-
from typing import
|
|
14
|
+
from typing import Any, Callable, TypeVar, cast, overload
|
|
15
15
|
|
|
16
16
|
from typing_extensions import ParamSpec, Self, get_args, get_origin
|
|
17
17
|
|
|
18
18
|
from ._async import run_sync
|
|
19
19
|
from ._context import InstanceContext
|
|
20
|
+
from ._decorators import is_provided
|
|
20
21
|
from ._module import ModuleDef, ModuleRegistrar
|
|
21
22
|
from ._provider import (
|
|
22
23
|
Provider,
|
|
@@ -441,17 +442,13 @@ class Container:
|
|
|
441
442
|
except LookupError:
|
|
442
443
|
if self.strict or interface is inspect.Parameter.empty:
|
|
443
444
|
raise
|
|
444
|
-
if
|
|
445
|
-
call = args[0]
|
|
446
|
-
else:
|
|
447
|
-
call = interface
|
|
448
|
-
if inspect.isclass(call) and not is_builtin_type(call):
|
|
445
|
+
if inspect.isclass(interface) and not is_builtin_type(interface):
|
|
449
446
|
# Try to get defined scope
|
|
450
|
-
if
|
|
451
|
-
scope =
|
|
447
|
+
if is_provided(interface):
|
|
448
|
+
scope = interface.__provided__["scope"]
|
|
452
449
|
else:
|
|
453
450
|
scope = parent_scope
|
|
454
|
-
return self._register_provider(
|
|
451
|
+
return self._register_provider(interface, scope, interface, **defaults)
|
|
455
452
|
raise
|
|
456
453
|
|
|
457
454
|
def _set_provider(self, provider: Provider) -> None:
|
|
@@ -793,8 +790,8 @@ class Container:
|
|
|
793
790
|
return cast(Callable[P, T], self._inject_cache[call])
|
|
794
791
|
|
|
795
792
|
injected_params = self._get_injected_params(call)
|
|
796
|
-
|
|
797
793
|
if not injected_params:
|
|
794
|
+
self._inject_cache[call] = call
|
|
798
795
|
return call
|
|
799
796
|
|
|
800
797
|
if inspect.iscoroutinefunction(call):
|
|
@@ -1,22 +1,41 @@
|
|
|
1
1
|
from collections.abc import Iterable
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import (
|
|
3
|
+
TYPE_CHECKING,
|
|
4
|
+
Any,
|
|
5
|
+
Callable,
|
|
6
|
+
Concatenate,
|
|
7
|
+
ParamSpec,
|
|
8
|
+
Protocol,
|
|
9
|
+
TypedDict,
|
|
10
|
+
TypeGuard,
|
|
11
|
+
TypeVar,
|
|
12
|
+
overload,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from ._module import Module
|
|
17
|
+
|
|
3
18
|
|
|
4
|
-
from ._module import Module
|
|
5
19
|
from ._scope import Scope
|
|
6
20
|
|
|
7
21
|
T = TypeVar("T")
|
|
8
22
|
P = ParamSpec("P")
|
|
9
23
|
|
|
10
24
|
ClassT = TypeVar("ClassT", bound=type)
|
|
11
|
-
ModuleT = TypeVar("ModuleT", bound=Module)
|
|
25
|
+
ModuleT = TypeVar("ModuleT", bound="Module")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ProvidedMetadata(TypedDict):
|
|
29
|
+
"""Metadata for classes marked as provided by AnyDI."""
|
|
30
|
+
|
|
31
|
+
scope: Scope
|
|
12
32
|
|
|
13
33
|
|
|
14
34
|
def provided(*, scope: Scope) -> Callable[[ClassT], ClassT]:
|
|
15
35
|
"""Decorator for marking a class as provided by AnyDI with a specific scope."""
|
|
16
36
|
|
|
17
37
|
def decorator(cls: ClassT) -> ClassT:
|
|
18
|
-
cls.__provided__ =
|
|
19
|
-
cls.__scope__ = scope
|
|
38
|
+
cls.__provided__ = ProvidedMetadata(scope=scope)
|
|
20
39
|
return cls
|
|
21
40
|
|
|
22
41
|
return decorator
|
|
@@ -28,6 +47,14 @@ request = provided(scope="request")
|
|
|
28
47
|
singleton = provided(scope="singleton")
|
|
29
48
|
|
|
30
49
|
|
|
50
|
+
class Provided(Protocol):
|
|
51
|
+
__provided__: ProvidedMetadata
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def is_provided(cls: Any) -> TypeGuard[type[Provided]]:
|
|
55
|
+
return hasattr(cls, "__provided__")
|
|
56
|
+
|
|
57
|
+
|
|
31
58
|
class ProviderMetadata(TypedDict):
|
|
32
59
|
scope: Scope
|
|
33
60
|
override: bool
|
|
@@ -49,8 +76,15 @@ def provider(
|
|
|
49
76
|
return decorator
|
|
50
77
|
|
|
51
78
|
|
|
79
|
+
class Provider(Protocol):
|
|
80
|
+
__provider__: ProviderMetadata
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_provider(obj: Callable[..., Any]) -> TypeGuard[Provider]:
|
|
84
|
+
return hasattr(obj, "__provider__")
|
|
85
|
+
|
|
86
|
+
|
|
52
87
|
class InjectableMetadata(TypedDict):
|
|
53
|
-
wrapped: bool
|
|
54
88
|
tags: Iterable[str] | None
|
|
55
89
|
|
|
56
90
|
|
|
@@ -71,10 +105,18 @@ def injectable(
|
|
|
71
105
|
"""Decorator for marking a function or method as requiring dependency injection."""
|
|
72
106
|
|
|
73
107
|
def decorator(inner: Callable[P, T]) -> Callable[P, T]:
|
|
74
|
-
inner.__injectable__ = InjectableMetadata(
|
|
108
|
+
inner.__injectable__ = InjectableMetadata(tags=tags) # type: ignore
|
|
75
109
|
return inner
|
|
76
110
|
|
|
77
111
|
if func is None:
|
|
78
112
|
return decorator
|
|
79
113
|
|
|
80
114
|
return decorator(func)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Injectable(Protocol):
|
|
118
|
+
__injectable__: InjectableMetadata
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def is_injectable(obj: Callable[..., Any]) -> TypeGuard[Injectable]:
|
|
122
|
+
return hasattr(obj, "__injectable__")
|
|
@@ -4,9 +4,10 @@ import importlib
|
|
|
4
4
|
import inspect
|
|
5
5
|
from typing import TYPE_CHECKING, Any, Callable
|
|
6
6
|
|
|
7
|
+
from ._decorators import ProviderMetadata, is_provider
|
|
8
|
+
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
from ._container import Container
|
|
9
|
-
from ._decorators import ProviderMetadata
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class ModuleMeta(type):
|
|
@@ -14,9 +15,9 @@ class ModuleMeta(type):
|
|
|
14
15
|
|
|
15
16
|
def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> Any:
|
|
16
17
|
attrs["providers"] = [
|
|
17
|
-
(name,
|
|
18
|
+
(name, value.__provider__)
|
|
18
19
|
for name, value in attrs.items()
|
|
19
|
-
if
|
|
20
|
+
if is_provider(value)
|
|
20
21
|
]
|
|
21
22
|
return super().__new__(cls, name, bases, attrs)
|
|
22
23
|
|
|
@@ -6,9 +6,9 @@ import pkgutil
|
|
|
6
6
|
from collections.abc import Iterable
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from types import ModuleType
|
|
9
|
-
from typing import TYPE_CHECKING, Any, Union
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable, Union
|
|
10
10
|
|
|
11
|
-
from ._decorators import
|
|
11
|
+
from ._decorators import is_injectable
|
|
12
12
|
from ._typing import get_typed_parameters, is_marker
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
@@ -73,9 +73,8 @@ class Scanner:
|
|
|
73
73
|
|
|
74
74
|
return dependencies
|
|
75
75
|
|
|
76
|
-
@staticmethod
|
|
77
76
|
def _scan_module(
|
|
78
|
-
module: ModuleType, *, tags: Iterable[str]
|
|
77
|
+
self, module: ModuleType, *, tags: Iterable[str]
|
|
79
78
|
) -> list[ScannedDependency]:
|
|
80
79
|
"""Scan a module for decorated members."""
|
|
81
80
|
dependencies: list[ScannedDependency] = []
|
|
@@ -84,27 +83,28 @@ class Scanner:
|
|
|
84
83
|
if getattr(member, "__module__", None) != module.__name__:
|
|
85
84
|
continue
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
member,
|
|
89
|
-
"__injectable__",
|
|
90
|
-
InjectableMetadata(wrapped=False, tags=[]),
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
should_include = False
|
|
94
|
-
if metadata["wrapped"]:
|
|
95
|
-
should_include = True
|
|
96
|
-
elif tags and metadata["tags"]:
|
|
97
|
-
should_include = bool(set(metadata["tags"]) & set(tags))
|
|
98
|
-
elif tags and not metadata["tags"]:
|
|
99
|
-
continue # tags are provided but member has none
|
|
100
|
-
|
|
101
|
-
if not should_include:
|
|
102
|
-
for param in get_typed_parameters(member):
|
|
103
|
-
if is_marker(param.default):
|
|
104
|
-
should_include = True
|
|
105
|
-
break
|
|
106
|
-
|
|
107
|
-
if should_include:
|
|
86
|
+
if self._should_include_member(member, tags=tags):
|
|
108
87
|
dependencies.append(ScannedDependency(member=member, module=module))
|
|
109
88
|
|
|
110
89
|
return dependencies
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def _should_include_member(
|
|
93
|
+
member: Callable[..., Any], *, tags: Iterable[str]
|
|
94
|
+
) -> bool:
|
|
95
|
+
"""Determine if a member should be included based on tags or marker defaults."""
|
|
96
|
+
|
|
97
|
+
if is_injectable(member):
|
|
98
|
+
member_tags = set(member.__injectable__["tags"] or [])
|
|
99
|
+
if tags:
|
|
100
|
+
return bool(set(tags) & member_tags)
|
|
101
|
+
return True # No tags passed → include all injectables
|
|
102
|
+
|
|
103
|
+
# If no tags are passed and not explicitly injectable,
|
|
104
|
+
# check for parameter markers
|
|
105
|
+
if not tags:
|
|
106
|
+
for param in get_typed_parameters(member):
|
|
107
|
+
if is_marker(param.default):
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
return False
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Iterator
|
|
4
|
-
from functools import wraps
|
|
5
4
|
from typing import Annotated, Any
|
|
6
5
|
|
|
7
6
|
from django.conf import settings
|
|
@@ -9,7 +8,6 @@ from django.core.cache import BaseCache, caches
|
|
|
9
8
|
from django.db import connections
|
|
10
9
|
from django.db.backends.base.base import BaseDatabaseWrapper
|
|
11
10
|
from django.urls import URLPattern, URLResolver, get_resolver
|
|
12
|
-
from typing_extensions import get_origin
|
|
13
11
|
|
|
14
12
|
from anydi import Container
|
|
15
13
|
|
|
@@ -29,14 +27,11 @@ def register_settings(
|
|
|
29
27
|
continue
|
|
30
28
|
|
|
31
29
|
container.register(
|
|
32
|
-
Annotated[
|
|
30
|
+
Annotated[type(setting_value), f"{prefix}{setting_name}"],
|
|
33
31
|
_get_setting_value(setting_value),
|
|
34
32
|
scope="singleton",
|
|
35
33
|
)
|
|
36
34
|
|
|
37
|
-
# Patch AnyDI to resolve Any types for annotated settings
|
|
38
|
-
_patch_any_typed_annotated(container, prefix=prefix)
|
|
39
|
-
|
|
40
35
|
|
|
41
36
|
def register_components(container: Container) -> None:
|
|
42
37
|
"""Register Django components into the container."""
|
|
@@ -91,38 +86,3 @@ def iter_urlpatterns(
|
|
|
91
86
|
|
|
92
87
|
def _get_setting_value(value: Any) -> Any:
|
|
93
88
|
return lambda: value
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _any_typed_interface(interface: Any, prefix: str) -> Any:
|
|
97
|
-
origin = get_origin(interface)
|
|
98
|
-
if origin is not Annotated:
|
|
99
|
-
return interface # pragma: no cover
|
|
100
|
-
named = interface.__metadata__[-1]
|
|
101
|
-
|
|
102
|
-
if isinstance(named, str) and named.startswith(prefix):
|
|
103
|
-
_, setting_name = named.rsplit(prefix, maxsplit=1)
|
|
104
|
-
return Annotated[Any, f"{prefix}{setting_name}"]
|
|
105
|
-
return interface
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def _patch_any_typed_annotated(container: Container, *, prefix: str) -> None:
|
|
109
|
-
def _patch_resolve(resolve: Any) -> Any:
|
|
110
|
-
@wraps(resolve)
|
|
111
|
-
def wrapper(interface: Any) -> Any:
|
|
112
|
-
return resolve(_any_typed_interface(interface, prefix))
|
|
113
|
-
|
|
114
|
-
return wrapper
|
|
115
|
-
|
|
116
|
-
def _patch_aresolve(resolve: Any) -> Any:
|
|
117
|
-
@wraps(resolve)
|
|
118
|
-
async def wrapper(interface: Any) -> Any:
|
|
119
|
-
return await resolve(_any_typed_interface(interface, prefix))
|
|
120
|
-
|
|
121
|
-
return wrapper
|
|
122
|
-
|
|
123
|
-
container.resolve = _patch_resolve( # type: ignore
|
|
124
|
-
container.resolve
|
|
125
|
-
)
|
|
126
|
-
container.aresolve = _patch_aresolve( # type: ignore
|
|
127
|
-
container.aresolve
|
|
128
|
-
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "anydi"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.43.0"
|
|
4
4
|
description = "Dependency Injection library"
|
|
5
5
|
authors = [{ name = "Anton Ruhlov", email = "antonruhlov@gmail.com" }]
|
|
6
6
|
requires-python = "~=3.9"
|
|
@@ -65,6 +65,7 @@ dev = [
|
|
|
65
65
|
"pydantic-settings>=2.4.0,<3",
|
|
66
66
|
"bump-my-version>=1.1.4",
|
|
67
67
|
"pyright>=1.1.401",
|
|
68
|
+
"pytest-mock>=3.14.1",
|
|
68
69
|
]
|
|
69
70
|
docs = [
|
|
70
71
|
"mkdocs>=1.4.2,<2",
|
|
@@ -136,7 +137,7 @@ omit = [
|
|
|
136
137
|
]
|
|
137
138
|
|
|
138
139
|
[tool.bumpversion]
|
|
139
|
-
current_version = "0.
|
|
140
|
+
current_version = "0.43.0"
|
|
140
141
|
parse = """(?x)
|
|
141
142
|
(?P<major>0|[1-9]\\d*)\\.
|
|
142
143
|
(?P<minor>0|[1-9]\\d*)\\.
|
|
@@ -10,7 +10,7 @@ from typing import Annotated, Any, Callable, Union
|
|
|
10
10
|
import pytest
|
|
11
11
|
from typing_extensions import Self
|
|
12
12
|
|
|
13
|
-
from anydi import Container, Provider, Scope, auto
|
|
13
|
+
from anydi import Container, Provider, Scope, auto, request, singleton, transient
|
|
14
14
|
from anydi._provider import ProviderKind
|
|
15
15
|
from anydi._typing import Event
|
|
16
16
|
|
|
@@ -979,8 +979,9 @@ class TestContainer:
|
|
|
979
979
|
def test_resolve_non_strict_provider_scope_defined(
|
|
980
980
|
self, container: Container
|
|
981
981
|
) -> None:
|
|
982
|
+
@singleton
|
|
982
983
|
class Service:
|
|
983
|
-
|
|
984
|
+
pass
|
|
984
985
|
|
|
985
986
|
_ = container.resolve(Service)
|
|
986
987
|
|
|
@@ -990,17 +991,6 @@ class TestContainer:
|
|
|
990
991
|
assert provider.scope == "singleton"
|
|
991
992
|
assert provider.interface == Service
|
|
992
993
|
|
|
993
|
-
def test_resolve_non_strict_annotated(self, container: Container) -> None:
|
|
994
|
-
class Service:
|
|
995
|
-
pass
|
|
996
|
-
|
|
997
|
-
service_1 = container.resolve(Annotated[Service, "service_1"])
|
|
998
|
-
service_2 = container.resolve(Annotated[Service, "service_2"])
|
|
999
|
-
|
|
1000
|
-
assert service_1 != service_2
|
|
1001
|
-
assert container.is_registered(Annotated[Service, "service_1"])
|
|
1002
|
-
assert container.is_registered(Annotated[Service, "service_2"])
|
|
1003
|
-
|
|
1004
994
|
def test_resolve_non_strict_provider_scope_from_sub_provider_request(
|
|
1005
995
|
self,
|
|
1006
996
|
container: Container,
|
|
@@ -1039,13 +1029,13 @@ class TestContainer:
|
|
|
1039
1029
|
def test_resolve_non_strict_nested_singleton_provider(
|
|
1040
1030
|
self, container: Container
|
|
1041
1031
|
) -> None:
|
|
1042
|
-
@
|
|
1032
|
+
@singleton
|
|
1043
1033
|
class Repository:
|
|
1044
|
-
|
|
1034
|
+
pass
|
|
1045
1035
|
|
|
1046
|
-
@dataclass
|
|
1047
1036
|
class Service:
|
|
1048
|
-
repository: Repository
|
|
1037
|
+
def __init__(self, repository: Repository) -> None:
|
|
1038
|
+
self.repository = repository
|
|
1049
1039
|
|
|
1050
1040
|
with container.request_context():
|
|
1051
1041
|
_ = container.resolve(Service)
|
|
@@ -1099,9 +1089,8 @@ class TestContainer:
|
|
|
1099
1089
|
def test_resolve_non_strict_with_as_context_manager(
|
|
1100
1090
|
self, container: Container
|
|
1101
1091
|
) -> None:
|
|
1092
|
+
@singleton
|
|
1102
1093
|
class Resource:
|
|
1103
|
-
__scope__ = "singleton"
|
|
1104
|
-
|
|
1105
1094
|
def __init__(self) -> None:
|
|
1106
1095
|
self.entered = False
|
|
1107
1096
|
self.exited = False
|
|
@@ -1125,9 +1114,8 @@ class TestContainer:
|
|
|
1125
1114
|
self,
|
|
1126
1115
|
container: Container,
|
|
1127
1116
|
) -> None:
|
|
1117
|
+
@singleton
|
|
1128
1118
|
class Service:
|
|
1129
|
-
__scope__ = "singleton"
|
|
1130
|
-
|
|
1131
1119
|
def __init__(self) -> None:
|
|
1132
1120
|
self.entered = False
|
|
1133
1121
|
self.exited = False
|
|
@@ -1326,11 +1314,10 @@ class TestContainer:
|
|
|
1326
1314
|
assert kwargs == {"a": 10, "b": 1.0, "c": "test"}
|
|
1327
1315
|
|
|
1328
1316
|
def test_create_transient_non_strict(self) -> None:
|
|
1329
|
-
@
|
|
1317
|
+
@transient
|
|
1330
1318
|
class Component:
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
name: str
|
|
1319
|
+
def __init__(self, name: str) -> None:
|
|
1320
|
+
self.name = name
|
|
1334
1321
|
|
|
1335
1322
|
container = Container(strict=False)
|
|
1336
1323
|
|
|
@@ -1339,11 +1326,10 @@ class TestContainer:
|
|
|
1339
1326
|
assert instance.name == "test"
|
|
1340
1327
|
|
|
1341
1328
|
def test_create_singleton_non_strict(self) -> None:
|
|
1342
|
-
@
|
|
1329
|
+
@singleton
|
|
1343
1330
|
class Component:
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
name: str
|
|
1331
|
+
def __init__(self, name: str) -> None:
|
|
1332
|
+
self.name = name
|
|
1347
1333
|
|
|
1348
1334
|
container = Container(strict=False)
|
|
1349
1335
|
|
|
@@ -1352,11 +1338,10 @@ class TestContainer:
|
|
|
1352
1338
|
assert instance.name == "test"
|
|
1353
1339
|
|
|
1354
1340
|
def test_create_scoped_non_strict(self) -> None:
|
|
1355
|
-
@
|
|
1341
|
+
@request
|
|
1356
1342
|
class Component:
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
name: str
|
|
1343
|
+
def __init__(self, name: str) -> None:
|
|
1344
|
+
self.name = name
|
|
1360
1345
|
|
|
1361
1346
|
container = Container(strict=False)
|
|
1362
1347
|
|
|
@@ -1366,8 +1351,9 @@ class TestContainer:
|
|
|
1366
1351
|
assert instance.name == "test"
|
|
1367
1352
|
|
|
1368
1353
|
def test_create_non_existing_keyword_arg(self) -> None:
|
|
1354
|
+
@singleton
|
|
1369
1355
|
class Component:
|
|
1370
|
-
|
|
1356
|
+
pass
|
|
1371
1357
|
|
|
1372
1358
|
container = Container(strict=False)
|
|
1373
1359
|
|
|
@@ -1375,11 +1361,10 @@ class TestContainer:
|
|
|
1375
1361
|
container.create(Component, param="test")
|
|
1376
1362
|
|
|
1377
1363
|
async def test_create_async_transient_non_strict(self) -> None:
|
|
1378
|
-
@
|
|
1364
|
+
@transient
|
|
1379
1365
|
class Component:
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
name: str
|
|
1366
|
+
def __init__(self, name: str) -> None:
|
|
1367
|
+
self.name = name
|
|
1383
1368
|
|
|
1384
1369
|
container = Container(strict=False)
|
|
1385
1370
|
|
|
@@ -1388,11 +1373,10 @@ class TestContainer:
|
|
|
1388
1373
|
assert instance.name == "test"
|
|
1389
1374
|
|
|
1390
1375
|
async def test_create_async_singleton_non_strict(self) -> None:
|
|
1391
|
-
@
|
|
1376
|
+
@singleton
|
|
1392
1377
|
class Component:
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
name: str
|
|
1378
|
+
def __init__(self, name: str) -> None:
|
|
1379
|
+
self.name = name
|
|
1396
1380
|
|
|
1397
1381
|
container = Container(strict=False)
|
|
1398
1382
|
|
|
@@ -1401,11 +1385,10 @@ class TestContainer:
|
|
|
1401
1385
|
assert instance.name == "test"
|
|
1402
1386
|
|
|
1403
1387
|
async def test_create_async_scoped_non_strict(self) -> None:
|
|
1404
|
-
@
|
|
1388
|
+
@request
|
|
1405
1389
|
class Component:
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
name: str
|
|
1390
|
+
def __init__(self, name: str) -> None:
|
|
1391
|
+
self.name = name
|
|
1409
1392
|
|
|
1410
1393
|
container = Container(strict=False)
|
|
1411
1394
|
|
|
@@ -1415,8 +1398,9 @@ class TestContainer:
|
|
|
1415
1398
|
assert instance.name == "test"
|
|
1416
1399
|
|
|
1417
1400
|
async def test_create_async_non_existing_keyword_arg(self) -> None:
|
|
1401
|
+
@singleton
|
|
1418
1402
|
class Component:
|
|
1419
|
-
|
|
1403
|
+
pass
|
|
1420
1404
|
|
|
1421
1405
|
container = Container(strict=False)
|
|
1422
1406
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from anydi import Module, injectable, provider, request, singleton, transient
|
|
2
|
+
from anydi._decorators import is_injectable, is_provided
|
|
2
3
|
|
|
3
4
|
from tests.fixtures import Service
|
|
4
5
|
|
|
@@ -18,22 +19,28 @@ def test_provider_decorator() -> None:
|
|
|
18
19
|
def test_request_decorator() -> None:
|
|
19
20
|
request(Service)
|
|
20
21
|
|
|
21
|
-
assert
|
|
22
|
-
assert
|
|
22
|
+
assert is_provided(Service)
|
|
23
|
+
assert Service.__provided__ == {
|
|
24
|
+
"scope": "request",
|
|
25
|
+
}
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
def test_transient_decorator() -> None:
|
|
26
29
|
transient(Service)
|
|
27
30
|
|
|
28
|
-
assert
|
|
29
|
-
assert
|
|
31
|
+
assert is_provided(Service)
|
|
32
|
+
assert Service.__provided__ == {
|
|
33
|
+
"scope": "transient",
|
|
34
|
+
}
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
def test_singleton_decorator() -> None:
|
|
33
38
|
singleton(Service)
|
|
34
39
|
|
|
35
|
-
assert
|
|
36
|
-
assert
|
|
40
|
+
assert is_provided(Service)
|
|
41
|
+
assert Service.__provided__ == {
|
|
42
|
+
"scope": "singleton",
|
|
43
|
+
}
|
|
37
44
|
|
|
38
45
|
|
|
39
46
|
def test_injectable_no_args() -> None:
|
|
@@ -41,8 +48,8 @@ def test_injectable_no_args() -> None:
|
|
|
41
48
|
def my_func() -> None:
|
|
42
49
|
pass
|
|
43
50
|
|
|
44
|
-
assert
|
|
45
|
-
|
|
51
|
+
assert is_injectable(my_func)
|
|
52
|
+
assert my_func.__injectable__ == {
|
|
46
53
|
"tags": None,
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -52,7 +59,10 @@ def test_injectable_no_args_provided() -> None:
|
|
|
52
59
|
def my_func() -> None:
|
|
53
60
|
pass
|
|
54
61
|
|
|
55
|
-
assert
|
|
62
|
+
assert is_injectable(my_func)
|
|
63
|
+
assert my_func.__injectable__ == {
|
|
64
|
+
"tags": None,
|
|
65
|
+
}
|
|
56
66
|
|
|
57
67
|
|
|
58
68
|
def test_injectable_with_tags() -> None:
|
|
@@ -60,7 +70,7 @@ def test_injectable_with_tags() -> None:
|
|
|
60
70
|
def my_func() -> None:
|
|
61
71
|
pass
|
|
62
72
|
|
|
63
|
-
assert
|
|
64
|
-
|
|
73
|
+
assert is_injectable(my_func)
|
|
74
|
+
assert my_func.__injectable__ == {
|
|
65
75
|
"tags": ["tag1", "tag2"],
|
|
66
76
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pytest_mock import MockerFixture
|
|
3
|
+
|
|
4
|
+
from anydi import Container
|
|
5
|
+
|
|
6
|
+
from tests.scan_app import ScanAppModule
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestContainerScan:
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def container(self) -> Container:
|
|
12
|
+
return Container()
|
|
13
|
+
|
|
14
|
+
def test_scan_registers_all_dependencies(
|
|
15
|
+
self, container: Container, mocker: MockerFixture
|
|
16
|
+
) -> None:
|
|
17
|
+
inject_spy = mocker.spy(container, "inject")
|
|
18
|
+
|
|
19
|
+
container.register_module(ScanAppModule)
|
|
20
|
+
container.scan(["tests.scan_app"])
|
|
21
|
+
|
|
22
|
+
# Expecting 7 total inject calls from all eligible @injectable functions
|
|
23
|
+
assert inject_spy.call_count == 7
|
|
24
|
+
|
|
25
|
+
from .scan_app.a.a3.handlers import a_a3_handler_1, a_a3_handler_2
|
|
26
|
+
|
|
27
|
+
assert a_a3_handler_1() == "a.a1.str_provider"
|
|
28
|
+
assert a_a3_handler_2().ident == "a.a1.str_provider"
|
|
29
|
+
|
|
30
|
+
def test_scan_single_module_registers_limited_dependencies(
|
|
31
|
+
self, container: Container, mocker: MockerFixture
|
|
32
|
+
) -> None:
|
|
33
|
+
inject_spy = mocker.spy(container, "inject")
|
|
34
|
+
|
|
35
|
+
container.register_module(ScanAppModule)
|
|
36
|
+
container.scan("tests.scan_app.a.a3.handlers")
|
|
37
|
+
|
|
38
|
+
# Expecting only 2 handlers to be registered from this specific module
|
|
39
|
+
assert inject_spy.call_count == 2
|
|
40
|
+
|
|
41
|
+
from .scan_app.a.a3.handlers import a_a3_handler_1
|
|
42
|
+
|
|
43
|
+
assert a_a3_handler_1() == "a.a1.str_provider"
|
|
44
|
+
|
|
45
|
+
def test_scan_with_unknown_tag_registers_nothing(
|
|
46
|
+
self, container: Container, mocker: MockerFixture
|
|
47
|
+
) -> None:
|
|
48
|
+
inject_spy = mocker.spy(container, "inject")
|
|
49
|
+
|
|
50
|
+
container.scan(["tests.scan_app"], tags=["non_existing_tag"])
|
|
51
|
+
|
|
52
|
+
# No injectable functions should be picked up with unmatched tags
|
|
53
|
+
inject_spy.assert_not_called()
|
|
54
|
+
|
|
55
|
+
def test_scan_with_matching_tag_registers_only_matching_dependencies(
|
|
56
|
+
self, container: Container, mocker: MockerFixture
|
|
57
|
+
) -> None:
|
|
58
|
+
inject_spy = mocker.spy(container, "inject")
|
|
59
|
+
|
|
60
|
+
container.register_module(ScanAppModule)
|
|
61
|
+
container.scan(["tests.scan_app"], tags=["inject"])
|
|
62
|
+
|
|
63
|
+
# Only handlers with @injectable(..., tags=["inject"]) should be included
|
|
64
|
+
assert inject_spy.call_count == 1
|
|
65
|
+
|
|
66
|
+
from .scan_app.a.a3.handlers import a_a3_handler_1
|
|
67
|
+
|
|
68
|
+
assert a_a3_handler_1() == "a.a1.str_provider"
|
|
@@ -6,7 +6,7 @@ from unittest import mock
|
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
|
-
from anydi import Container, auto
|
|
9
|
+
from anydi import Container, auto, singleton
|
|
10
10
|
from anydi.testing import TestContainer
|
|
11
11
|
|
|
12
12
|
from tests.fixtures import Service
|
|
@@ -99,9 +99,8 @@ class TestTestContainer:
|
|
|
99
99
|
def get_user(self) -> str:
|
|
100
100
|
return "user"
|
|
101
101
|
|
|
102
|
+
@singleton
|
|
102
103
|
class UserService:
|
|
103
|
-
__scope__ = "singleton"
|
|
104
|
-
|
|
105
104
|
def __init__(self, repo: UserRepo, param: Annotated[str, "param"]) -> None:
|
|
106
105
|
self.repo = repo
|
|
107
106
|
self.param = param
|
|
@@ -130,9 +129,8 @@ class TestTestContainer:
|
|
|
130
129
|
container = TestContainer(strict=False)
|
|
131
130
|
container.register(Annotated[str, "param"], lambda: "param", scope="singleton")
|
|
132
131
|
|
|
132
|
+
@singleton
|
|
133
133
|
class UserService:
|
|
134
|
-
__scope__ = "singleton"
|
|
135
|
-
|
|
136
134
|
def __init__(self, param: Annotated[str, "param"]) -> None:
|
|
137
135
|
self.param = param
|
|
138
136
|
|
|
@@ -13,7 +13,7 @@ wheels = [
|
|
|
13
13
|
|
|
14
14
|
[[package]]
|
|
15
15
|
name = "anydi"
|
|
16
|
-
version = "0.
|
|
16
|
+
version = "0.43.0"
|
|
17
17
|
source = { editable = "." }
|
|
18
18
|
dependencies = [
|
|
19
19
|
{ name = "anyio" },
|
|
@@ -35,6 +35,7 @@ dev = [
|
|
|
35
35
|
{ name = "pytest" },
|
|
36
36
|
{ name = "pytest-cov" },
|
|
37
37
|
{ name = "pytest-django" },
|
|
38
|
+
{ name = "pytest-mock" },
|
|
38
39
|
{ name = "redis" },
|
|
39
40
|
{ name = "ruff" },
|
|
40
41
|
{ name = "starlette" },
|
|
@@ -65,6 +66,7 @@ dev = [
|
|
|
65
66
|
{ name = "pytest", specifier = ">=8.4.0,<9" },
|
|
66
67
|
{ name = "pytest-cov", specifier = ">=6.0.0,<7" },
|
|
67
68
|
{ name = "pytest-django", specifier = ">=4.8.0,<5" },
|
|
69
|
+
{ name = "pytest-mock", specifier = ">=3.14.1" },
|
|
68
70
|
{ name = "redis", specifier = ">=5.0.4,<6" },
|
|
69
71
|
{ name = "ruff", specifier = ">=0.11.10,<1.0" },
|
|
70
72
|
{ name = "starlette", specifier = ">=0.37.2" },
|
|
@@ -973,6 +975,18 @@ wheels = [
|
|
|
973
975
|
{ url = "https://files.pythonhosted.org/packages/47/fe/54f387ee1b41c9ad59e48fb8368a361fad0600fe404315e31a12bacaea7d/pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99", size = 23723, upload-time = "2024-09-02T15:49:17.127Z" },
|
|
974
976
|
]
|
|
975
977
|
|
|
978
|
+
[[package]]
|
|
979
|
+
name = "pytest-mock"
|
|
980
|
+
version = "3.14.1"
|
|
981
|
+
source = { registry = "https://pypi.org/simple" }
|
|
982
|
+
dependencies = [
|
|
983
|
+
{ name = "pytest" },
|
|
984
|
+
]
|
|
985
|
+
sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" }
|
|
986
|
+
wheels = [
|
|
987
|
+
{ url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" },
|
|
988
|
+
]
|
|
989
|
+
|
|
976
990
|
[[package]]
|
|
977
991
|
name = "python-dateutil"
|
|
978
992
|
version = "2.9.0.post0"
|
anydi-0.42.0/tests/test_scan.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from anydi import Container
|
|
4
|
-
|
|
5
|
-
from tests.scan_app import ScanAppModule
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestContainerScan:
|
|
9
|
-
@pytest.fixture
|
|
10
|
-
def container(self) -> Container:
|
|
11
|
-
return Container()
|
|
12
|
-
|
|
13
|
-
def test_scan(self, container: Container) -> None:
|
|
14
|
-
container.register_module(ScanAppModule)
|
|
15
|
-
container.scan(["tests.scan_app"])
|
|
16
|
-
|
|
17
|
-
from .scan_app.a.a3.handlers import a_a3_handler_1, a_a3_handler_2
|
|
18
|
-
|
|
19
|
-
assert a_a3_handler_1() == "a.a1.str_provider"
|
|
20
|
-
assert a_a3_handler_2().ident == "a.a1.str_provider"
|
|
21
|
-
|
|
22
|
-
def test_scan_single_package(self, container: Container) -> None:
|
|
23
|
-
container.register_module(ScanAppModule)
|
|
24
|
-
container.scan("tests.scan_app.a.a3.handlers")
|
|
25
|
-
|
|
26
|
-
from .scan_app.a.a3.handlers import a_a3_handler_1
|
|
27
|
-
|
|
28
|
-
assert a_a3_handler_1() == "a.a1.str_provider"
|
|
29
|
-
|
|
30
|
-
def test_scan_non_existing_tag(self, container: Container) -> None:
|
|
31
|
-
container.scan(["tests.scan_app"], tags=["non_existing_tag"])
|
|
32
|
-
|
|
33
|
-
assert not container.providers
|
|
34
|
-
|
|
35
|
-
def test_scan_tagged(self, container: Container) -> None:
|
|
36
|
-
container.register_module(ScanAppModule)
|
|
37
|
-
container.scan(["tests.scan_app.a"], tags=["inject"])
|
|
38
|
-
|
|
39
|
-
from .scan_app.a.a3.handlers import a_a3_handler_1
|
|
40
|
-
|
|
41
|
-
assert a_a3_handler_1() == "a.a1.str_provider"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|