anydi 0.25.0a0__py3-none-any.whl → 0.25.0a1__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
@@ -1,6 +1,6 @@
1
1
  """AnyDI public objects and functions."""
2
2
 
3
- from typing import Any
3
+ from typing import Any, cast
4
4
 
5
5
  from ._container import Container, request, singleton, transient
6
6
  from ._module import Module, provider
@@ -14,7 +14,7 @@ def dep() -> Any:
14
14
 
15
15
 
16
16
  # Alias for dependency auto marker
17
- auto = dep
17
+ auto = cast(Any, Marker())
18
18
 
19
19
 
20
20
  __all__ = [
anydi/_container.py CHANGED
@@ -47,7 +47,7 @@ from ._context import (
47
47
  from ._logger import logger
48
48
  from ._module import Module, ModuleRegistry
49
49
  from ._scanner import Scanner
50
- from ._types import AnyInterface, Interface, Marker, Provider, Scope
50
+ from ._types import AnyInterface, Interface, Provider, Scope, is_marker
51
51
  from ._utils import get_full_qualname, get_signature, is_builtin_type
52
52
 
53
53
  T = TypeVar("T", bound=Any)
@@ -760,7 +760,7 @@ class Container:
760
760
  """
761
761
  injected_params = {}
762
762
  for parameter in get_signature(obj).parameters.values():
763
- if not isinstance(parameter.default, Marker):
763
+ if not is_marker(parameter.default):
764
764
  continue
765
765
  try:
766
766
  self._validate_injected_parameter(obj, parameter)
anydi/_scanner.py CHANGED
@@ -21,7 +21,7 @@ from typing import (
21
21
 
22
22
  from typing_extensions import NamedTuple, ParamSpec
23
23
 
24
- from ._types import Marker
24
+ from ._types import is_marker
25
25
  from ._utils import get_signature
26
26
 
27
27
  if TYPE_CHECKING:
@@ -163,7 +163,7 @@ class Scanner:
163
163
  else:
164
164
  signature = get_signature(member)
165
165
  for parameter in signature.parameters.values():
166
- if isinstance(parameter.default, Marker):
166
+ if is_marker(parameter.default):
167
167
  dependencies.append(
168
168
  self._create_dependency(member=member, module=module)
169
169
  )
anydi/_types.py CHANGED
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3
3
  from functools import cached_property
4
4
  from typing import Any, Callable, Type, TypeVar, Union
5
5
 
6
- from typing_extensions import Annotated, Literal, Mapping, TypeAlias
6
+ from typing_extensions import Annotated, Literal, Mapping, Self, TypeAlias
7
7
 
8
8
  from ._utils import get_full_qualname, get_signature
9
9
 
@@ -19,6 +19,14 @@ class Marker:
19
19
 
20
20
  __slots__ = ()
21
21
 
22
+ def __call__(self) -> Self:
23
+ return self
24
+
25
+
26
+ def is_marker(obj: Any) -> bool:
27
+ """Checks if an object is a marker."""
28
+ return isinstance(obj, Marker)
29
+
22
30
 
23
31
  @dataclass(frozen=True)
24
32
  class Provider:
anydi/ext/django/apps.py CHANGED
@@ -1,35 +1,53 @@
1
+ import logging
1
2
  import types
3
+ from asyncio import get_running_loop
2
4
  from functools import wraps
3
- from typing import Annotated, Any, get_origin
5
+ from typing import Any, Callable, cast
4
6
 
5
7
  from django.apps import AppConfig
6
8
  from django.conf import settings
7
9
  from django.core.cache import BaseCache, caches
10
+ from django.core.exceptions import ImproperlyConfigured
8
11
  from django.db import connections
9
12
  from django.db.backends.base.base import BaseDatabaseWrapper
10
13
  from django.urls import get_resolver
11
14
  from django.utils.module_loading import import_string
15
+ from typing_extensions import Annotated, get_origin
12
16
 
13
17
  import anydi
14
18
 
15
19
  from ._utils import iter_urlpatterns
16
20
 
21
+ logger = logging.getLogger(__name__)
22
+
17
23
 
18
24
  class ContainerConfig(AppConfig): # type: ignore[misc]
19
25
  name = "anydi.ext.django"
20
26
  label = "anydi_django"
21
27
 
22
28
  # Prefix for Django settings
23
- settings_prefix = "django.conf.settings"
29
+ settings_prefix = "django.conf.settings."
24
30
 
25
31
  def __init__(self, app_name: str, app_module: types.ModuleType | None) -> None:
26
32
  super().__init__(app_name, app_module)
27
33
  # Create a container
28
- self.container = anydi.Container(
29
- strict=getattr(settings, "ANYDI_STRICT_MODE", False),
30
- )
34
+ container_getter_path = getattr(settings, "ANYDI_CONTAINER_GETTER", None)
35
+ if container_getter_path:
36
+ try:
37
+ container_getter = cast(
38
+ Callable[[], anydi.Container], import_string(container_getter_path)
39
+ )
40
+ except ImportError as exc:
41
+ raise ImproperlyConfigured(
42
+ f"Cannot import container getter '{container_getter_path}'."
43
+ ) from exc
44
+ self.container = container_getter()
45
+ else:
46
+ self.container = anydi.Container(
47
+ strict=getattr(settings, "ANYDI_STRICT_MODE", False),
48
+ )
31
49
 
32
- def ready(self) -> None:
50
+ def ready(self) -> None: # noqa: C901
33
51
  # Register Django settings
34
52
  if getattr(settings, "ANYDI_REGISTER_SETTINGS", False):
35
53
  self.register_settings()
@@ -40,7 +58,12 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
40
58
 
41
59
  # Register modules
42
60
  for module_path in getattr(settings, "ANYDI_MODULES", []):
43
- module_cls = import_string(module_path)
61
+ try:
62
+ module_cls = import_string(module_path)
63
+ except ImportError as exc:
64
+ raise ImproperlyConfigured(
65
+ f"Cannot import module '{module_path}'."
66
+ ) from exc
44
67
  self.container.register_module(module_cls)
45
68
 
46
69
  # Patching the django-ninja framework if it installed
@@ -51,30 +74,47 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
51
74
  if urlconf := getattr(settings, "ANYDI_AUTO_INJECT_URLCONF", None):
52
75
  self.auto_inject_urlconf(urlconf)
53
76
 
77
+ # Scan packages
78
+ for scan_package in getattr(settings, "ANYDI_SCAN_PACKAGES", []):
79
+ self.container.scan(scan_package)
80
+
81
+ # Start the container
82
+ if getattr(settings, "ANYDI_START_CONTAINER", False):
83
+ try:
84
+ get_running_loop()
85
+ except RuntimeError:
86
+ logger.warning(
87
+ "Starting the container is only supported in an async context."
88
+ )
89
+ else:
90
+ self.container.start()
91
+
54
92
  def register_settings(self) -> None: # noqa: C901
55
93
  """Register Django settings into the container."""
56
94
 
57
95
  def _get_setting_value(value: Any) -> Any:
58
96
  return lambda: value
59
97
 
60
- for setting_name, value in settings.__dict__.items():
98
+ for setting_name in dir(settings):
99
+ setting_value = getattr(settings, setting_name)
61
100
  if not setting_name.isupper():
62
101
  continue
102
+
63
103
  self.container.register(
64
- Annotated[Any, f"{self.settings_prefix}.{setting_name}"],
65
- _get_setting_value(value),
104
+ Annotated[Any, f"{self.settings_prefix}{setting_name}"],
105
+ _get_setting_value(setting_value),
66
106
  scope="singleton",
67
107
  )
68
108
 
69
109
  def _aware_settings(interface: Any) -> Any:
70
110
  origin = get_origin(interface)
71
111
  if origin is not Annotated:
72
- return interface
112
+ return interface # pragma: no cover
73
113
  named = interface.__metadata__[-1]
74
114
 
75
- if isinstance(named, str):
115
+ if isinstance(named, str) and named.startswith(self.settings_prefix):
76
116
  _, setting_name = named.rsplit(self.settings_prefix, maxsplit=1)
77
- return Annotated[Any, f"{self.settings_prefix}.{setting_name}"]
117
+ return Annotated[Any, f"{self.settings_prefix}{setting_name}"]
78
118
  return interface
79
119
 
80
120
  def _resolve(resolve: Any) -> Any:
@@ -110,7 +150,6 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
110
150
  )
111
151
 
112
152
  # Register database connections
113
-
114
153
  def _get_connection(alias: str) -> Any:
115
154
  return lambda: connections[alias]
116
155
 
@@ -127,7 +166,7 @@ class ContainerConfig(AppConfig): # type: ignore[misc]
127
166
  for pattern in iter_urlpatterns(resolver.url_patterns):
128
167
  # Skip django-ninja views
129
168
  if pattern.lookup_str.startswith("ninja."):
130
- continue
169
+ continue # pragma: no cover
131
170
  pattern.callback = self.container.inject(pattern.callback)
132
171
 
133
172
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  try:
2
2
  from ninja import operation
3
- except ImportError as exc:
3
+ except ImportError as exc: # pragma: no cover
4
4
  raise ImportError(
5
5
  "'django-ninja' is not installed. "
6
6
  "Please install it using 'pip install django-ninja'."
@@ -3,8 +3,8 @@ from collections.abc import Callable
3
3
  from typing import Any
4
4
 
5
5
  from django.http import HttpResponse
6
- from ninja.signature.details import ( # noqa
7
- FuncParam,
6
+ from ninja.signature.details import (
7
+ FuncParam, # noqa
8
8
  ViewSignature as BaseViewSignature,
9
9
  )
10
10
  from ninja.signature.utils import get_path_param_names, get_typed_signature
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anydi
3
- Version: 0.25.0a0
3
+ Version: 0.25.0a1
4
4
  Summary: Dependency Injection library
5
5
  Home-page: https://github.com/antonrh/anydi
6
6
  License: MIT
@@ -89,7 +89,7 @@ pip install anydi
89
89
  *app.py*
90
90
 
91
91
  ```python
92
- from anydi import Container, dep
92
+ from anydi import auto, Container
93
93
 
94
94
  container = Container()
95
95
 
@@ -100,7 +100,7 @@ def message() -> str:
100
100
 
101
101
 
102
102
  @container.inject
103
- def say_hello(message: str = dep()) -> None:
103
+ def say_hello(message: str = auto) -> None:
104
104
  print(message)
105
105
 
106
106
 
@@ -1,26 +1,26 @@
1
- anydi/__init__.py,sha256=p2GQq45mulZRlC8oJ7bvOpjVFCeycsgI2IcNJjs_LhU,562
2
- anydi/_container.py,sha256=bd1qe6DY6dyyIjBXbRhSglUD0MJX6OeVKVSGqUqFTuU,28347
1
+ anydi/__init__.py,sha256=aeaBp5vq09sG-e9sqqs9qpUtUIDNfOdFPrlAfE5Ku9E,584
2
+ anydi/_container.py,sha256=LeGrujx8zMzBuR8PD7qNqVJmmLB-frAG74gihX0lxNc,28341
3
3
  anydi/_context.py,sha256=btGJzvTMkj5v95rAw6kjOclISKcSugC4wzrHlWlCk_I,10258
4
4
  anydi/_logger.py,sha256=UpubJUnW83kffFxkhUlObm2DmZX1Pjqoz9YFKS-JOPg,52
5
5
  anydi/_module.py,sha256=1fBo9-RWxo7TeyP0Y2uJokT-NXP2pjik6CXNoeo3l-8,3712
6
- anydi/_scanner.py,sha256=YYPqzAQRgxxMpOSOpE7EqA_hccC6VjKi9Y8y_5HbtlA,6781
7
- anydi/_types.py,sha256=AuHR2hvqaMazL55xj8I9YI6nyBAD16uQznNcmZoQsPE,3240
6
+ anydi/_scanner.py,sha256=9S3XbNVFAnHClT3FBo2CWeDBzCLG58i93a1ZJgaMNIo,6775
7
+ anydi/_types.py,sha256=TfqJ7TfudOzGMG4_OliAMMJplu42mXZcZmgmtK2oz14,3412
8
8
  anydi/_utils.py,sha256=haRwC6YTTFf6CNTfwsRQ4NZsNByoUkEmE4-rh1-VYLs,3152
9
9
  anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  anydi/ext/django/__init__.py,sha256=wsFBQP2j76Ui_0SJlN_8LKhZvEEdxA-w_yoyRAcaM58,59
11
11
  anydi/ext/django/_container.py,sha256=cxVoYQG16WP0S_Yv4TnLwuaaT7NVEOhLWO-YdALJUb4,418
12
12
  anydi/ext/django/_utils.py,sha256=C-1UabpwgCHUryAT-Pv8vU5QFfBahQlqhjecvihjtgY,430
13
- anydi/ext/django/apps.py,sha256=d1679lLRtlzaEQCwSj92JBTJcEk0s0F5cD_Hi3jbM2Y,4739
14
- anydi/ext/django/ninja/__init__.py,sha256=TBufIoxOWDbKHqd4-t6MBLwILqMUhwoEiJkpp1RrZZc,520
13
+ anydi/ext/django/apps.py,sha256=qjzKqYIB00qjsSv2RQNVnFMi0lCljmaGtMIy3JMAE0U,6326
14
+ anydi/ext/django/ninja/__init__.py,sha256=FckjVqzXTzlBllFg2PZ62wi432-ydZFyWq6ZkF2LJ9I,540
15
15
  anydi/ext/django/ninja/_operation.py,sha256=A_RbMbJyVaafoeBhZpmPQ96l1RQFLA1FchkJC_uRKnM,2660
16
- anydi/ext/django/ninja/_signature.py,sha256=Nbt2Zh-XlOtqrkhYydOUvCnNbuDytTunJPeQDtyTIyo,2177
16
+ anydi/ext/django/ninja/_signature.py,sha256=kdPU0QL3_oloWi43ehi2QJVn_WaRm1klZ6D7sA-SQYo,2177
17
17
  anydi/ext/fastapi.py,sha256=zKuo7nNpcMXppUsime5LLKTkAiNrlEtcLRFK4FRur0c,5311
18
18
  anydi/ext/pytest_plugin.py,sha256=tmncz2IbIR7FGlgmAtcf00yDKg9SQP9lCFnJBmtHx3A,3976
19
19
  anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  anydi/ext/starlette/middleware.py,sha256=Ni0BQaPjs_Ha6zcLZYYJ3-XkslTCnL9aCSa06rnRDMI,1139
21
21
  anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- anydi-0.25.0a0.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
23
- anydi-0.25.0a0.dist-info/METADATA,sha256=t4xUiUIM8ogKK0vHY9rr2xVaZ8iZmW85kZNkImahCXU,4373
24
- anydi-0.25.0a0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
25
- anydi-0.25.0a0.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
26
- anydi-0.25.0a0.dist-info/RECORD,,
22
+ anydi-0.25.0a1.dist-info/LICENSE,sha256=V6rU8a8fv6o2jQ-7ODHs0XfDFimot8Q6Km6CylRIDTo,1069
23
+ anydi-0.25.0a1.dist-info/METADATA,sha256=WvjurCb1o4pBgxTvNh-7KY3-6qocjK0euML7cVagy2E,4373
24
+ anydi-0.25.0a1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
25
+ anydi-0.25.0a1.dist-info/entry_points.txt,sha256=GmQblwzxFg42zva1HyBYJJ7TvrTIcSAGBHmyi3bvsi4,42
26
+ anydi-0.25.0a1.dist-info/RECORD,,