anydi 0.39.3__py3-none-any.whl → 0.41.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 CHANGED
@@ -14,7 +14,7 @@ from collections import defaultdict
14
14
  from collections.abc import AsyncIterator, Iterable, Iterator, Sequence
15
15
  from contextvars import ContextVar
16
16
  from types import ModuleType
17
- from typing import Annotated, Any, Callable, TypeVar, Union, cast, overload
17
+ from typing import Annotated, Any, Callable, TypeVar, cast, overload
18
18
 
19
19
  from typing_extensions import Concatenate, ParamSpec, Self, final, get_args, get_origin
20
20
 
@@ -94,18 +94,18 @@ class Container:
94
94
  testing: bool = False,
95
95
  logger: logging.Logger | None = None,
96
96
  ) -> None:
97
- self._providers: dict[type[Any], Provider] = {}
97
+ self._providers: dict[Any, Provider] = {}
98
98
  self._strict = strict
99
- self._default_scope = default_scope
99
+ self._default_scope: Scope = default_scope
100
100
  self._testing = testing
101
101
  self._logger = logger or logging.getLogger(__name__)
102
- self._resources: dict[str, list[type[Any]]] = defaultdict(list)
102
+ self._resources: dict[str, list[Any]] = defaultdict(list)
103
103
  self._singleton_context = InstanceContext()
104
104
  self._request_context_var: ContextVar[InstanceContext | None] = ContextVar(
105
105
  "request_context", default=None
106
106
  )
107
- self._override_instances: dict[type[Any], Any] = {}
108
- self._unresolved_interfaces: set[type[Any]] = set()
107
+ self._override_instances: dict[Any, Any] = {}
108
+ self._unresolved_interfaces: set[Any] = set()
109
109
  self._inject_cache: dict[Callable[..., Any], Callable[..., Any]] = {}
110
110
 
111
111
  # Register providers
@@ -364,8 +364,8 @@ class Container:
364
364
  )
365
365
 
366
366
  unresolved_parameter = None
367
- parameters = []
368
- scopes = {}
367
+ parameters: list[inspect.Parameter] = []
368
+ scopes: dict[Scope, Provider] = {}
369
369
 
370
370
  for parameter in signature.parameters.values():
371
371
  if parameter.annotation is inspect.Parameter.empty:
@@ -490,7 +490,10 @@ class Container:
490
490
  call = interface
491
491
  if inspect.isclass(call) and not is_builtin_type(call):
492
492
  # Try to get defined scope
493
- scope = getattr(interface, "__scope__", parent_scope)
493
+ if hasattr(call, "__scope__"):
494
+ scope = getattr(call, "__scope__")
495
+ else:
496
+ scope = parent_scope
494
497
  return self._register_provider(call, scope, interface, **defaults)
495
498
  raise
496
499
 
@@ -510,9 +513,11 @@ class Container:
510
513
  def _parameter_has_default(
511
514
  self, parameter: inspect.Parameter, /, **defaults: Any
512
515
  ) -> bool:
513
- return (defaults and parameter.name in defaults) or (
514
- not self.strict and parameter.default is not inspect.Parameter.empty
516
+ has_default_in_kwargs = parameter.name in defaults if defaults else False
517
+ has_non_strict_default = not self.strict and (
518
+ parameter.default is not inspect.Parameter.empty
515
519
  )
520
+ return has_default_in_kwargs or has_non_strict_default
516
521
 
517
522
  ############################
518
523
  # Instance Methods
@@ -522,7 +527,7 @@ class Container:
522
527
  def resolve(self, interface: type[T]) -> T: ...
523
528
 
524
529
  @overload
525
- def resolve(self, interface: T) -> T: ...
530
+ def resolve(self, interface: T) -> T: ... # type: ignore
526
531
 
527
532
  def resolve(self, interface: type[T]) -> T:
528
533
  """Resolve an instance by interface."""
@@ -942,7 +947,7 @@ class Container:
942
947
 
943
948
  def _get_injected_params(self, call: Callable[..., Any]) -> dict[str, Any]:
944
949
  """Get the injected parameters of a callable object."""
945
- injected_params = {}
950
+ injected_params: dict[str, Any] = {}
946
951
  for parameter in get_typed_parameters(call):
947
952
  if not is_marker(parameter.default):
948
953
  continue
@@ -1007,6 +1012,10 @@ class Container:
1007
1012
  scope=decorator_args.scope,
1008
1013
  override=decorator_args.override,
1009
1014
  )(obj)
