anydi 0.60.1__py3-none-any.whl → 0.60.2__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/_container.py CHANGED
@@ -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 get_ordered_scopes(self, scopes: set[Scope]) -> list[str]:
261
- """Get ordered list of scopes to enter."""
262
- # Expand scopes to include all parent scopes
263
- expanded_scopes: set[str] = set()
264
- for scope in scopes:
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
- elif scope == "singleton":
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
- # Separate singleton from other scopes
274
- has_singleton = "singleton" in expanded_scopes
275
- other_scopes = expanded_scopes - {"singleton"}
277
+ if has_request:
278
+ ordered.append("request")
276
279
 
277
- # Sort other scopes by dependency depth (parents before children)
278
- # Scopes with fewer parents come first
279
- ordered_scopes = sorted(
280
- other_scopes, key=lambda scope: len(self._scopes[scope])
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
- # Return with singleton first if needed
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
 
anydi/ext/typer.py CHANGED
@@ -12,11 +12,10 @@ 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
 
18
- from anydi._decorators import is_provided
19
-
20
19
 
21
20
  def _wrap_async_callback_no_injection(callback: Callable[..., Any]) -> Any:
22
21
  """Wrap async callback without injection in anyio.run()."""
@@ -40,11 +39,12 @@ def _wrap_async_callback_with_injection(
40
39
  @functools.wraps(callback)
41
40
  def async_wrapper(*args: Any, **kwargs: Any) -> Any:
42
41
  async def _run() -> Any:
43
- ordered_scopes = container.get_ordered_scopes(scopes)
42
+ # Get scopes for execution (injected OR have resources)
43
+ needed_scopes = container.get_context_scopes(scopes)
44
44
 
45
45
  async with contextlib.AsyncExitStack() as stack:
46
46
  # Start scoped contexts in dependency order
47
- for scope in ordered_scopes:
47
+ for scope in needed_scopes:
48
48
  if scope == "singleton":
49
49
  await stack.enter_async_context(container)
50
50
  else:
@@ -100,11 +100,12 @@ def _process_callback(callback: Callable[..., Any], container: Container) -> Any
100
100
 
101
101
  @functools.wraps(callback)
102
102
  def wrapper(*args: Any, **kwargs: Any) -> Any:
103
- ordered_scopes = container.get_ordered_scopes(scopes)
103
+ # Get scopes for execution (injected OR have resources)
104
+ needed_scopes = container.get_context_scopes(scopes)
104
105
 
105
106
  with contextlib.ExitStack() as stack:
106
107
  # Start scoped contexts in dependency order
107
- for scope in ordered_scopes:
108
+ for scope in needed_scopes:
108
109
  if scope == "singleton":
109
110
  stack.enter_context(container)
110
111
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anydi
3
- Version: 0.60.1
3
+ Version: 0.60.2
4
4
  Summary: Dependency Injection library
5
5
  Keywords: dependency injection,dependencies,di,async,asyncio,application
6
6
  Author: Anton Ruhlov
@@ -1,6 +1,6 @@
1
1
  anydi/__init__.py,sha256=bQKzn9qfNnIMi1m3J-DdSknSDwNg8j08fdQg_-Edkto,613
2
2
  anydi/_async_lock.py,sha256=3dwZr0KthXFYha0XKMyXf8jMmGb1lYoNC0O5w29V9ic,1104
3
- anydi/_container.py,sha256=r7qcUCu4KO0YLPaUO5SEgUwnNOcZsP7aVDZ1oDTi0kA,27093
3
+ anydi/_container.py,sha256=Yr4X-3K4qgdCz_6LMbjjntp_-E8pOgorRYkt2dQUSuM,27746
4
4
  anydi/_context.py,sha256=-9QqeMWo9OpZVXZxZCQgIsswggl3Ch7lgx1KiFX_ezc,3752
5
5
  anydi/_decorators.py,sha256=J3W261ZAG7q4XKm4tbAv1wsWr9ysx9_5MUbUvSJB_MQ,2809
6
6
  anydi/_injector.py,sha256=IxKTh2rzMHrsW554tbiJl33Hb5sRGKYY_NU1rC4UvxE,4378
@@ -17,10 +17,10 @@ anydi/ext/pydantic_settings.py,sha256=jVJZ1wPaPpsxdNPlJj9yq282ebqLZ9tckWpZ0eIwWL
17
17
  anydi/ext/pytest_plugin.py,sha256=M54DkA-KxD9GqLnXdoCyn-Qur2c44MB6d0AgJuYCZ5w,16171
18
18
  anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
20
- anydi/ext/typer.py,sha256=__Ve50wRcxmLoCHxHg-75hs6frySGoRvv2Q2TnLElOg,5006
20
+ anydi/ext/typer.py,sha256=Jw3DspLeCD_yF_GSk4eDlC3bZJWMNYpJ3H6zs5PTVAc,5133
21
21
  anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  anydi/testing.py,sha256=cHg3mMScZbEep9smRqSNQ81BZMQOkyugHe8TvKdPnEg,1347
23
- anydi-0.60.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
24
- anydi-0.60.1.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
25
- anydi-0.60.1.dist-info/METADATA,sha256=35zPm0rrXV9B_swYvWOzRjCKH36M4tONVeeGlgaXOMI,8007
26
- anydi-0.60.1.dist-info/RECORD,,
23
+ anydi-0.60.2.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
24
+ anydi-0.60.2.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
25
+ anydi-0.60.2.dist-info/METADATA,sha256=h2TRAy4zFSXKRfRXPD6SnzQTFUi0jom2d6NbWRbyeB0,8007
26
+ anydi-0.60.2.dist-info/RECORD,,
File without changes