anydi 0.54.0__py3-none-any.whl → 0.55.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/__init__.py CHANGED
@@ -4,8 +4,7 @@ from ._container import Container
4
4
  from ._decorators import injectable, provided, provider, request, singleton, transient
5
5
  from ._module import Module
6
6
  from ._provider import ProviderDef as Provider
7
- from ._scope import Scope
8
- from ._typing import Inject
7
+ from ._types import Inject, Scope
9
8
 
10
9
  # Alias for dependency auto marker
11
10
  # TODO: deprecate it
@@ -1,18 +1,8 @@
1
- import functools
2
- from collections.abc import Callable
3
1
  from types import TracebackType
4
- from typing import Any, TypeVar
2
+ from typing import Any
5
3
 
6
- import anyio.to_thread
7
- from typing_extensions import ParamSpec, Self
8
-
9
- T = TypeVar("T")
10
- P = ParamSpec("P")
11
-
12
-
13
- async def run_sync(func: Callable[P, T], /, *args: P.args, **kwargs: P.kwargs) -> T:
14
- """Runs the given function asynchronously using the `anyio` library."""
15
- return await anyio.to_thread.run_sync(functools.partial(func, *args, **kwargs))
4
+ import anyio
5
+ from typing_extensions import Self
16
6
 
17
7
 
18
8
  class AsyncRLock:
anydi/_container.py CHANGED
@@ -15,18 +15,16 @@ from typing import Annotated, Any, TypeVar, cast, get_args, get_origin, overload
15
15
 
16
16
  from typing_extensions import ParamSpec, Self, type_repr
17
17
 
18
- from ._async import run_sync
19
18
  from ._context import InstanceContext
20
19
  from ._decorators import is_provided
21
20
  from ._module import ModuleDef, ModuleRegistrar
22
21
  from ._provider import Provider, ProviderDef, ProviderKind, ProviderParameter