1015
+ else:
1016
+ raise TypeError(
1017
+ "The module must be a callable, a module type, or a module instance."
1018
+ )
1010
1019
 
1011
1020
  ############################
1012
1021
  # Scanner Methods
@@ -1022,10 +1031,10 @@ class Container:
1022
1031
  """Scan packages or modules for decorated members and inject dependencies."""
1023
1032
  dependencies: list[ScannedDependency] = []
1024
1033
 
1025
- if isinstance(packages, Iterable) and not isinstance(packages, str):
1026
- scan_packages: Iterable[ModuleType | str] = packages
1034
+ if isinstance(packages, ModuleType | str):
1035
+ scan_packages: Iterable[ModuleType | str] = [packages]
1027
1036
  else:
1028
- scan_packages = cast(Iterable[Union[ModuleType, str]], [packages])
1037
+ scan_packages = packages
1029
1038
 
1030
1039
  for package in scan_packages:
1031
1040
  dependencies.extend(self._scan_package(package, tags=tags))
@@ -1117,19 +1126,19 @@ class Container:
1117
1126
 
1118
1127
  def transient(target: T) -> T:
1119
1128
  """Decorator for marking a class as transient scope."""
1120
- setattr(target, "__scope__", "transient")
1129
+ target.__scope__ = "transient"
1121
1130
  return target
1122
1131
 
1123
1132
 
1124
1133
  def request(target: T) -> T:
1125
1134
  """Decorator for marking a class as request scope."""
1126
- setattr(target, "__scope__", "request")
1135
+ target.__scope__ = "request"
1127
1136
  return target
1128
1137
 
1129
1138
 
1130
1139
  def singleton(target: T) -> T:
1131
1140
  """Decorator for marking a class as singleton scope."""
1132
- setattr(target, "__scope__", "singleton")
1141
+ target.__scope__ = "singleton"
1133
1142
  return target
1134
1143
 
1135
1144
 
