anydi 0.67.2__py3-none-any.whl → 0.69.0__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 CHANGED
@@ -33,18 +33,24 @@ class Scanner:
33
33
  self._container = container
34
34
 
35
35
  def scan(
36
- self, /, packages: PackageOrIterable, *, tags: Iterable[str] | None = None
36
+ self,
37
+ /,
38
+ packages: PackageOrIterable,
39
+ *,
40
+ tags: Iterable[str] | None = None,
41
+ ignore: PackageOrIterable | None = None,
37
42
  ) -> None:
38
43
  """Scan packages or modules for decorated members and inject dependencies."""
39
44
  if isinstance(packages, (ModuleType, str)):
40
45
  packages = [packages]
41
46
 
42
47
  tags_list = list(tags) if tags else []
48
+ ignore_prefixes = self._normalize_ignore(ignore)
43
49
  provided_classes: list[type[Provided]] = []
44
50
  injectable_dependencies: list[ScannedDependency] = []
45
51
 
46
52
  # Single pass: collect both @provided classes and @injectable functions
47
- for module in self._iter_modules(packages):
53
+ for module in self._iter_modules(packages, ignore_prefixes=ignore_prefixes):
48
54
  provided_classes.extend(self._scan_module_for_provided(module))
49
55
  injectable_dependencies.extend(
50
56
  self._scan_module_for_injectable(module, tags=tags_list)
@@ -52,16 +58,47 @@ class Scanner:
52
58
 
53
59
  # First: register @provided classes
54
60
  for cls in provided_classes:
55
- if not self._container.is_registered(cls):
61
+ dependency_type = cls.__provided__.get("dependency_type", cls)
62
+ if not self._container.is_registered(dependency_type):
56
63
  scope = cls.__provided__["scope"]
57
- self._container.register(cls, scope=scope)
64
+ from_context = cls.__provided__.get("from_context", False)
65
+ self._container.register(
66
+ dependency_type, cls, scope=scope, from_context=from_context
67
+ )
58
68
 
59
69
  # Second: inject @injectable functions
60
70
  for dependency in injectable_dependencies:
61
71
  decorated = self._container.inject()(dependency.member)
62
72
  setattr(dependency.module, dependency.member.__name__, decorated)
63
73
 
64
- def _iter_modules(self, packages: Iterable[Package]) -> Iterator[ModuleType]:
74
+ def _normalize_ignore(self, ignore: PackageOrIterable | None) -> list[str]:
75
+ """Normalize ignore parameter to a list of module name prefixes."""
76
+ if ignore is None:
77
+ return []
78
+
79
+ if isinstance(ignore, (ModuleType, str)):
80
+ ignore = [ignore]
81
+
82
+ prefixes: list[str] = []
83
+ for item in ignore:
84
+ if isinstance(item, ModuleType):
85
+ prefixes.append(item.__name__)
86
+ else:
87
+ prefixes.append(item)
88
+ return prefixes
89
+
90
+ def _should_ignore_module(
91
+ self, module_name: str, ignore_prefixes: list[str]
92
+ ) -> bool:
93
+ """Check if a module should be ignored based on ignore prefixes."""
94
+ for prefix in ignore_prefixes:
95
+ if module_name == prefix or module_name.startswith(prefix + "."):
96
+ return True
97
+ return False
98
+
99
+ def _iter_modules(
100
+ self, packages: Iterable[Package], *, ignore_prefixes: list[str]
101
+ ) -> Iterator[ModuleType]:
65
102
  """Iterate over all modules in the given packages."""
66
103
  for package in packages:
67
104
  if isinstance(package, str):
@@ -69,14 +106,16 @@ class Scanner:
69
106
 
70
107
  # Single module (not a package)
71
108
  if not hasattr(package, "__path__"):
72
- yield package
109
+ if not self._should_ignore_module(package.__name__, ignore_prefixes):
110
+ yield package
73
111
  continue
74
112
 
75
113
  # Package - walk all submodules
76
114
  for module_info in pkgutil.walk_packages(
77
115
  package.__path__, prefix=package.__name__ + "."
78
116
  ):
79
- yield importlib.import_module(module_info.name)
117
+ if not self._should_ignore_module(module_info.name, ignore_prefixes):
118
+ yield importlib.import_module(module_info.name)
80
119
 
81
120
  def _scan_module_for_provided(self, module: ModuleType) -> list[type[Provided]]:
82
121
  """Scan a module for @provided classes."""
anydi/ext/fastapi.py CHANGED
@@ -35,7 +35,7 @@ class FastAPIMarker(params.Depends, Marker):
35
35
  async def _fastapi_dependency(
36
36
  self, container: Annotated[Container, Depends(get_container)]
37
37
  ) -> Any:
38
- return await container.aresolve(self.interface)
38
+ return await container.aresolve(self.dependency_type)
39
39
 
40
40
 
41
41
  # Configure Inject() and Provide[T] to use FastAPI-specific marker at import time
anydi/ext/faststream.py CHANGED
@@ -62,7 +62,7 @@ class FastStreamMarker(Dependant, Marker):
62
62
 
63
63
  async def _faststream_dependency(self, context: ContextRepo) -> Any:
64
64
  container = get_container_from_context(context)
65
- return await container.aresolve(self.interface)
65
+ return await container.aresolve(self.dependency_type)
66
66
 
67
67
 
68
68
  # Configure Inject() and Provide[T] to use FastStream-specific marker
@@ -26,14 +26,14 @@ def install(
26
26
  all_fields = {**settings_cls.model_fields, **settings_cls.model_computed_fields}
27
27
  for setting_name, field_info in all_fields.items():
28
28
  if isinstance(field_info, ComputedFieldInfo):
29
- interface = field_info.return_type
29
+ origin = field_info.return_type
30
30
  elif isinstance(field_info, FieldInfo):
31
- interface = field_info.annotation
31
+ origin = field_info.annotation
32
32
  else:
33
33
  continue
34
34
 
35
35
  container.register(
36
- Annotated[interface, f"{prefix}{setting_name}"],
36
+ Annotated[origin, f"{prefix}{setting_name}"],
37
37
  _get_setting_value(getattr(_settings, setting_name)),
38
38
  scope="singleton",
39
39
  )