23
- from ._scan import PackageOrIterable, Scanner
24
- from ._scope import ALLOWED_SCOPES, Scope
25
- from ._typing import (
22
+ from ._resolver import Resolver
23
+ from ._scanner import PackageOrIterable, Scanner
24
+ from ._types import (
26
25
  NOT_SET,
27
26
  Event,
28
- is_async_context_manager,
29
- is_context_manager,
27
+ Scope,
30
28
  is_event_type,
31
29
  is_inject_marker,
32
30
  is_iterator_type,
@@ -36,6 +34,12 @@ from ._typing import (
36
34
  T = TypeVar("T", bound=Any)
37
35
  P = ParamSpec("P")
38
36
 
37
+ ALLOWED_SCOPES: dict[Scope, list[Scope]] = {
38
+ "singleton": ["singleton"],
39
+ "request": ["request", "singleton"],
40
+ "transient": ["transient", "request", "singleton"],
41
+ }
42
+
39
43
 
40
44
  class Container:
41
45
  """AnyDI is a dependency injection container."""
@@ -54,10 +58,10 @@ class Container:
54
58
  self._request_context_var: ContextVar[InstanceContext | None] = ContextVar(
55
59
  "request_context", default=None
56
60
  )
57
- self._unresolved_interfaces: set[Any] = set()
58
61
  self._inject_cache: dict[Callable[..., Any], Callable[..., Any]] = {}
59
62
 
60
63
  # Components
64
+ self._resolver = Resolver(self)
61
65
  self._modules = ModuleRegistrar(self)
62
66
  self._scanner = Scanner(self)
63
67
 
@@ -75,9 +79,7 @@ class Container:
75
79
  for module in modules:
76
80
  self.register_module(module)
77
81
 
78
- ############################
79
- # Properties
80
- ############################
82
+ # == Container Properties ==
81
83
 
82
84
  @property
83
85
  def providers(self) -> dict[type[Any], Provider]:
@@ -89,9 +91,7 @@ class Container:
89
91
  """Get the logger instance."""
90
92
  return self._logger
91
93
 
92
- ############################
93
- # Lifespan/Context Methods
94
- ############################
94
+ # == Context & Lifespan Management ==
95
95
 
96
96
  def __enter__(self) -> Self:
97
97
  """Enter the singleton context."""
@@ -190,9 +190,7 @@ class Container:
190
190
  return self._singleton_context
191
191
  return self._get_request_context()
192
192
 
193
- ############################
194
- # Provider Methods
195
- ############################
193
+ # == Provider Registry ==
196
194
 
197
195
  def register(
198
196
  self,
@@ -251,22 +249,23 @@ class Container:
251
249
  scope: Scope,
252
250
  interface: Any = NOT_SET,
253
251
  override: bool = False,
254
- /,
255
- **defaults: Any,
252
+ defaults: dict[str, Any] | None = None,
256
253
  ) -> Provider:
257
254
  """Register a provider with the specified scope."""
258
255
  name = type_repr(call)
259
256
  kind = ProviderKind.from_call(call)
257
+ is_class = kind == ProviderKind.CLASS
258
+ is_resource = kind in (ProviderKind.GENERATOR, ProviderKind.ASYNC_GENERATOR)
260
259
 
261
260
  # Validate scope if it provided
262
- self._validate_provider_scope(scope, name, kind)
261
+ self._validate_provider_scope(scope, name, is_resource)
263
262
 
264
263
  # Get the signature
265
264
  signature = inspect.signature(call, eval_str=True)
266
265
 
267
266
  # Detect the interface
268
267
  if interface is NOT_SET:
269
- if kind == ProviderKind.CLASS:
268
+ if is_class:
270
269
  interface = call
271
270
  else:
272
271
  interface = signature.return_annotation
@@ -313,10 +312,17 @@ class Container:
313
312
  f"are not allowed in the provider `{name}`."
314
313
  )
315
314
 
315
+ default = (
316
+ parameter.default
317
+ if parameter.default is not inspect.Parameter.empty
318
+ else NOT_SET
319
+ )
320
+ has_default = default is not NOT_SET
321
+
316
322
  try:
317
323
  sub_provider = self._get_or_register_provider(parameter.annotation)
318
324
  except LookupError as exc:
319
- if self._parameter_has_default(parameter, **defaults):
325
+ if parameter.name in defaults if defaults else False or has_default:
320
326
  continue
321
327
  unresolved_parameter = parameter
322
328
  unresolved_exc = exc
@@ -326,26 +332,20 @@ class Container:
326
332
  if sub_provider.scope not in scopes:
327
333
  scopes[sub_provider.scope] = sub_provider
328
334
 
329
- default = (
330
- parameter.default
331
- if parameter.default is not inspect.Parameter.empty
332
- else NOT_SET
333
- )
334
335
  parameters.append(
335
336
  ProviderParameter(
336
337
  name=parameter.name,
337
338
  annotation=parameter.annotation,
338
339
  default=default,
339
- has_default=default is not NOT_SET,
340
+ has_default=has_default,
340
341
  provider=sub_provider,
341
- shared_scope=sub_provider.scope == scope and scope != "transient",
342
342
  )
343
343
  )
344
344
 
345
345
  # Check for unresolved parameters
346
346
  if unresolved_parameter:
347
347
  if scope not in ("singleton", "transient"):
348
- self._unresolved_interfaces.add(interface)
348
+ self._resolver.add_unresolved(interface)
349
349
  else:
350
350
  raise LookupError(
351
351
  f"The provider `{name}` depends on `{unresolved_parameter.name}` "
@@ -364,20 +364,30 @@ class Container:
364
364
  "Please ensure all providers are registered with matching scopes."
365
365
  )
366
366
 
367
+ is_coroutine = kind == ProviderKind.COROUTINE
368
+ is_generator = kind == ProviderKind.GENERATOR
369
+ is_async_generator = kind == ProviderKind.ASYNC_GENERATOR
370
+ is_async = is_coroutine or is_async_generator
371
+
367
372
  provider = Provider(
368
373
  call=call,
369
374
  scope=scope,
370
375
  interface=interface,
371
376
  name=name,
372
- kind=kind,
373
377
  parameters=tuple(parameters),
378
+ is_class=is_class,
379
+ is_coroutine=is_coroutine,
380
+ is_generator=is_generator,
381
+ is_async_generator=is_async_generator,
382
+ is_async=is_async,
383
+ is_resource=is_resource,
374
384
  )
375
385
 
376
386
  self._set_provider(provider)
377
387
  return provider
378
388
 
379
389
  @staticmethod
380
- def _validate_provider_scope(scope: Scope, name: str, kind: ProviderKind) -> None:
390
+ def _validate_provider_scope(scope: Scope, name: str, is_resource: bool) -> None:
381
391
  """Validate the provider scope."""
382
392
  if scope not in (allowed_scopes := get_args(Scope)):
383
393
  raise ValueError(
@@ -385,7 +395,7 @@ class Container:
385
395
  f"scopes are supported: {', '.join(allowed_scopes)}. "
386
396
  "Please use one of the supported scopes when registering a provider."
387
397
  )
388
- if scope == "transient" and ProviderKind.is_resource(kind):
398
+ if scope == "transient" and is_resource:
389
399
  raise TypeError(
390
400
  f"The resource provider `{name}` is attempting to register "
391
401
  "with a transient scope, which is not allowed."
@@ -402,7 +412,9 @@ class Container:
402
412
  "properly registered before attempting to use it."
403
413
  ) from exc
404
414
 
405
- def _get_or_register_provider(self, interface: Any, /, **defaults: Any) -> Provider:
415
+ def _get_or_register_provider(
416
+ self, interface: Any, defaults: dict[str, Any] | None = None
417
+ ) -> Provider:
406
418
  """Get or register a provider by interface."""
407
419
  try:
408
420
  return self._providers[interface]
@@ -412,7 +424,8 @@ class Container:
412
424
  interface,
413
425
  interface.__provided__["scope"],
414
426
  NOT_SET,
415
- **defaults,
427
+ False,
428
+ defaults,
416
429
  )
417
430
  raise LookupError(
418
431
  f"The provider interface `{type_repr(interface)}` is either not "
@@ -434,17 +447,7 @@ class Container:
434
447
  if provider.is_resource:
435
448
  self._resources[provider.scope].remove(provider.interface)
436
449
 
437
- @staticmethod
438
- def _parameter_has_default(
439
- parameter: inspect.Parameter, /, **defaults: Any
440
- ) -> bool:
441
- has_default_in_kwargs = parameter.name in defaults if defaults else False
442
- has_default = parameter.default is not inspect.Parameter.empty
443
- return has_default_in_kwargs or has_default
444
-
445
- ############################
446
- # Instance Methods
447
- ############################
450
+ # == Instance Resolution ==
448
451
 
449
452
  @overload
450
453
  def resolve(self, interface: type[T]) -> T: ...
@@ -453,8 +456,14 @@ class Container:
453
456
  def resolve(self, interface: T) -> T: ... # type: ignore
454
457
 
455
458
  def resolve(self, interface: type[T]) -> T:
456
- """Resolve an instance by interface."""
457
- return self._resolve_or_create(interface, False)
459
+ """Resolve an instance by interface using compiled sync resolver."""
460
+ cached = self._resolver.get_cached(interface, is_async=False)
461
+ if cached is not None:
462
+ return cached.resolve(self)
463
+
464
+ provider = self._get_or_register_provider(interface)
465
+ compiled = self._resolver.compile(provider, is_async=False)
466
+ return compiled.resolve(self)
458
467
 
459
468
  @overload
460
469
  async def aresolve(self, interface: type[T]) -> T: ...
@@ -464,15 +473,35 @@ class Container:
464
473
 
465
474
  async def aresolve(self, interface: type[T]) -> T:
466
475
  """Resolve an instance by interface asynchronously."""
467
- return await self._aresolve_or_create(interface, False)
476
+ cached = self._resolver.get_cached(interface, is_async=True)
477
+ if cached is not None:
478
+ return await cached.resolve(self)
479
+
480
+ provider = self._get_or_register_provider(interface)
481
+ compiled = self._resolver.compile(provider, is_async=True)
482
+ return await compiled.resolve(self)
468
483
 
469
484
  def create(self, interface: type[T], /, **defaults: Any) -> T:
470
485
  """Create an instance by interface."""
471
- return self._resolve_or_create(interface, True, **defaults)
486
+ if not defaults:
487
+ cached = self._resolver.get_cached(interface, is_async=False)
488
+ if cached is not None:
489
+ return cached.create(self, None)
490
+
491
+ provider = self._get_or_register_provider(interface, defaults)
492
+ compiled = self._resolver.compile(provider, is_async=False)
493
+ return compiled.create(self, defaults or None)
472
494
 
473
495
  async def acreate(self, interface: type[T], /, **defaults: Any) -> T:
474
496
  """Create an instance by interface asynchronously."""
475
- return await self._aresolve_or_create(interface, True, **defaults)
497
+ if not defaults:
498
+ cached = self._resolver.get_cached(interface, is_async=True)
499
+ if cached is not None:
500
+ return await cached.create(self, None)
501
+
502
+ provider = self._get_or_register_provider(interface, defaults)
503
+ compiled = self._resolver.compile(provider, is_async=True)
504
+ return await compiled.create(self, defaults or None)
476
505
 
477
506
  def is_resolved(self, interface: Any) -> bool:
478
507
  """Check if an instance by interface exists."""
@@ -504,312 +533,7 @@ class Container:
504
533
  continue
505
534
  del context[interface]
506
535
 
507
- def _resolve_or_create(
508
- self, interface: Any, create: bool, /, **defaults: Any
509
- ) -> Any:
510
- """Internal method to handle instance resolution and creation."""
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:
524
- if provider.scope == "transient":
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
-
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
542
- with context.lock():
543
- return (
544
- self._get_or_create_instance(provider, context)
545
- if not create
546
- else self._create_instance(provider, context, **defaults)
547
- )
548
-
549
- async def _aresolve_with_provider(
550
- self, provider: Provider, create: bool, /, **defaults: Any
551
- ) -> Any:
552
- if provider.scope == "transient":
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
-
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
570
- async with context.alock():
571
- return (
572
- await self._aget_or_create_instance(provider, context)
573
- if not create
574
- else await self._acreate_instance(provider, context, **defaults)
575
- )
576
-
577
- def _get_or_create_instance(
578
- self, provider: Provider, context: InstanceContext
579
- ) -> Any:
580
- """Get an instance of a dependency from the scoped context."""
581
- instance = context.get(provider.interface, NOT_SET)
582
- if instance is NOT_SET:
583
- instance = self._create_instance(provider, context)
584
- context.set(provider.interface, instance)
585
- return instance
586
- return instance
587
-
588
- async def _aget_or_create_instance(
589
- self, provider: Provider, context: InstanceContext
590
- ) -> Any:
591
- """Get an async instance of a dependency from the scoped context."""
592
- instance = context.get(provider.interface, NOT_SET)
593
- if instance is NOT_SET:
594
- instance = await self._acreate_instance(provider, context)
595
- context.set(provider.interface, instance)
596
- return instance
597
- return instance
598
-
599
- def _create_instance(
600
- self, provider: Provider, context: InstanceContext | None, /, **defaults: Any
601
- ) -> Any:
602
- """Create an instance using the provider."""
603
- if provider.is_async:
604
- raise TypeError(
605
- f"The instance for the provider `{provider}` cannot be created in "
606
- "synchronous mode."
607
- )
608
-
609
- provider_kwargs = self._get_provided_kwargs(
610
- provider, context, defaults=defaults if defaults else None
611
- )
612
-
613
- if provider.is_generator:
614
- if context is None:
615
- raise ValueError("The context is required for generator providers.")
616
- cm = contextlib.contextmanager(provider.call)(**provider_kwargs)
617
- return context.enter(cm)
618
-
619
- instance = provider.call(**provider_kwargs)
620
- if context is not None and provider.is_class and is_context_manager(instance):
621
- context.enter(instance)
622
- return instance
623
-
624
- async def _acreate_instance(
625
- self, provider: Provider, context: InstanceContext | None, /, **defaults: Any
626
- ) -> Any:
627
- """Create an instance asynchronously using the provider."""
628
- provider_kwargs = await self._aget_provided_kwargs(
629
- provider, context, defaults=defaults if defaults else None
630
- )
631
-
632
- if provider.is_coroutine:
633
- return await provider.call(**provider_kwargs)
634
-
635
- if provider.is_async_generator:
636
- if context is None:
637
- raise ValueError(
638
- "The async stack is required for async generator providers."
639
- )
640
- cm = contextlib.asynccontextmanager(provider.call)(**provider_kwargs)
641
- return await context.aenter(cm)
642
-
643
- if provider.is_generator:
644
-
645
- def _create() -> Any:
646
- if context is None:
647
- raise ValueError("The stack is required for generator providers.")
648
- cm = contextlib.contextmanager(provider.call)(**provider_kwargs)
649
- return context.enter(cm)
650
-
651
- return await run_sync(_create)
652
-
653
- instance = await run_sync(provider.call, **provider_kwargs)
654
- if (
655
- context is not None
656
- and provider.is_class
657
- and is_async_context_manager(instance)
658
- ):
659
- await context.aenter(instance)
660
- return instance
661
-
662
- def _get_provided_kwargs(
663
- self,
664
- provider: Provider,
665
- context: InstanceContext | None,
666
- /,
667
- defaults: dict[str, Any] | None = None,
668
- ) -> dict[str, Any]:
669
- """Retrieve the arguments for a provider."""
670
- if not provider.parameters:
671
- return defaults if defaults else {}
672
-
673
- provided_kwargs = dict(defaults) if defaults else {}
674
- for parameter in provider.parameters:
675
- provided_kwargs[parameter.name] = self._get_provider_instance(
676
- provider,
677
- parameter,
678
- context,
679
- defaults=defaults,
680
- )
681
- return provided_kwargs
682
-
683
- def _get_provider_instance( # noqa: C901
684
- self,
685
- provider: Provider,
686
- parameter: ProviderParameter,
687
- context: InstanceContext | None,
688
- /,
689
- *,
690
- defaults: dict[str, Any] | None = None,
691
- ) -> Any:
692
- """Retrieve an instance of a dependency from the scoped context."""
693
-
694
- if defaults and parameter.name in defaults:
695
- return defaults[parameter.name]
696
-
697
- sub_provider = parameter.provider
698
-
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
723
-
724
- async def _aget_provided_kwargs(
725
- self,
726
- provider: Provider,
727
- context: InstanceContext | None,
728
- /,
729
- defaults: dict[str, Any] | None = None,
730
- ) -> dict[str, Any]:
731
- """Asynchronously retrieve the arguments for a provider."""
732
- if not provider.parameters:
733
- return defaults if defaults else {}
734
-
735
- provided_kwargs = dict(defaults) if defaults else {}
736
- for parameter in provider.parameters:
737
- provided_kwargs[parameter.name] = await self._aget_provider_instance(
738
- provider,
739
- parameter,
740
- context,
741
- defaults=defaults,
742
- )
743
- return provided_kwargs
744
-
745
- async def _aget_provider_instance( # noqa: C901
746
- self,
747
- provider: Provider,
748
- parameter: ProviderParameter,
749
- context: InstanceContext | None,
750
- /,
751
- *,
752
- defaults: dict[str, Any] | None = None,
753
- ) -> Any:
754
- """Asynchronously retrieve an instance of a dependency from the context."""
755
-
756
- if defaults and parameter.name in defaults:
757
- return defaults[parameter.name]
758
-
759
- sub_provider = parameter.provider
760
-
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
785
-
786
- def _resolve_parameter(
787
- self, provider: Provider, parameter: ProviderParameter
788
- ) -> Any:
789
- self._validate_resolvable_parameter(provider, parameter)
790
- return self._resolve_or_create(parameter.annotation, False)
791
-
792
- async def _aresolve_parameter(
793
- self, provider: Provider, parameter: ProviderParameter
794
- ) -> Any:
795
- self._validate_resolvable_parameter(provider, parameter)
796
- return await self._aresolve_or_create(parameter.annotation, False)
797
-
798
- def _validate_resolvable_parameter(
799
- self, provider: Provider, parameter: ProviderParameter
800
- ) -> None:
801
- """Ensure that the specified interface is resolved."""
802
- if parameter.annotation in self._unresolved_interfaces:
803
- raise LookupError(
804
- f"You are attempting to get the parameter `{parameter.name}` with the "
805
- f"annotation `{type_repr(parameter.annotation)}` as a "
806
- f"dependency into `{type_repr(provider.call)}` which is "
807
- "not registered or set in the scoped context."
808
- )
809
-
810
- ############################
811
- # Injector Methods
812
- ############################
536
+ # == Injection Utilities ==
813
537
 
814
538
  @overload
815
539
  def inject(self, func: Callable[P, T]) -> Callable[P, T]: ...
@@ -933,26 +657,20 @@ class Container:
933
657
 
934
658
  return interface, True
935
659
 
936
- ############################
937
- # Module Methods
938
- ############################
660
+ # == Module Registration ==
939
661
 
940
662
  def register_module(self, module: ModuleDef) -> None:
941
663
  """Register a module as a callable, module type, or module instance."""
942
664
  self._modules.register(module)
943
665
 
944
- ############################
945
- # Scanner Methods
946
- ############################
666
+ # == Package Scanning ==
947
667
 
948
668
  def scan(
949
669
  self, /, packages: PackageOrIterable, *, tags: Iterable[str] | None = None
950
670
  ) -> None:
951
671
  self._scanner.scan(packages=packages, tags=tags)
952
672
 
953
- ############################
954
- # Testing
955
- ############################
673
+ # == Testing ==
956
674
 
957
675
  @contextlib.contextmanager
958
676
  def override(self, interface: Any, instance: Any) -> Iterator[None]: