anydi 0.67.0__py3-none-any.whl → 0.67.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/_container.py CHANGED
@@ -390,28 +390,27 @@ class Container:
390
390
  name = type_repr(call)
391
391
  kind = ProviderKind.from_call(call)
392
392
  is_class = kind == ProviderKind.CLASS
393
- is_resource = kind in (ProviderKind.GENERATOR, ProviderKind.ASYNC_GENERATOR)
393
+ is_coroutine = kind == ProviderKind.COROUTINE
394
+ is_generator = kind == ProviderKind.GENERATOR
395
+ is_async_generator = kind == ProviderKind.ASYNC_GENERATOR
396
+ is_resource = is_generator or is_async_generator
394
397
 
395
- # Validate scope if it provided
398
+ # Validate scope
396
399
  self._validate_provider_scope(scope, name, is_resource)
397
400
 
398
- # Get the signature
401
+ # Get signature and detect interface
399
402
  signature = inspect.signature(call, eval_str=True)
400
403
 
401
- # Detect the interface
402
404
  if interface is NOT_SET:
403
- if is_class:
404
- interface = call
405
- else:
406
- interface = signature.return_annotation
407
- if interface is inspect.Signature.empty:
408
- interface = None
405
+ interface = call if is_class else signature.return_annotation
406
+ if interface is inspect.Signature.empty:
407
+ interface = None
409
408
 
410
- # If the callable is an iterator, return the actual type
411
- if is_iterator_type(interface) or is_iterator_type(get_origin(interface)):
409
+ # Handle iterator types for resources
410
+ interface_origin = get_origin(interface)
411
+ if is_iterator_type(interface) or is_iterator_type(interface_origin):
412
412
  if args := get_args(interface):
413
413
  interface = args[0]
414
- # If the callable is a generator, return the resource type
415
414
  if is_none_type(interface):
416
415
  interface = type(f"Event_{uuid.uuid4().hex}", (Event,), {})
417
416
  else:
@@ -420,24 +419,22 @@ class Container:
420
419
  "without actual type argument."
421
420
  )
422
421
 
423
- # None interface is not allowed
422
+ # Validate interface
424
423
  if is_none_type(interface):
425
424
  raise TypeError(f"Missing `{name}` provider return annotation.")
426
425
 
427
- # Check for existing provider
428
426
  if interface in self._providers and not override:
429
427
  raise LookupError(
430
428
  f"The provider interface `{type_repr(interface)}` already registered."
431
429
  )
432
430
 
433
- unresolved_parameter = None
434
- unresolved_exc: LookupError | None = None
431
+ # Process parameters
435
432
  parameters: list[ProviderParameter] = []
436
433
  scope_provider: dict[Scope, Provider] = {}
437
-
438
- # Precompute constant checks
434
+ unresolved_parameter = None
435
+ unresolved_exc: LookupError | None = None
439
436
  is_scoped = scope not in ("singleton", "transient")
440
- has_defaults = defaults is not None
437
+ scope_hierarchy = self._scopes.get(scope, ()) if scope != "transient" else ()
441
438
 
442
439
  for parameter in signature.parameters.values():
443
440
  if parameter.annotation is inspect.Parameter.empty:
@@ -451,20 +448,15 @@ class Container:
451
448
  f"are not allowed in the provider `{name}`."
452
449
  )
453
450
 
454
- default = (
455
- parameter.default
456
- if parameter.default is not inspect.Parameter.empty
457
- else NOT_SET
458
- )
459
- has_default = default is not NOT_SET
451
+ has_default = parameter.default is not inspect.Parameter.empty
452
+ default = parameter.default if has_default else NOT_SET
460
453
 
461
454
  try:
462
455
  sub_provider = self._get_or_register_provider(parameter.annotation)
463
456
  except LookupError as exc:
464
- if (has_defaults and parameter.name in defaults) or has_default:
457
+ if (defaults and parameter.name in defaults) or has_default:
465
458
  continue
