anydi 0.60.0__tar.gz → 0.60.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.60.0 → anydi-0.60.2}/PKG-INFO +1 -1
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_container.py +41 -20
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/typer.py +13 -6
- {anydi-0.60.0 → anydi-0.60.2}/pyproject.toml +1 -1
- {anydi-0.60.0 → anydi-0.60.2}/README.md +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/__init__.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_async_lock.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_context.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_decorators.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_injector.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_module.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_provider.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_resolver.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_scanner.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/_types.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/__init__.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/django/__init__.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/fastapi.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/faststream.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/pydantic_settings.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/pytest_plugin.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/starlette/__init__.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/ext/starlette/middleware.py +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/py.typed +0 -0
- {anydi-0.60.0 → anydi-0.60.2}/anydi/testing.py +0 -0
|
@@ -257,31 +257,52 @@ class Container:
|
|
|
257
257
|
# Register the scope
|
|
258
258
|
self._scopes[scope] = tuple({scope, "singleton"} | set(parents))
|
|
259
259
|
|
|
260
|
-
def
|
|
261
|
-
"""
|
|
262
|
-
#
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
def get_context_scopes(self, scopes: set[Scope] | None = None) -> list[str]: # noqa: C901
|
|
261
|
+
"""Return scopes that require context management in dependency order."""
|
|
262
|
+
# Build execution order: singleton -> request -> custom (by depth)
|
|
263
|
+
ordered = ["singleton"]
|
|
264
|
+
custom_scopes: list[tuple[int, str]] = []
|
|
265
|
+
has_request = False
|
|
266
|
+
|
|
267
|
+
for scope, parents in self._scopes.items():
|
|
268
|
+
if scope == "singleton":
|
|
269
|
+
continue
|
|
270
|
+
if scope == "request":
|
|
271
|
+
has_request = True
|
|
272
|
+
continue
|
|
265
273
|
if scope == "transient":
|
|
266
274
|
continue
|
|
267
|
-
|
|
268
|
-
expanded_scopes.add("singleton")
|
|
269
|
-
else:
|
|
270
|
-
# Add the scope and all its parents from container._scopes
|
|
271
|
-
expanded_scopes.update(self._scopes[scope])
|
|
275
|
+
custom_scopes.append((len(parents), scope))
|
|
272
276
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
other_scopes = expanded_scopes - {"singleton"}
|
|
277
|
+
if has_request:
|
|
278
|
+
ordered.append("request")
|
|
276
279
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
custom_scopes.sort(key=lambda item: item[0])
|
|
281
|
+
ordered.extend(scope for _, scope in custom_scopes)
|
|
282
|
+
|
|
283
|
+
# If no filter, return all scopes with contexts (transient excluded)
|
|
284
|
+
if scopes is None:
|
|
285
|
+
return ordered
|
|
286
|
+
|
|
287
|
+
# Helper to add scope with its parents to needed set
|
|
288
|
+
def add_scope_tree(needed: set[str], scope: str) -> None:
|
|
289
|
+
if scope == "singleton":
|
|
290
|
+
needed.add("singleton")
|
|
291
|
+
elif scope != "transient":
|
|
292
|
+
needed.update(self._scopes[scope])
|
|
293
|
+
|
|
294
|
+
needed_scopes: set[str] = set()
|
|
295
|
+
|
|
296
|
+
# Add injected scopes and their parents
|
|
297
|
+
for scope in scopes:
|
|
298
|
+
add_scope_tree(needed_scopes, scope)
|
|
299
|
+
|
|
300
|
+
# Add scopes with resource providers and their parents
|
|
301
|
+
for scope in ordered:
|
|
302
|
+
if self._resources.get(scope):
|
|
303
|
+
add_scope_tree(needed_scopes, scope)
|
|
282
304
|
|
|
283
|
-
|
|
284
|
-
return ["singleton", *ordered_scopes] if has_singleton else ordered_scopes
|
|
305
|
+
return [scope for scope in ordered if scope in needed_scopes]
|
|
285
306
|
|
|
286
307
|
# == Provider Registry ==
|
|
287
308
|
|
|
@@ -12,6 +12,7 @@ import anyio
|
|
|
12
12
|
from typer import Typer
|
|
13
13
|
|
|
14
14
|
from anydi import Container, Scope
|
|
15
|
+
from anydi._decorators import is_provided
|
|
15
16
|
|
|
16
17
|
__all__ = ["install"]
|
|
17
18
|
|
|
@@ -38,11 +39,12 @@ def _wrap_async_callback_with_injection(
|
|
|
38
39
|
@functools.wraps(callback)
|
|
39
40
|
def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
40
41
|
async def _run() -> Any:
|
|
41
|
-
|
|
42
|
+
# Get scopes for execution (injected OR have resources)
|
|
43
|
+
needed_scopes = container.get_context_scopes(scopes)
|
|
42
44
|
|
|
43
45
|
async with contextlib.AsyncExitStack() as stack:
|
|
44
46
|
# Start scoped contexts in dependency order
|
|
45
|
-
for scope in
|
|
47
|
+
for scope in needed_scopes:
|
|
46
48
|
if scope == "singleton":
|
|
47
49
|
await stack.enter_async_context(container)
|
|
48
50
|
else:
|
|
@@ -60,7 +62,7 @@ def _wrap_async_callback_with_injection(
|
|
|
60
62
|
return async_wrapper
|
|
61
63
|
|
|
62
64
|
|
|
63
|
-
def _process_callback(callback: Callable[..., Any], container: Container) -> Any:
|
|
65
|
+
def _process_callback(callback: Callable[..., Any], container: Container) -> Any: # noqa: C901
|
|
64
66
|
"""Validate and wrap a callback for dependency injection."""
|
|
65
67
|
sig = inspect.signature(callback, eval_str=True)
|
|
66
68
|
injected_param_names: set[str] = set()
|
|
@@ -74,7 +76,11 @@ def _process_callback(callback: Callable[..., Any], container: Container) -> Any
|
|
|
74
76
|
)
|
|
75
77
|
if should_inject:
|
|
76
78
|
injected_param_names.add(parameter.name)
|
|
77
|
-
|
|
79
|
+
try:
|
|
80
|
+
scopes.add(container.providers[interface].scope)
|
|
81
|
+
except KeyError:
|
|
82
|
+
if inspect.isclass(interface) and is_provided(interface):
|
|
83
|
+
scopes.add(interface.__provided__["scope"])
|
|
78
84
|
else:
|
|
79
85
|
non_injected_params.add(parameter)
|
|
80
86
|
|
|
@@ -94,11 +100,12 @@ def _process_callback(callback: Callable[..., Any], container: Container) -> Any
|
|
|
94
100
|
|
|
95
101
|
@functools.wraps(callback)
|
|
96
102
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
97
|
-
|
|
103
|
+
# Get scopes for execution (injected OR have resources)
|
|
104
|
+
needed_scopes = container.get_context_scopes(scopes)
|
|
98
105
|
|
|
99
106
|
with contextlib.ExitStack() as stack:
|
|
100
107
|
# Start scoped contexts in dependency order
|
|
101
|
-
for scope in
|
|
108
|
+
for scope in needed_scopes:
|
|
102
109
|
if scope == "singleton":
|
|
103
110
|
stack.enter_context(container)
|
|
104
111
|
else:
|
|
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
|