anydi 0.36.1a5__py3-none-any.whl → 0.37.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
@@ -36,7 +36,9 @@ from ._utils import (
36
36
  get_full_qualname,
37
37
  get_typed_parameters,
38
38
  import_string,
39
+ is_async_context_manager,
39
40
  is_builtin_type,
41
+ is_context_manager,
40
42
  run_async,
41
43
  )
42
44
 
@@ -553,7 +555,10 @@ class Container:
553
555
  cm = contextlib.contextmanager(provider.call)(**provider_kwargs)
554
556
  return context.enter(cm)
555
557
 
556
- return provider.call(**provider_kwargs)
558
+ instance = provider.call(**provider_kwargs)
559
+ if context is not None and provider.is_class and is_context_manager(instance):
560
+ context.enter(instance)
561
+ return instance
557
562
 
558
563
  async def _acreate_instance(
559
564
  self, provider: Provider, context: InstanceContext | None, /, **defaults: Any
@@ -584,7 +589,14 @@ class Container:
584
589
 
585
590
  return await run_async(_create)
586
591
 
587
- return await run_async(provider.call, **provider_kwargs)
592
+ instance = await run_async(provider.call, **provider_kwargs)
593
+ if (
594
+ context is not None
595
+ and provider.is_class
596
+ and is_async_context_manager(instance)
597
+ ):
598
+ await context.aenter(instance)
599
+ return instance
588
600
 
589
601
  def _get_provided_kwargs(
590
602
  self, provider: Provider, context: InstanceContext | None, /, **defaults: Any
@@ -627,7 +639,7 @@ class Container:
627
639
 
628
640
  # Wrap the instance in a proxy for testing
629
641
  if self.testing:
630
- return InstanceProxy(interface=parameter.annotation, instance=instance)
642
+ return InstanceProxy(instance, interface=parameter.annotation)
631
643
  return instance
632
644
 
633
645
  async def _aget_provided_kwargs(
@@ -671,7 +683,7 @@ class Container:
671
683
 
672
684
  # Wrap the instance in a proxy for testing
673
685
  if self.testing:
674
- return InstanceProxy(interface=parameter.annotation, instance=instance)
686
+ return InstanceProxy(instance, interface=parameter.annotation)
675
687
  return instance
676
688
 
677
689
  def _resolve_parameter(
@@ -776,6 +788,10 @@ class Container:
776
788
  """
777
789
  Override the provider for the specified interface with a specific instance.
778
790
  """
791
+ if not self.testing:
792
+ raise RuntimeError(
793
+ "The `override` method can only be used in testing mode."
794
+ )
779
795
  if not self.is_registered(interface) and self.strict:
780
796
  raise LookupError(
781
797
  f"The provider interface `{get_full_qualname(interface)}` "
anydi/_provider.py CHANGED
@@ -38,6 +38,7 @@ class Provider:
38
38
  "_kind",
39
39
  "_interface",
40
40
  "_parameters",
41
+ "_is_class",
41
42
  "_is_coroutine",
42
43
  "_is_generator",
43
44
  "_is_async_generator",
@@ -57,6 +58,7 @@ class Provider:
57
58
  # Detect the kind of callable provider
58
59
  self._detect_kind()
59
60
 
61
+ self._is_class = self._kind == CallableKind.CLASS
60
62
  self._is_coroutine = self._kind == CallableKind.COROUTINE
61
63
  self._is_generator = self._kind == CallableKind.GENERATOR
62
64
  self._is_async_generator = self._kind == CallableKind.ASYNC_GENERATOR
@@ -107,6 +109,11 @@ class Provider:
107
109
  def parameters(self) -> list[inspect.Parameter]:
108
110
  return self._parameters
109
111
 
112
+ @property
113
+ def is_class(self) -> bool:
114
+ """Check if the provider is a class."""
115
+ return self._is_class
116
+
110
117
  @property
111
118
  def is_coroutine(self) -> bool:
112
119
  """Check if the provider is a coroutine."""
anydi/_types.py CHANGED
@@ -5,6 +5,7 @@ from collections.abc import Iterable
5
5
  from types import ModuleType
6
6
  from typing import Annotated, Any, NamedTuple, Union
7
7
 
8
+ import wrapt
8
9
  from typing_extensions import Literal, Self, TypeAlias
9
10
 
10
11
  Scope = Literal["transient", "singleton", "request"]
@@ -37,20 +38,19 @@ def is_event_type(obj: Any) -> bool:
37
38
  return inspect.isclass(obj) and issubclass(obj, Event)
38
39
 
39
40
 
40
- class InstanceProxy:
41
- __slots__ = ("interface", "instance")
41
+ class InstanceProxy(wrapt.ObjectProxy): # type: ignore[misc]
42
+ def __init__(self, wrapped: Any, *, interface: type[Any]) -> None:
43
+ super().__init__(wrapped)
44
+ self._self_interface = interface
42
45
 
43
- def __init__(self, *, interface: type[Any], instance: Any):
44
- self.interface = interface
45
- self.instance = instance
46
+ @property
47
+ def interface(self) -> type[Any]:
48
+ return self._self_interface
46
49
 
47
- def __getattribute__(self, name: str) -> Any:
48
- if name in ("interface", "instance"):
49
- return object.__getattribute__(self, name)
50
- return getattr(self.instance, name)
51
-
52
- def __repr__(self) -> str:
53
- return f"InstanceProxy({self.interface!r})"
50
+ def __getattribute__(self, item: str) -> Any:
51
+ if item in "interface":
52
+ return object.__getattribute__(self, item)
53
+ return object.__getattribute__(self, item)
54
54
 
55
55
 
56
56
  class ProviderDecoratorArgs(NamedTuple):
anydi/_utils.py CHANGED
@@ -44,6 +44,16 @@ def is_builtin_type(tp: type[Any]) -> bool:
44
44
  return tp.__module__ == builtins.__name__
45
45
 
46
46
 
47
+ def is_context_manager(obj: Any) -> bool:
48
+ """Check if the given object is a context manager."""
49
+ return hasattr(obj, "__enter__") and hasattr(obj, "__exit__")
50
+
51
+
52
+ def is_async_context_manager(obj: Any) -> bool:
53
+ """Check if the given object is an async context manager."""
54
+ return hasattr(obj, "__aenter__") and hasattr(obj, "__aexit__")
55
+
56
+
47
57
  def get_typed_annotation(
48
58
  annotation: Any, globalns: dict[str, Any], module: Any = None
49
59
  ) -> Any:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anydi
3
- Version: 0.36.1a5
3
+ Version: 0.37.1
4
4
  Summary: Dependency Injection library
5
5
  Home-page: https://github.com/antonrh/anydi
6
6
  License: MIT
@@ -33,6 +33,7 @@ Requires-Dist: anyio (>=3.7.1)
33
33
  Requires-Dist: mkdocs (>=1.4.2,<2.0.0) ; extra == "docs"
34
34
  Requires-Dist: mkdocs-material (>=9.5.29,<10.0.0) ; extra == "docs"
35
35
  Requires-Dist: typing-extensions (>=4.12.1,<5.0.0)
36
+ Requires-Dist: wrapt (>=1.17.0,<2.0.0)
36
37
  Project-URL: Repository, https://github.com/antonrh/anydi
37
38
  Description-Content-Type: text/markdown
38
39
 
@@ -1,9 +1,9 @@
1
1
  anydi/__init__.py,sha256=OfRg2EfXD65pHTGQKhfkABMwUhw5LvsuTQV_Tv4V4wk,501
2
- anydi/_container.py,sha256=7Qwreyqnc95M0mgDus6vQmyuIF5qLFTMaBoA7GoWELg,37772
2
+ anydi/_container.py,sha256=6dr2sOSkx8GRdqzlokHB5McfBNOWp_9ozyyiS5dpxoE,38320
3
3
  anydi/_context.py,sha256=7LV_SL4QWkJeiG7_4D9PZ5lmU-MPzhofxC95zCgY9Gc,2651
4
- anydi/_provider.py,sha256=W42y8wbsnWbb9B9gI-pnEa-lsz68nK0VIm55CJW3pWg,7457
5
- anydi/_types.py,sha256=Vttj9GTp9g0KKpK-uqolLfVZJPIM7f7_YL8bPlablcQ,1539
6
- anydi/_utils.py,sha256=gU5ccCC2JZpKOY84ZCJ3PHyaEGhTW82PkGAhS6_R_qI,4027
4
+ anydi/_provider.py,sha256=1IyxHO83NHjsPDHLDIZtW1pJ7i8VpWD3EM4T6duw9zA,7661
5
+ anydi/_types.py,sha256=fdO4xNXtGMxVArmlfDkFYbyR895ixkBTW6V8lMceN7Q,1562
6
+ anydi/_utils.py,sha256=INI0jNIXrJ6LS4zqJymMO2yUEobpxmBGASf4G_vR6AU,4378
7
7
  anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  anydi/ext/_utils.py,sha256=U6sRqWzccWUu7eMhbXX1NrwcaxitQF9cO1KxnKF37gw,2566
9
9
  anydi/ext/django/__init__.py,sha256=QI1IABCVgSDTUoh7M9WMECKXwB3xvh04HfQ9TOWw1Mk,223
@@ -22,8 +22,8 @@ anydi/ext/pytest_plugin.py,sha256=ShGhiZnP1KyMHhnc9Ci1RKAuHVhw628OTS2P2BLEOfc,50
22
22
  anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
24
24
  anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- anydi-0.36.1a5.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
26
- anydi-0.36.1a5.dist-info/METADATA,sha256=pmhLHtLYS6Hi90rx0etN5ViIkIJYGMeOopTfyKPCHhY,5066
27
- anydi-0.36.1a5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
28
- anydi-0.36.1a5.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
29
- anydi-0.36.1a5.dist-info/RECORD,,
25
+ anydi-0.37.1.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
26
+ anydi-0.37.1.dist-info/METADATA,sha256=9pkB0M7YtPuvIXxskF0h7roe7FY_R90rHZ6772w2LSI,5103
27
+ anydi-0.37.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
28
+ anydi-0.37.1.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
29
+ anydi-0.37.1.dist-info/RECORD,,