@@ -1141,11 +1150,7 @@ def provider(
1141
1150
  def decorator(
1142
1151
  target: Callable[Concatenate[M, P], T],
1143
1152
  ) -> Callable[Concatenate[M, P], T]:
1144
- setattr(
1145
- target,
1146
- "__provider__",
1147
- ProviderDecoratorArgs(scope=scope, override=override),
1148
- )
1153
+ target.__provider__ = ProviderDecoratorArgs(scope=scope, override=override) # type: ignore
1149
1154
  return target
1150
1155
 
1151
1156
  return decorator
anydi/_context.py CHANGED
@@ -16,17 +16,17 @@ class InstanceContext:
16
16
  __slots__ = ("_instances", "_stack", "_async_stack", "_lock", "_async_lock")
17
17
 
18
18
  def __init__(self) -> None:
19
- self._instances: dict[type[Any], Any] = {}
19
+ self._instances: dict[Any, Any] = {}
20
20
  self._stack = contextlib.ExitStack()
21
21
  self._async_stack = contextlib.AsyncExitStack()
22
22
  self._lock = threading.RLock()
23
23
  self._async_lock = AsyncRLock()
24
24
 
25
- def get(self, interface: type[Any]) -> Any | None:
25
+ def get(self, interface: Any) -> Any | None:
26
26
  """Get an instance from the context."""
27
27
  return self._instances.get(interface)
28
28
 
29
- def set(self, interface: type[Any], value: Any) -> None:
29
+ def set(self, interface: Any, value: Any) -> None:
30
30
  """Set an instance in the context."""
31
31
  self._instances[interface] = value
32
32
 
@@ -38,16 +38,16 @@ class InstanceContext:
38
38
  """Enter the context asynchronously."""
39
39
  return await self._async_stack.enter_async_context(cm)
40
40
 
41
- def __setitem__(self, interface: type[Any], value: Any) -> None:
41
+ def __setitem__(self, interface: Any, value: Any) -> None:
42
42
  self._instances[interface] = value
43
43
 
44
- def __getitem__(self, interface: type[Any]) -> Any:
44
+ def __getitem__(self, interface: Any) -> Any:
45
45
  return self._instances[interface]
46
46
 
47
- def __contains__(self, interface: type[Any]) -> bool:
47
+ def __contains__(self, interface: Any) -> bool:
48
48
  return interface in self._instances
49
49
 
50
- def __delitem__(self, interface: type[Any]) -> None:
50
+ def __delitem__(self, interface: Any) -> None:
51
51
  self._instances.pop(interface, None)
52
52
 
53
53
  def __enter__(self) -> Self:
@@ -78,9 +78,9 @@ class InstanceContext:
78
78
  exc_tb: TracebackType | None,
79
79
  ) -> bool:
80
80
  """Exit the context asynchronously."""
81
- return await run_async(
82
- self.__exit__, exc_type, exc_val, exc_tb
83
- ) or await self._async_stack.__aexit__(exc_type, exc_val, exc_tb)
81
+ sync_exit = await run_async(self.__exit__, exc_type, exc_val, exc_tb)
82
+ async_exit = await self._async_stack.__aexit__(exc_type, exc_val, exc_tb)
83
+ return bool(sync_exit) or bool(async_exit)
84
84
 
85
85
  async def aclose(self) -> None:
86
86
  """Close the scoped context asynchronously."""
anydi/_types.py CHANGED
@@ -8,7 +8,7 @@ from functools import cached_property
8
8
  from types import ModuleType
9
9
  from typing import Annotated, Any, Callable, NamedTuple, Union
10
10
 
11
- import wrapt
11
+ import wrapt # type: ignore
12
12
  from typing_extensions import Literal, Self, TypeAlias
13
13
 
14
14
  Scope = Literal["transient", "singleton", "request"]
@@ -43,9 +43,9 @@ def is_event_type(obj: Any) -> bool:
43
43
  return inspect.isclass(obj) and issubclass(obj, Event)
44
44
 
45
45
 
46
- class InstanceProxy(wrapt.ObjectProxy): # type: ignore[misc]
46
+ class InstanceProxy(wrapt.ObjectProxy): # type: ignore
47
47
  def __init__(self, wrapped: Any, *, interface: type[Any]) -> None:
48
- super().__init__(wrapped)
48
+ super().__init__(wrapped) # type: ignore
49
49
  self._self_interface = interface
50
50
 
51
51
  @property
anydi/_utils.py CHANGED
@@ -12,7 +12,7 @@ from collections.abc import AsyncIterator, Iterator
12
12
  from types import TracebackType
13
13
  from typing import Any, Callable, ForwardRef, TypeVar
14
14
 
15
- import anyio
15
+ import anyio.to_thread
16
16
  from typing_extensions import ParamSpec, Self, get_args, get_origin
17
17
 
18
18
  try:
@@ -53,8 +53,9 @@ class ViewSignature(BaseViewSignature):
53
53
  func_param = self._get_param_type(name, arg)
54
54
  self.params.append(func_param)
55
55
 
