anydi 0.52.0__py3-none-any.whl → 0.54.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/_container.py +163 -72
- anydi/_context.py +3 -2
- anydi/_provider.py +14 -3
- anydi/_scan.py +3 -3
- anydi/_typing.py +5 -43
- anydi/ext/fastapi.py +3 -2
- anydi/ext/faststream.py +3 -2
- anydi/ext/pytest_plugin.py +8 -8
- anydi/testing.py +3 -4
- {anydi-0.52.0.dist-info → anydi-0.54.0.dist-info}/METADATA +1 -1
- anydi-0.54.0.dist-info/RECORD +24 -0
- anydi-0.52.0.dist-info/RECORD +0 -24
- {anydi-0.52.0.dist-info → anydi-0.54.0.dist-info}/WHEEL +0 -0
- {anydi-0.52.0.dist-info → anydi-0.54.0.dist-info}/entry_points.txt +0 -0
anydi/_container.py
CHANGED
|
@@ -19,14 +19,12 @@ from ._async import run_sync
|
|
|
19
19
|
from ._context import InstanceContext
|
|
20
20
|
from ._decorators import is_provided
|
|
21
21
|
from ._module import ModuleDef, ModuleRegistrar
|
|
22
|
-
from ._provider import Provider, ProviderDef, ProviderKind
|
|
22
|
+
from ._provider import Provider, ProviderDef, ProviderKind, ProviderParameter
|
|
23
23
|
from ._scan import PackageOrIterable, Scanner
|
|
24
24
|
from ._scope import ALLOWED_SCOPES, Scope
|
|
25
25
|
from ._typing import (
|
|
26
26
|
NOT_SET,
|
|
27
27
|
Event,
|
|
28
|
-
get_typed_annotation,
|
|
29
|
-
get_typed_parameters,
|
|
30
28
|
is_async_context_manager,
|
|
31
29
|
is_context_manager,
|
|
32
30
|
is_event_type,
|
|
@@ -264,9 +262,7 @@ class Container:
|
|
|
264
262
|
self._validate_provider_scope(scope, name, kind)
|
|
265
263
|
|
|
266
264
|
# Get the signature
|
|
267
|
-
|
|
268
|
-
module = getattr(call, "__module__", None)
|
|
269
|
-
signature = inspect.signature(call, globals=globalns)
|
|
265
|
+
signature = inspect.signature(call, eval_str=True)
|
|
270
266
|
|
|
271
267
|
# Detect the interface
|
|
272
268
|
if interface is NOT_SET:
|
|
@@ -277,9 +273,6 @@ class Container:
|
|
|
277
273
|
if interface is inspect.Signature.empty:
|
|
278
274
|
interface = None
|
|
279
275
|
|
|
280
|
-
if isinstance(interface, str):
|
|
281
|
-
interface = get_typed_annotation(interface, globalns, module)
|
|
282
|
-
|
|
283
276
|
# If the callable is an iterator, return the actual type
|
|
284
277
|
if is_iterator_type(interface) or is_iterator_type(get_origin(interface)):
|
|
285
278
|
if args := get_args(interface):
|
|
@@ -305,7 +298,7 @@ class Container:
|
|
|
305
298
|
|
|
306
299
|
unresolved_parameter = None
|
|
307
300
|
unresolved_exc: LookupError | None = None
|
|
308
|
-
parameters: list[
|
|
301
|
+
parameters: list[ProviderParameter] = []
|
|
309
302
|
scopes: dict[Scope, Provider] = {}
|
|
310
303
|
|
|
311
304
|
for parameter in signature.parameters.values():
|
|
@@ -320,10 +313,6 @@ class Container:
|
|
|
320
313
|
f"are not allowed in the provider `{name}`."
|
|
321
314
|
)
|
|
322
315
|
|
|
323
|
-
parameter = parameter.replace(
|
|
324
|
-
annotation=get_typed_annotation(parameter.annotation, globalns, module)
|
|
325
|
-
)
|
|
326
|
-
|
|
327
316
|
try:
|
|
328
317
|
sub_provider = self._get_or_register_provider(parameter.annotation)
|
|
329
318
|
except LookupError as exc:
|
|
@@ -337,7 +326,21 @@ class Container:
|
|
|
337
326
|
if sub_provider.scope not in scopes:
|
|
338
327
|
scopes[sub_provider.scope] = sub_provider
|
|
339
328
|
|
|
340
|
-
|
|
329
|
+
default = (
|
|
330
|
+
parameter.default
|
|
331
|
+
if parameter.default is not inspect.Parameter.empty
|
|
332
|
+
else NOT_SET
|
|
333
|
+
)
|
|
334
|
+
parameters.append(
|
|
335
|
+
ProviderParameter(
|
|
336
|
+
name=parameter.name,
|
|
337
|
+
annotation=parameter.annotation,
|
|
338
|
+
default=default,
|
|
339
|
+
has_default=default is not NOT_SET,
|
|
340
|
+
provider=sub_provider,
|
|
341
|
+
shared_scope=sub_provider.scope == scope and scope != "transient",
|
|
342
|
+
)
|
|
343
|
+
)
|
|
341
344
|
|
|
342
345
|
# Check for unresolved parameters
|
|
343
346
|
if unresolved_parameter:
|
|
@@ -367,7 +370,7 @@ class Container:
|
|
|
367
370
|
interface=interface,
|
|
368
371
|
name=name,
|
|
369
372
|
kind=kind,
|
|
370
|
-
parameters=parameters,
|
|
373
|
+
parameters=tuple(parameters),
|
|
371
374
|
)
|
|
372
375
|
|
|
373
376
|
self._set_provider(provider)
|
|
@@ -506,9 +509,36 @@ class Container:
|
|
|
506
509
|
) -> Any:
|
|
507
510
|
"""Internal method to handle instance resolution and creation."""
|
|
508
511
|
provider = self._get_or_register_provider(interface, **defaults)
|
|
512
|
+
return self._resolve_with_provider(provider, create, **defaults)
|
|
513
|
+
|
|
514
|
+
async def _aresolve_or_create(
|
|
515
|
+
self, interface: Any, create: bool, /, **defaults: Any
|
|
516
|
+
) -> Any:
|
|
517
|
+
"""Internal method to handle instance resolution and creation asynchronously."""
|
|
518
|
+
provider = self._get_or_register_provider(interface, **defaults)
|
|
519
|
+
return await self._aresolve_with_provider(provider, create, **defaults)
|
|
520
|
+
|
|
521
|
+
def _resolve_with_provider(
|
|
522
|
+
self, provider: Provider, create: bool, /, **defaults: Any
|
|
523
|
+
) -> Any:
|
|
509
524
|
if provider.scope == "transient":
|
|
510
525
|
return self._create_instance(provider, None, **defaults)
|
|
526
|
+
|
|
527
|
+
if provider.scope == "request":
|
|
528
|
+
context = self._get_request_context()
|
|
529
|
+
if not create:
|
|
530
|
+
cached = context.get(provider.interface, NOT_SET)
|
|
531
|
+
if cached is not NOT_SET:
|
|
532
|
+
return cached
|
|
533
|
+
if not create:
|
|
534
|
+
return self._get_or_create_instance(provider, context)
|
|
535
|
+
return self._create_instance(provider, context, **defaults)
|
|
536
|
+
|
|
511
537
|
context = self._get_instance_context(provider.scope)
|
|
538
|
+
if not create:
|
|
539
|
+
cached = context.get(provider.interface, NOT_SET)
|
|
540
|
+
if cached is not NOT_SET:
|
|
541
|
+
return cached
|
|
512
542
|
with context.lock():
|
|
513
543
|
return (
|
|
514
544
|
self._get_or_create_instance(provider, context)
|
|
@@ -516,14 +546,27 @@ class Container:
|
|
|
516
546
|
else self._create_instance(provider, context, **defaults)
|
|
517
547
|
)
|
|
518
548
|
|
|
519
|
-
async def
|
|
520
|
-
self,
|
|
549
|
+
async def _aresolve_with_provider(
|
|
550
|
+
self, provider: Provider, create: bool, /, **defaults: Any
|
|
521
551
|
) -> Any:
|
|
522
|
-
"""Internal method to handle instance resolution and creation asynchronously."""
|
|
523
|
-
provider = self._get_or_register_provider(interface, **defaults)
|
|
524
552
|
if provider.scope == "transient":
|
|
525
553
|
return await self._acreate_instance(provider, None, **defaults)
|
|
554
|
+
|
|
555
|
+
if provider.scope == "request":
|
|
556
|
+
context = self._get_request_context()
|
|
557
|
+
if not create:
|
|
558
|
+
cached = context.get(provider.interface, NOT_SET)
|
|
559
|
+
if cached is not NOT_SET:
|
|
560
|
+
return cached
|
|
561
|
+
if not create:
|
|
562
|
+
return await self._aget_or_create_instance(provider, context)
|
|
563
|
+
return await self._acreate_instance(provider, context, **defaults)
|
|
564
|
+
|
|
526
565
|
context = self._get_instance_context(provider.scope)
|
|
566
|
+
if not create:
|
|
567
|
+
cached = context.get(provider.interface, NOT_SET)
|
|
568
|
+
if cached is not NOT_SET:
|
|
569
|
+
return cached
|
|
527
570
|
async with context.alock():
|
|
528
571
|
return (
|
|
529
572
|
await self._aget_or_create_instance(provider, context)
|
|
@@ -535,8 +578,8 @@ class Container:
|
|
|
535
578
|
self, provider: Provider, context: InstanceContext
|
|
536
579
|
) -> Any:
|
|
537
580
|
"""Get an instance of a dependency from the scoped context."""
|
|
538
|
-
instance = context.get(provider.interface)
|
|
539
|
-
if instance is
|
|
581
|
+
instance = context.get(provider.interface, NOT_SET)
|
|
582
|
+
if instance is NOT_SET:
|
|
540
583
|
instance = self._create_instance(provider, context)
|
|
541
584
|
context.set(provider.interface, instance)
|
|
542
585
|
return instance
|
|
@@ -546,8 +589,8 @@ class Container:
|
|
|
546
589
|
self, provider: Provider, context: InstanceContext
|
|
547
590
|
) -> Any:
|
|
548
591
|
"""Get an async instance of a dependency from the scoped context."""
|
|
549
|
-
instance = context.get(provider.interface)
|
|
550
|
-
if instance is
|
|
592
|
+
instance = context.get(provider.interface, NOT_SET)
|
|
593
|
+
if instance is NOT_SET:
|
|
551
594
|
instance = await self._acreate_instance(provider, context)
|
|
552
595
|
context.set(provider.interface, instance)
|
|
553
596
|
return instance
|
|
@@ -563,7 +606,9 @@ class Container:
|
|
|
563
606
|
"synchronous mode."
|
|
564
607
|
)
|
|
565
608
|
|
|
566
|
-
provider_kwargs = self._get_provided_kwargs(
|
|
609
|
+
provider_kwargs = self._get_provided_kwargs(
|
|
610
|
+
provider, context, defaults=defaults if defaults else None
|
|
611
|
+
)
|
|
567
612
|
|
|
568
613
|
if provider.is_generator:
|
|
569
614
|
if context is None:
|
|
@@ -581,7 +626,7 @@ class Container:
|
|
|
581
626
|
) -> Any:
|
|
582
627
|
"""Create an instance asynchronously using the provider."""
|
|
583
628
|
provider_kwargs = await self._aget_provided_kwargs(
|
|
584
|
-
provider, context,
|
|
629
|
+
provider, context, defaults=defaults if defaults else None
|
|
585
630
|
)
|
|
586
631
|
|
|
587
632
|
if provider.is_coroutine:
|
|
@@ -615,97 +660,143 @@ class Container:
|
|
|
615
660
|
return instance
|
|
616
661
|
|
|
617
662
|
def _get_provided_kwargs(
|
|
618
|
-
self,
|
|
663
|
+
self,
|
|
664
|
+
provider: Provider,
|
|
665
|
+
context: InstanceContext | None,
|
|
666
|
+
/,
|
|
667
|
+
defaults: dict[str, Any] | None = None,
|
|
619
668
|
) -> dict[str, Any]:
|
|
620
669
|
"""Retrieve the arguments for a provider."""
|
|
621
|
-
|
|
670
|
+
if not provider.parameters:
|
|
671
|
+
return defaults if defaults else {}
|
|
672
|
+
|
|
673
|
+
provided_kwargs = dict(defaults) if defaults else {}
|
|
622
674
|
for parameter in provider.parameters:
|
|
623
675
|
provided_kwargs[parameter.name] = self._get_provider_instance(
|
|
624
|
-
provider,
|
|
676
|
+
provider,
|
|
677
|
+
parameter,
|
|
678
|
+
context,
|
|
679
|
+
defaults=defaults,
|
|
625
680
|
)
|
|
626
|
-
return
|
|
681
|
+
return provided_kwargs
|
|
627
682
|
|
|
628
|
-
def _get_provider_instance(
|
|
683
|
+
def _get_provider_instance( # noqa: C901
|
|
629
684
|
self,
|
|
630
685
|
provider: Provider,
|
|
631
|
-
parameter:
|
|
686
|
+
parameter: ProviderParameter,
|
|
632
687
|
context: InstanceContext | None,
|
|
633
688
|
/,
|
|
634
|
-
|
|
689
|
+
*,
|
|
690
|
+
defaults: dict[str, Any] | None = None,
|
|
635
691
|
) -> Any:
|
|
636
692
|
"""Retrieve an instance of a dependency from the scoped context."""
|
|
637
693
|
|
|
638
|
-
|
|
639
|
-
if parameter.name in defaults:
|
|
694
|
+
if defaults and parameter.name in defaults:
|
|
640
695
|
return defaults[parameter.name]
|
|
641
696
|
|
|
642
|
-
|
|
643
|
-
elif context and parameter.annotation in context:
|
|
644
|
-
instance = context[parameter.annotation]
|
|
697
|
+
sub_provider = parameter.provider
|
|
645
698
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
699
|
+
if context and parameter.shared_scope and sub_provider is not None:
|
|
700
|
+
existing = context.get(sub_provider.interface, NOT_SET)
|
|
701
|
+
if existing is not NOT_SET:
|
|
702
|
+
return existing
|
|
703
|
+
|
|
704
|
+
if context:
|
|
705
|
+
cached = context.get(parameter.annotation, NOT_SET)
|
|
706
|
+
if cached is not NOT_SET:
|
|
707
|
+
return cached
|
|
708
|
+
|
|
709
|
+
sub_provider = parameter.provider
|
|
710
|
+
|
|
711
|
+
if sub_provider:
|
|
712
|
+
if sub_provider.scope == "transient":
|
|
713
|
+
return self._create_instance(sub_provider, None)
|
|
714
|
+
if sub_provider.scope == "singleton" and sub_provider is not provider:
|
|
715
|
+
return self._resolve_with_provider(sub_provider, False)
|
|
716
|
+
|
|
717
|
+
try:
|
|
718
|
+
return self._resolve_parameter(provider, parameter)
|
|
719
|
+
except LookupError:
|
|
720
|
+
if not parameter.has_default:
|
|
721
|
+
raise
|
|
722
|
+
return parameter.default
|
|
655
723
|
|
|
656
724
|
async def _aget_provided_kwargs(
|
|
657
|
-
self,
|
|
725
|
+
self,
|
|
726
|
+
provider: Provider,
|
|
727
|
+
context: InstanceContext | None,
|
|
728
|
+
/,
|
|
729
|
+
defaults: dict[str, Any] | None = None,
|
|
658
730
|
) -> dict[str, Any]:
|
|
659
731
|
"""Asynchronously retrieve the arguments for a provider."""
|
|
660
|
-
|
|
732
|
+
if not provider.parameters:
|
|
733
|
+
return defaults if defaults else {}
|
|
734
|
+
|
|
735
|
+
provided_kwargs = dict(defaults) if defaults else {}
|
|
661
736
|
for parameter in provider.parameters:
|
|
662
737
|
provided_kwargs[parameter.name] = await self._aget_provider_instance(
|
|
663
|
-
provider,
|
|
738
|
+
provider,
|
|
739
|
+
parameter,
|
|
740
|
+
context,
|
|
741
|
+
defaults=defaults,
|
|
664
742
|
)
|
|
665
|
-
return
|
|
743
|
+
return provided_kwargs
|
|
666
744
|
|
|
667
|
-
async def _aget_provider_instance(
|
|
745
|
+
async def _aget_provider_instance( # noqa: C901
|
|
668
746
|
self,
|
|
669
747
|
provider: Provider,
|
|
670
|
-
parameter:
|
|
748
|
+
parameter: ProviderParameter,
|
|
671
749
|
context: InstanceContext | None,
|
|
672
750
|
/,
|
|
673
|
-
|
|
751
|
+
*,
|
|
752
|
+
defaults: dict[str, Any] | None = None,
|
|
674
753
|
) -> Any:
|
|
675
754
|
"""Asynchronously retrieve an instance of a dependency from the context."""
|
|
676
755
|
|
|
677
|
-
|
|
678
|
-
if parameter.name in defaults:
|
|
756
|
+
if defaults and parameter.name in defaults:
|
|
679
757
|
return defaults[parameter.name]
|
|
680
758
|
|
|
681
|
-
|
|
682
|
-
elif context and parameter.annotation in context:
|
|
683
|
-
instance = context[parameter.annotation]
|
|
759
|
+
sub_provider = parameter.provider
|
|
684
760
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
761
|
+
if context and parameter.shared_scope and sub_provider is not None:
|
|
762
|
+
existing = context.get(sub_provider.interface, NOT_SET)
|
|
763
|
+
if existing is not NOT_SET:
|
|
764
|
+
return existing
|
|
765
|
+
|
|
766
|
+
if context:
|
|
767
|
+
cached = context.get(parameter.annotation, NOT_SET)
|
|
768
|
+
if cached is not NOT_SET:
|
|
769
|
+
return cached
|
|
770
|
+
|
|
771
|
+
sub_provider = parameter.provider
|
|
772
|
+
|
|
773
|
+
if sub_provider:
|
|
774
|
+
if sub_provider.scope == "transient":
|
|
775
|
+
return await self._acreate_instance(sub_provider, None)
|
|
776
|
+
if sub_provider.scope == "singleton" and sub_provider is not provider:
|
|
777
|
+
return await self._aresolve_with_provider(sub_provider, False)
|
|
778
|
+
|
|
779
|
+
try:
|
|
780
|
+
return await self._aresolve_parameter(provider, parameter)
|
|
781
|
+
except LookupError:
|
|
782
|
+
if not parameter.has_default:
|
|
783
|
+
raise
|
|
784
|
+
return parameter.default
|
|
694
785
|
|
|
695
786
|
def _resolve_parameter(
|
|
696
|
-
self, provider: Provider, parameter:
|
|
787
|
+
self, provider: Provider, parameter: ProviderParameter
|
|
697
788
|
) -> Any:
|
|
698
789
|
self._validate_resolvable_parameter(provider, parameter)
|
|
699
790
|
return self._resolve_or_create(parameter.annotation, False)
|
|
700
791
|
|
|
701
792
|
async def _aresolve_parameter(
|
|
702
|
-
self, provider: Provider, parameter:
|
|
793
|
+
self, provider: Provider, parameter: ProviderParameter
|
|
703
794
|
) -> Any:
|
|
704
795
|
self._validate_resolvable_parameter(provider, parameter)
|
|
705
796
|
return await self._aresolve_or_create(parameter.annotation, False)
|
|
706
797
|
|
|
707
798
|
def _validate_resolvable_parameter(
|
|
708
|
-
self, provider: Provider, parameter:
|
|
799
|
+
self, provider: Provider, parameter: ProviderParameter
|
|
709
800
|
) -> None:
|
|
710
801
|
"""Ensure that the specified interface is resolved."""
|
|
711
802
|
if parameter.annotation in self._unresolved_interfaces:
|
|
@@ -777,7 +868,7 @@ class Container:
|
|
|
777
868
|
def _get_injected_params(self, call: Callable[..., Any]) -> dict[str, Any]:
|
|
778
869
|
"""Get the injected parameters of a callable object."""
|
|
779
870
|
injected_params: dict[str, Any] = {}
|
|
780
|
-
for parameter in
|
|
871
|
+
for parameter in inspect.signature(call, eval_str=True).parameters.values():
|
|
781
872
|
interface, should_inject = self.validate_injected_parameter(
|
|
782
873
|
parameter, call=call
|
|
783
874
|
)
|
anydi/_context.py
CHANGED
|
@@ -8,6 +8,7 @@ from typing import Any
|
|
|
8
8
|
from typing_extensions import Self
|
|
9
9
|
|
|
10
10
|
from ._async import AsyncRLock, run_sync
|
|
11
|
+
from ._typing import NOT_SET
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class InstanceContext:
|
|
@@ -22,9 +23,9 @@ class InstanceContext:
|
|
|
22
23
|
self._lock = threading.RLock()
|
|
23
24
|
self._async_lock = AsyncRLock()
|
|
24
25
|
|
|
25
|
-
def get(self, interface: Any) -> Any
|
|
26
|
+
def get(self, interface: Any, default: Any = NOT_SET) -> Any:
|
|
26
27
|
"""Get an instance from the context."""
|
|
27
|
-
return self._instances.get(interface)
|
|
28
|
+
return self._instances.get(interface, default)
|
|
28
29
|
|
|
29
30
|
def set(self, interface: Any, value: Any) -> None:
|
|
30
31
|
"""Set an instance in the context."""
|
anydi/_provider.py
CHANGED
|
@@ -5,7 +5,7 @@ import inspect
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from functools import cached_property
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
from ._scope import Scope
|
|
11
11
|
from ._typing import NOT_SET
|
|
@@ -39,13 +39,23 @@ class ProviderKind(enum.IntEnum):
|
|
|
39
39
|
return kind in (cls.GENERATOR, cls.ASYNC_GENERATOR)
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
@dataclass(kw_only=True, frozen=True, slots=True)
|
|
43
|
+
class ProviderParameter:
|
|
44
|
+
name: str
|
|
45
|
+
annotation: Any
|
|
46
|
+
default: Any
|
|
47
|
+
has_default: bool
|
|
48
|
+
provider: Provider | None = None
|
|
49
|
+
shared_scope: bool = False
|
|
50
|
+
|
|
51
|
+
|
|
42
52
|
@dataclass(kw_only=True, frozen=True)
|
|
43
53
|
class Provider:
|
|
44
54
|
call: Callable[..., Any]
|
|
45
55
|
scope: Scope
|
|
46
56
|
interface: Any
|
|
47
57
|
name: str
|
|
48
|
-
parameters:
|
|
58
|
+
parameters: tuple[ProviderParameter, ...]
|
|
49
59
|
kind: ProviderKind
|
|
50
60
|
|
|
51
61
|
def __str__(self) -> str:
|
|
@@ -76,7 +86,8 @@ class Provider:
|
|
|
76
86
|
return ProviderKind.is_resource(self.kind)
|
|
77
87
|
|
|
78
88
|
|
|
79
|
-
|
|
89
|
+
@dataclass(kw_only=True, frozen=True, slots=True)
|
|
90
|
+
class ProviderDef:
|
|
80
91
|
call: Callable[..., Any]
|
|
81
92
|
scope: Scope
|
|
82
93
|
interface: Any = NOT_SET
|
anydi/_scan.py
CHANGED
|
@@ -9,7 +9,7 @@ from types import ModuleType
|
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
11
|
from ._decorators import is_injectable
|
|
12
|
-
from ._typing import
|
|
12
|
+
from ._typing import is_inject_marker
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
15
|
from ._container import Container
|
|
@@ -103,8 +103,8 @@ class Scanner:
|
|
|
103
103
|
# If no tags are passed and not explicitly injectable,
|
|
104
104
|
# check for parameter markers
|
|
105
105
|
if not tags:
|
|
106
|
-
for
|
|
107
|
-
if is_inject_marker(
|
|
106
|
+
for parameter in inspect.signature(member).parameters.values():
|
|
107
|
+
if is_inject_marker(parameter.default):
|
|
108
108
|
return True
|
|
109
109
|
|
|
110
110
|
return False
|
anydi/_typing.py
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
-
from collections.abc import AsyncIterator,
|
|
6
|
+
from collections.abc import AsyncIterator, Iterator
|
|
7
7
|
from types import NoneType
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from typing_extensions import Sentinel
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def is_context_manager(obj: Any) -> bool:
|
|
@@ -28,47 +30,7 @@ def is_iterator_type(tp: Any) -> bool:
|
|
|
28
30
|
return tp in (Iterator, AsyncIterator)
|
|
29
31
|
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
annotation: Any, globalns: dict[str, Any], module: Any = None
|
|
33
|
-
) -> Any:
|
|
34
|
-
"""Get the typed annotation of a callable object."""
|
|
35
|
-
if isinstance(annotation, str):
|
|
36
|
-
ref = ForwardRef(annotation, module=module)
|
|
37
|
-
annotation = ref._evaluate(globalns, globalns, recursive_guard=frozenset()) # type: ignore[reportDeprecated]
|
|
38
|
-
return annotation
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
|
|
42
|
-
"""Get the typed parameters of a callable object."""
|
|
43
|
-
globalns = getattr(obj, "__globals__", {})
|
|
44
|
-
module = getattr(obj, "__module__", None)
|
|
45
|
-
return [
|
|
46
|
-
parameter.replace(
|
|
47
|
-
annotation=get_typed_annotation(
|
|
48
|
-
parameter.annotation, globalns, module=module
|
|
49
|
-
)
|
|
50
|
-
)
|
|
51
|
-
for parameter in inspect.signature(obj).parameters.values()
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class _Sentinel:
|
|
56
|
-
__slots__ = ("_name",)
|
|
57
|
-
|
|
58
|
-
def __init__(self, name: str) -> None:
|
|
59
|
-
self._name = name
|
|
60
|
-
|
|
61
|
-
def __repr__(self) -> str:
|
|
62
|
-
return f"<{self._name}>"
|
|
63
|
-
|
|
64
|
-
def __eq__(self, other: object) -> bool:
|
|
65
|
-
return self is other
|
|
66
|
-
|
|
67
|
-
def __hash__(self) -> int:
|
|
68
|
-
return id(self)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
NOT_SET = _Sentinel("NOT_SET")
|
|
33
|
+
NOT_SET = Sentinel("NOT_SET")
|
|
72
34
|
|
|
73
35
|
|
|
74
36
|
class InjectMarker:
|
anydi/ext/fastapi.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import inspect
|
|
5
6
|
from collections.abc import Iterator
|
|
6
7
|
from typing import Annotated, Any, cast
|
|
7
8
|
|
|
@@ -11,7 +12,7 @@ from fastapi.routing import APIRoute
|
|
|
11
12
|
from starlette.requests import Request
|
|
12
13
|
|
|
13
14
|
from anydi._container import Container
|
|
14
|
-
from anydi._typing import InjectMarker
|
|
15
|
+
from anydi._typing import InjectMarker
|
|
15
16
|
|
|
16
17
|
from .starlette.middleware import RequestScopedMiddleware
|
|
17
18
|
|
|
@@ -39,7 +40,7 @@ def install(app: FastAPI, container: Container) -> None:
|
|
|
39
40
|
call, *params = dependant.cache_key
|
|
40
41
|
if not call:
|
|
41
42
|
continue # pragma: no cover
|
|
42
|
-
for parameter in
|
|
43
|
+
for parameter in inspect.signature(call, eval_str=True).parameters.values():
|
|
43
44
|
container.validate_injected_parameter(parameter, call=call)
|
|
44
45
|
|
|
45
46
|
|
anydi/ext/faststream.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import inspect
|
|
5
6
|
from typing import Any, cast
|
|
6
7
|
|
|
7
8
|
from fast_depends.dependencies import Depends
|
|
@@ -9,7 +10,7 @@ from faststream import ContextRepo
|
|
|
9
10
|
from faststream.broker.core.usecase import BrokerUsecase
|
|
10
11
|
|
|
11
12
|
from anydi import Container
|
|
12
|
-
from anydi._typing import InjectMarker
|
|
13
|
+
from anydi._typing import InjectMarker
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
|
|
@@ -23,7 +24,7 @@ def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
|
|
|
23
24
|
|
|
24
25
|
for handler in _get_broken_handlers(broker):
|
|
25
26
|
call = handler._original_call # noqa
|
|
26
|
-
for parameter in
|
|
27
|
+
for parameter in inspect.signature(call, eval_str=True).parameters.values():
|
|
27
28
|
container.validate_injected_parameter(parameter, call=call)
|
|
28
29
|
|
|
29
30
|
|
anydi/ext/pytest_plugin.py
CHANGED
|
@@ -7,9 +7,9 @@ from typing import Any, cast
|
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
from anyio.pytest_plugin import extract_backend_and_options, get_runner
|
|
10
|
+
from typing_extensions import get_annotations
|
|
10
11
|
|
|
11
12
|
from anydi import Container
|
|
12
|
-
from anydi._typing import get_typed_parameters
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
@@ -61,14 +61,14 @@ def _anydi_injected_parameter_iterator(
|
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
def _iterator() -> Iterator[tuple[str, inspect.Parameter]]:
|
|
64
|
-
for
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
or parameter.name not in fixturenames
|
|
69
|
-
):
|
|
64
|
+
for name, annotation in get_annotations(
|
|
65
|
+
request.function, eval_str=True
|
|
66
|
+
).items():
|
|
67
|
+
if name == "return":
|
|
70
68
|
continue
|
|
71
|
-
|
|
69
|
+
if name not in fixturenames:
|
|
70
|
+
continue
|
|
71
|
+
yield name, annotation
|
|
72
72
|
|
|
73
73
|
return _iterator
|
|
74
74
|
|
anydi/testing.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
-
import inspect
|
|
3
2
|
import logging
|
|
4
3
|
from collections.abc import Iterable, Iterator, Sequence
|
|
5
4
|
from typing import Any, TypeVar
|
|
@@ -10,7 +9,7 @@ from typing_extensions import Self, type_repr
|
|
|
10
9
|
from ._container import Container
|
|
11
10
|
from ._context import InstanceContext
|
|
12
11
|
from ._module import ModuleDef
|
|
13
|
-
from ._provider import Provider, ProviderDef
|
|
12
|
+
from ._provider import Provider, ProviderDef, ProviderParameter
|
|
14
13
|
from ._scope import Scope
|
|
15
14
|
|
|
16
15
|
T = TypeVar("T")
|
|
@@ -76,7 +75,7 @@ class TestContainer(Container):
|
|
|
76
75
|
def _get_provider_instance(
|
|
77
76
|
self,
|
|
78
77
|
provider: Provider,
|
|
79
|
-
parameter:
|
|
78
|
+
parameter: ProviderParameter,
|
|
80
79
|
context: InstanceContext | None,
|
|
81
80
|
/,
|
|
82
81
|
**defaults: Any,
|
|
@@ -90,7 +89,7 @@ class TestContainer(Container):
|
|
|
90
89
|
async def _aget_provider_instance(
|
|
91
90
|
self,
|
|
92
91
|
provider: Provider,
|
|
93
|
-
parameter:
|
|
92
|
+
parameter: ProviderParameter,
|
|
94
93
|
context: InstanceContext | None,
|
|
95
94
|
/,
|
|
96
95
|
**defaults: Any,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
anydi/__init__.py,sha256=KjjYm-1yAFxiPYaMs1WRJMtxE0q_vdX7ZRLQR1fFGs8,567
|
|
2
|
+
anydi/_async.py,sha256=lrKTrqMC81POFbea3kaC9KkY3sp0DaenuVPvwOg88xA,1472
|
|
3
|
+
anydi/_container.py,sha256=zNDS22G52JlCPvrRM0fD4FvzP3rvvipqYyRhDXcMjKU,35233
|
|
4
|
+
anydi/_context.py,sha256=_9hKjimFZhnVs_lllon0111az8-KtwzBRpxnNoOy3Z0,3075
|
|
5
|
+
anydi/_decorators.py,sha256=yxY876_2fzC30UIb4-4mR-J5ToQN3BH61E2cFH-iX8Y,2809
|
|
6
|
+
anydi/_module.py,sha256=2kN5uEXLd2Dsc58gz5IWK43wJewr_QgIVGSO3iWp798,2609
|
|
7
|
+
anydi/_provider.py,sha256=Ik3Js0hwGvdz_cOW6yHXPmOOzhfkNRuiHgFnhIshlZM,2400
|
|
8
|
+
anydi/_scan.py,sha256=BLayc0NY_NYicr-ApX7Ztco9sd0izSZKGB-pUktui_s,3637
|
|
9
|
+
anydi/_scope.py,sha256=PFHjPb2-n0vhRo9mvD_craTFfoJBzR3y-N3_0apL5Q0,258
|
|
10
|
+
anydi/_typing.py,sha256=I0Qt3Ec6ki6ZXf0Krm-LUIPRvzvyUUHfKdvmQijBErQ,1718
|
|
11
|
+
anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
anydi/ext/django/__init__.py,sha256=Ve8lncLU9dPY_Vjt4zihPgsSxwAtFHACn0XvBM5JG8k,367
|
|
13
|
+
anydi/ext/fastapi.py,sha256=pdYumpXMAw2VjlDlb8u3R_RQi6htRieiZKsSA-9BSdA,2349
|
|
14
|
+
anydi/ext/faststream.py,sha256=CEuGjVZRbgSjkeV1rSp6xWggGYoKnBDeo3PiPgKtxuc,1930
|
|
15
|
+
anydi/ext/pydantic_settings.py,sha256=0GQjw7QpQlT5p6GxFClXYdtc6J42PClmAnRWPEzMjvY,1488
|
|
16
|
+
anydi/ext/pytest_plugin.py,sha256=Es1K1S6_2gIdTUYkbw2d1aZcHnjJutGFafVsLPGcVJc,4684
|
|
17
|
+
anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
anydi/ext/starlette/middleware.py,sha256=MxnzshAs-CMvjJp0r457k52MzBL8O4KAuClnF6exBdU,803
|
|
19
|
+
anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
anydi/testing.py,sha256=X_zQqwqhqTbzp4YL9b9b2CQ0ag7gHO1cH-ASRVmVFoU,5523
|
|
21
|
+
anydi-0.54.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
22
|
+
anydi-0.54.0.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
|
|
23
|
+
anydi-0.54.0.dist-info/METADATA,sha256=tiwslLdSguquugx0-UrYjJhH8k2YL1Gc7zEN8keUk4w,5022
|
|
24
|
+
anydi-0.54.0.dist-info/RECORD,,
|
anydi-0.52.0.dist-info/RECORD
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
anydi/__init__.py,sha256=KjjYm-1yAFxiPYaMs1WRJMtxE0q_vdX7ZRLQR1fFGs8,567
|
|
2
|
-
anydi/_async.py,sha256=lrKTrqMC81POFbea3kaC9KkY3sp0DaenuVPvwOg88xA,1472
|
|
3
|
-
anydi/_container.py,sha256=t-5BcjsuKnaJ8Wmk9ZXXQN90NQhrVl41bLjObG0wYTY,31940
|
|
4
|
-
anydi/_context.py,sha256=_Xy8cTpRskb4cxTd-Fe-5NnIZyBe1DnovkofhdeUfmw,3020
|
|
5
|
-
anydi/_decorators.py,sha256=yxY876_2fzC30UIb4-4mR-J5ToQN3BH61E2cFH-iX8Y,2809
|
|
6
|
-
anydi/_module.py,sha256=2kN5uEXLd2Dsc58gz5IWK43wJewr_QgIVGSO3iWp798,2609
|
|
7
|
-
anydi/_provider.py,sha256=BWW4nXJ5r-0tCgS91kLWzYbrGv1v1oIm8BSt1cyYkDc,2150
|
|
8
|
-
anydi/_scan.py,sha256=SoB9uugaZVqBIW2IMKQGbu9YGaWHT3BhKWrDpiQaINE,3634
|
|
9
|
-
anydi/_scope.py,sha256=PFHjPb2-n0vhRo9mvD_craTFfoJBzR3y-N3_0apL5Q0,258
|
|
10
|
-
anydi/_typing.py,sha256=LXOmWua-UimL9te8Ye3u3MCLiA7EzNhDuacw4kgnxP4,2877
|
|
11
|
-
anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
anydi/ext/django/__init__.py,sha256=Ve8lncLU9dPY_Vjt4zihPgsSxwAtFHACn0XvBM5JG8k,367
|
|
13
|
-
anydi/ext/fastapi.py,sha256=L9VGPHGy23se1sflmJqTE7LNfQuElVdYEogoT1f5-4A,2324
|
|
14
|
-
anydi/ext/faststream.py,sha256=Dy81Vf34CP6pEIbZ-41vh_-Dn6Qc-rcf14U5poebjxI,1905
|
|
15
|
-
anydi/ext/pydantic_settings.py,sha256=0GQjw7QpQlT5p6GxFClXYdtc6J42PClmAnRWPEzMjvY,1488
|
|
16
|
-
anydi/ext/pytest_plugin.py,sha256=yqaFPzYD88HEmqc2URsBtp2FUktORhUxWfs0hJja1IU,4732
|
|
17
|
-
anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
anydi/ext/starlette/middleware.py,sha256=MxnzshAs-CMvjJp0r457k52MzBL8O4KAuClnF6exBdU,803
|
|
19
|
-
anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
anydi/testing.py,sha256=gbQnJ6qFaQ5qbUwFWfSJ67SKhxLuPzwLGidistsmHk8,5519
|
|
21
|
-
anydi-0.52.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
22
|
-
anydi-0.52.0.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
|
|
23
|
-
anydi-0.52.0.dist-info/METADATA,sha256=GvHhlEF-L72OAvA-6fuFzjUnvOlvM16T8MpZXT1vSbA,5022
|
|
24
|
-
anydi-0.52.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|