466
- # For scoped (request/custom) dependencies, allow unresolved parameters
467
- # that will be provided via context.set()
459
+ # For scoped dependencies, allow unresolved parameters via context.set()
468
460
  if is_scoped:
469
461
  self._resolver.add_unresolved(parameter.annotation)
470
462
  parameters.append(
@@ -482,12 +474,11 @@ class Container:
482
474
  unresolved_exc = exc
483
475
  continue
484
476
 
485
- # Store first provider for each scope
486
- if sub_provider.scope not in scope_provider:
487
- scope_provider[sub_provider.scope] = sub_provider
477
+ # Track scope providers for validation
478
+ scope_provider.setdefault(sub_provider.scope, sub_provider)
488
479
 
489
- # For scoped dependencies with same scope: if auto-registered provider
490
- # has unresolved parameters, defer to context.set() instead
480
+ # For scoped dependencies with same scope having unresolved params,
481
+ # defer to context.set() instead
491
482
  if (
492
483
  is_scoped
493
484
  and sub_provider.scope == scope
@@ -506,6 +497,15 @@ class Container:
506
497
  )
507
498
  continue
508
499
 
500
+ # Validate scope compatibility inline
501
+ if scope_hierarchy and sub_provider.scope not in scope_hierarchy:
502
+ raise ValueError(
503
+ f"The provider `{name}` with a `{scope}` scope "
504
+ f"cannot depend on `{sub_provider}` with a "
505
+ f"`{sub_provider.scope}` scope. Please ensure all "
506
+ "providers are registered with matching scopes."
507
+ )
508
+
509
509
  parameters.append(
510
510
  ProviderParameter(
511
511
  name=parameter.name,
@@ -517,21 +517,11 @@ class Container:
517
517
  )
518
518
  )
519
519
 
520
- # Check scope compatibility
521
- # Transient scope can use any scoped dependencies
522
- if scope != "transient":
523
- for sub_provider in scope_provider.values():
524
- if sub_provider.scope not in self._scopes.get(scope, []):
525
- raise ValueError(
526
- f"The provider `{name}` with a `{scope}` scope "
527
- f"cannot depend on `{sub_provider}` with a "
528
- f"`{sub_provider.scope}` scope. Please ensure all "
529
- "providers are registered with matching scopes."
530
- )
531
-
532
- # Check for unresolved parameters
520
+ # Handle unresolved parameters
533
521
  if unresolved_parameter:
534
- if scope not in ("singleton", "transient"):
522
+ if is_scoped: # pragma: no cover
523
+ # Note: This branch is currently unreachable because
524
+ # unresolved_parameter is only set when is_scoped=False
535
525
  self._resolver.add_unresolved(interface)
536
526
  else:
537
527
  raise LookupError(
@@ -542,11 +532,7 @@ class Container:
542
532
  f"attempting to use it."
543
533
  ) from unresolved_exc
544
534
 
545
- is_coroutine = kind == ProviderKind.COROUTINE
546
- is_generator = kind == ProviderKind.GENERATOR
547
- is_async_generator = kind == ProviderKind.ASYNC_GENERATOR
548
- is_async = is_coroutine or is_async_generator
549
-
535
+ # Create and register provider
550
536
  provider = Provider(
551
537
  call=call,
552
538
  scope=scope,
@@ -557,13 +543,12 @@ class Container:
557
543
  is_coroutine=is_coroutine,
558
544
  is_generator=is_generator,
559
545
  is_async_generator=is_async_generator,
560
- is_async=is_async,
546
+ is_async=is_coroutine or is_async_generator,
561
547
  is_resource=is_resource,
562
548
  )
563
549
 
564
550
  self._set_provider(provider)
565
551
 
566
- # Clear cached resolvers when overriding
567
552
  if override:
568
553
  self._resolver.clear_caches()
569
554
 
anydi/_resolver.py CHANGED
@@ -672,6 +672,8 @@ class Resolver:
672
672
 
673
673
  def _wrap_for_override(self, annotation: Any, value: Any) -> Any:
674
674
  """Hook for wrapping dependencies to enable override patching."""
675
+ if isinstance(value, InstanceProxy):
676
+ return value
675
677
  return InstanceProxy(value, interface=annotation)
676
678
 
677
679
  def _post_resolve_override(self, interface: Any, instance: Any) -> Any: # noqa: C901
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anydi
3
- Version: 0.67.0
3
+ Version: 0.67.1
4
4
  Summary: Dependency Injection library
5
5
  Keywords: dependency injection,dependencies,di,async,asyncio,application
6
6
  Author: Anton Ruhlov
@@ -1,13 +1,13 @@
1
1
  anydi/__init__.py,sha256=KFX8OthKXwBuYDPCV61t-044DpJ88tAOzIxeUWRC5OA,633
2
2
  anydi/_async_lock.py,sha256=3dwZr0KthXFYha0XKMyXf8jMmGb1lYoNC0O5w29V9ic,1104
3
- anydi/_container.py,sha256=_gyNfAddQtp93tjCud2JZDvgoOAM2WuMx4qIvBs7IOs,29878
3
+ anydi/_container.py,sha256=4bv3_fiDw6aPZ-bKjU5jmqeT66CvRnteHbosqMk5R0U,29408
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=1Ux71DhGxu3dLwPJP8gU73olI0pcZ3_tVaVzwKH7100,4411
7
7
  anydi/_marker.py,sha256=xVydjGdkxd_DqqwttnJZRkQbhpCTE9OnrhFmFJMlgvI,3415
8
8
  anydi/_module.py,sha256=2kN5uEXLd2Dsc58gz5IWK43wJewr_QgIVGSO3iWp798,2609
9
9
  anydi/_provider.py,sha256=OV1WFHTYv7W2U0XDk_Kql1r551Vhq8o-pUV5ep1HQcU,1574
10
- anydi/_resolver.py,sha256=ask0W63w4fi1cZFd-3Bq9ael6rkTG01E05TTv81eSII,31394
10
+ anydi/_resolver.py,sha256=6pJS-9F0epCj7goVbc7GeGdpqe0woI3Blu-3dA3jeNs,31464
11
11
  anydi/_scanner.py,sha256=rbRkHzyd2zMu7AFLffN6_tZJcMaW9gy7E-lVdHLHYrs,4294
12
12
  anydi/_types.py,sha256=lsShY_-_CM2EFajeknAYXvLl-rHfopBT8udnK5_BtS4,1161
13
13
  anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -21,7 +21,7 @@ anydi/ext/starlette/middleware.py,sha256=n_JJ7BcG2Mg2M5HwM_SBboxZ-mnnD6WWJn4khq7
21
21
  anydi/ext/typer.py,sha256=z-sDd3jZMPTE2CyEuJ0f9uIJB43FjoLWbjpnkOvqSKA,6236
22
22
  anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  anydi/testing.py,sha256=cHg3mMScZbEep9smRqSNQ81BZMQOkyugHe8TvKdPnEg,1347
24
- anydi-0.67.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
25
- anydi-0.67.0.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
26
- anydi-0.67.0.dist-info/METADATA,sha256=Ave4jzjegADI3S02BtmeX-n42cKFtT9ehXROCTepXYw,8061
27
- anydi-0.67.0.dist-info/RECORD,,
24
+ anydi-0.67.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
25
+ anydi-0.67.1.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
26
+ anydi-0.67.1.dist-info/METADATA,sha256=lqtZFxSRc7ohAAkAAdjcgxQDy7q5FTNP2MtBPZo3c4o,8061
27
+ anydi-0.67.1.dist-info/RECORD,,
File without changes