56
- if hasattr(view_func, "_ninja_contribute_args"):
57
- for p_name, p_type, p_source in view_func._ninja_contribute_args: # noqa
56
+ ninja_contribute_args = getattr(view_func, "_ninja_contribute_args", None)
57
+ if ninja_contribute_args is not None:
58
+ for p_name, p_type, p_source in ninja_contribute_args:
58
59
  self.params.append(
59
60
  FuncParam(p_name, p_source.alias or p_name, p_source, p_type, False)
60
61
  )
anydi/ext/faststream.py CHANGED
@@ -30,8 +30,8 @@ def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
30
30
 
31
31
 
32
32
  def _get_broken_handlers(broker: BrokerUsecase[Any, Any]) -> list[Any]:
33
- if hasattr(broker, "handlers"):
34
- return [handler.calls[0][0] for handler in broker.handlers.values()]
33
+ if (handlers := getattr(broker, "handlers", None)) is not None:
34
+ return [handler.calls[0][0] for handler in handlers.values()]
35
35
  # faststream > 0.5.0
36
36
  return [
37
37
  subscriber.calls[0].handler
@@ -6,7 +6,6 @@ from collections.abc import Iterator
6
6
  from typing import Any, Callable, cast
7
7
 
8
8
  import pytest
9
- from _pytest.python import async_warn_and_skip
10
9
  from anyio.pytest_plugin import extract_backend_and_options, get_runner
11
10
 
12
11
  from anydi import Container
@@ -117,7 +116,12 @@ def _anydi_ainject(
117
116
 
118
117
  # Skip if the anyio backend is not available
119
118
  if "anyio_backend" not in request.fixturenames:
120
- async_warn_and_skip(request.node.nodeid)
119
+ msg = (
120
+ "To run async test functions with `anyio`, "
121
+ "please configure the `anyio` pytest plugin.\n"
122
+ "See: https://anyio.readthedocs.io/en/stable/testing.html"
123
+ )
124
+ pytest.fail(msg, pytrace=False)
121
125
 
122
126
  async def _awrapper() -> None:
123
127
  # Setup the container
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anydi
3
- Version: 0.39.3
3
+ Version: 0.41.0
4
4
  Summary: Dependency Injection library
5
5
  Project-URL: Repository, https://github.com/antonrh/anydi
6
6
  Author-email: Anton Ruhlov <antonruhlov@gmail.com>
@@ -29,7 +29,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
29
  Classifier: Typing :: Typed
30
30
  Requires-Python: ~=3.9
31
31
  Requires-Dist: anyio>=3.7.1
32
- Requires-Dist: typing-extensions<5,>=4.12.1
32
+ Requires-Dist: typing-extensions<5,>=4.13.2
33
33
  Requires-Dist: wrapt<2,>=1.17.0
34
34
  Description-Content-Type: text/markdown
35
35
 
@@ -1,15 +1,15 @@
1
1
  anydi/__init__.py,sha256=aAq10a1V_zQ3_Me3p_pll5d1O77PIgqotkOm3pshORI,495
2
- anydi/_container.py,sha256=UaNV4JcqOABkJ1FGNOQVclHjehEHXteFKOSSJxCl2As,41512
3
- anydi/_context.py,sha256=dlRvOFHbbIcg-q7g4TqJfytiSOERtbwTlCsdzXwo4GE,3012
4
- anydi/_types.py,sha256=ColGaOgF7Zqdh34N2JSWxr-8rt5bTVJomW0JqZASKHM,3631
5
- anydi/_utils.py,sha256=X_sSQFpRogHLh6Bxu0MdUthpc24jvnkHqdwKndQminA,4848
2
+ anydi/_container.py,sha256=b8Ul94gIP1-F0f_FJoWM9kRyyB8Do7YEmFvpynv8Qu0,41783
3
+ anydi/_context.py,sha256=pR97uMzafpuDe2jZcqnCShXmWp5bijzmNUOm6A6-NtY,3022
4
+ anydi/_types.py,sha256=MwLBNhjqndCraEUCIfEpvgZEJOaVmKan3zAh8BbFa0Y,3657
5
+ anydi/_utils.py,sha256=JHTp46uWdsWYJv4U1iU_twEnX__L3CEodK4yzhq3bHg,4858
6
6
  anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  anydi/ext/_utils.py,sha256=U6sRqWzccWUu7eMhbXX1NrwcaxitQF9cO1KxnKF37gw,2566
9
9
  anydi/ext/fastapi.py,sha256=AEL3ubu-LxUPHMMt1YIn3En_JZC7nyBKmKxmhka3O3c,2436
10
- anydi/ext/faststream.py,sha256=qXnNGvAqWWp9kbhbQUE6EF_OPUiYQmtOH211_O7BI_0,1898
10
+ anydi/ext/faststream.py,sha256=TBpN8DwZ4qOMVo_u1AgG5tAKcTMwJnV8eWavPMgb6lg,1923
11
11
  anydi/ext/pydantic_settings.py,sha256=8IXXLuG_OvKbvKlBkBRQUHcXgbTpgQUxeWyoMcRIUQM,1488
12
- anydi/ext/pytest_plugin.py,sha256=yR_vos8qt8uFS9uy_G_HJjNgudzJIXawrtpWnn3Pu_s,4613
12
+ anydi/ext/pytest_plugin.py,sha256=4R93VpLePF65NuZqYF3ljjjG5cvJvrzoMguDqMzlT9s,4771
13
13
  anydi/ext/django/__init__.py,sha256=QI1IABCVgSDTUoh7M9WMECKXwB3xvh04HfQ9TOWw1Mk,223
14
14
  anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
15
15
  anydi/ext/django/_settings.py,sha256=Z0RlAuXoO73oahWeMkK10w8c-4uCBde-DBpeKTV5USY,853
@@ -18,11 +18,11 @@ anydi/ext/django/apps.py,sha256=mjbf_mDCpNSriGnILzhRIr8wFHLMEK8sUerbmRku6i0,2844
18
18
  anydi/ext/django/middleware.py,sha256=5OUdp0OWRozyW338Sq04BDhacaFlyUTTOduS_7EwCTA,854
19
19
  anydi/ext/django/ninja/__init__.py,sha256=kW3grUgWp_nkWSG_-39ADHMrZLGNcj9TsJ9OW8iWWrk,546
20
20
  anydi/ext/django/ninja/_operation.py,sha256=wSWa7D73XTVlOibmOciv2l6JHPe1ERZcXrqI8W-oO2w,2696
21
- anydi/ext/django/ninja/_signature.py,sha256=2cSzKxBIxXLqtwNuH6GSlmjVJFftoGmleWfyk_NVEWw,2207
21
+ anydi/ext/django/ninja/_signature.py,sha256=uYrG2PFgG2IlXrM24rgDOtRBnrbKQeAMl6ErypRi8qs,2260
22
22
  anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
24
- anydi-0.39.3.dist-info/METADATA,sha256=-SHLMMBhORUTW0dJxLdOgQMaNWEVjzq61SsWIfPslPg,4957
25
- anydi-0.39.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- anydi-0.39.3.dist-info/entry_points.txt,sha256=Nklo9f3Oe4AkNsEgC4g43nCJ-23QDngZSVDNRMdaILI,43
27
- anydi-0.39.3.dist-info/licenses/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
28
- anydi-0.39.3.dist-info/RECORD,,
24
+ anydi-0.41.0.dist-info/METADATA,sha256=G_AXPWt34pdLtXg2ADFd8GxednjSkyBWnQbCp_HrWrQ,4957
25
+ anydi-0.41.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ anydi-0.41.0.dist-info/entry_points.txt,sha256=Nklo9f3Oe4AkNsEgC4g43nCJ-23QDngZSVDNRMdaILI,43
27
+ anydi-0.41.0.dist-info/licenses/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
28
+ anydi-0.41.0.dist-info/RECORD,,
File without changes