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/_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