anydi 0.34.0__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/_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