anydi 0.30.0__py3-none-any.whl → 0.32.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/_provider.py ADDED
@@ -0,0 +1,187 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import uuid
5
+ from collections.abc import AsyncIterator, Iterator
6
+ from enum import IntEnum
7
+ from typing import Any, Callable
8
+
9
+ from typing_extensions import get_args, get_origin
10
+
11
+ try:
12
+ from types import NoneType
13
+ except ImportError:
14
+ NoneType = type(None) # type: ignore[misc]
15
+
16
+
17
+ from ._types import Event, Scope
18
+ from ._utils import get_full_qualname, get_typed_annotation
19
+
20
+ _sentinel = object()
21
+
22
+
23
+ class CallableKind(IntEnum):
24
+ CLASS = 1
25
+ FUNCTION = 2
26
+ COROUTINE = 3
27
+ GENERATOR = 4
28
+ ASYNC_GENERATOR = 5
29
+
30
+
31
+ class Provider:
32
+ __slots__ = (
33
+ "_call",
34
+ "_call_module",
35
+ "_call_globals",
36
+ "_scope",
37
+ "_qualname",
38
+ "_kind",
39
+ "_interface",
40
+ "_parameters",
41
+ )
42
+
43
+ def __init__(
44
+ self, *, call: Callable[..., Any], scope: Scope, interface: Any = _sentinel
45
+ ) -> None:
46
+ self._call = call
47
+ self._call_module = getattr(call, "__module__", None)
48
+ self._call_globals = getattr(call, "__globals__", {})
49
+ self._scope = scope
50
+ self._qualname = get_full_qualname(call)
51
+
52
+ # Detect the kind of callable provider
53
+ self._detect_kind()
54
+
55
+ # Validate the scope of the provider
56
+ self._validate_scope()
57
+
58
+ # Get the signature
59
+ signature = inspect.signature(call)
60
+
61
+ # Detect the interface
62
+ self._detect_interface(interface, signature)
63
+
64
+ # Detect the parameters
65
+ self._detect_parameters(signature)
66
+
67
+ def __str__(self) -> str:
68
+ return self._qualname
69
+
70
+ def __eq__(self, other: object) -> bool:
71
+ if not isinstance(other, Provider):
72
+ return NotImplemented
73
+ return (
74
+ self._call == other._call
75
+ and self._scope == other._scope
76
+ and self._interface == other._interface
77
+ )
78
+
79
+ @property
80
+ def call(self) -> Callable[..., Any]:
81
+ return self._call
82
+
83
+ @property
84
+ def kind(self) -> CallableKind:
85
+ return self._kind
86
+
87
+ @property
88
+ def scope(self) -> Scope:
89
+ return self._scope
90
+
91
+ @property
92
+ def interface(self) -> Any:
93
+ return self._interface
94
+
95
+ @property
96
+ def parameters(self) -> list[inspect.Parameter]:
97
+ return self._parameters
98
+
99
+ @property
100
+ def is_resource(self) -> bool:
101
+ """Check if the provider is a resource."""
102
+ return self._kind in {
103
+ CallableKind.GENERATOR,
104
+ CallableKind.ASYNC_GENERATOR,
105
+ }
106
+
107
+ def _validate_scope(self) -> None:
108
+ """Validate the scope of the provider."""
109
+ if self.is_resource and self.scope == "transient":
110
+ raise TypeError(
111
+ f"The resource provider `{self}` is attempting to register "
112
+ "with a transient scope, which is not allowed."
113
+ )
114
+
115
+ def _detect_kind(self) -> None:
116
+ """Detect the kind of callable provider."""
117
+ if inspect.isclass(self.call):
118
+ self._kind = CallableKind.CLASS
119
+ elif inspect.iscoroutinefunction(self.call):
120
+ self._kind = CallableKind.COROUTINE
121
+ elif inspect.isasyncgenfunction(self.call):
122
+ self._kind = CallableKind.ASYNC_GENERATOR
123
+ elif inspect.isgeneratorfunction(self.call):
124
+ self._kind = CallableKind.GENERATOR
125
+ elif inspect.isfunction(self.call) or inspect.ismethod(self.call):
126
+ self._kind = CallableKind.FUNCTION
127
+ else:
128
+ raise TypeError(
129
+ f"The provider `{self.call}` is invalid because it is not a callable "
130
+ "object. Only callable providers are allowed."
131
+ )
132
+
133
+ def _detect_interface(self, interface: Any, signature: inspect.Signature) -> None:
134
+ """Detect the interface of callable provider."""
135
+ # If the callable is a class, return the class itself
136
+ if self._kind == CallableKind.CLASS:
137
+ self._interface = self._call
138
+ return
139
+
140
+ if interface is _sentinel:
141
+ interface = self._resolve_interface(interface, signature)
142
+
143
+ # If the callable is an iterator, return the actual type
144
+ iterator_types = {Iterator, AsyncIterator}
145
+ if interface in iterator_types or get_origin(interface) in iterator_types:
146
+ if args := get_args(interface):
147
+ interface = args[0]
148
+ # If the callable is a generator, return the resource type
149
+ if interface is NoneType or interface is None:
150
+ self._interface = type(f"Event_{uuid.uuid4().hex}", (Event,), {})
151
+ return
152
+ else:
153
+ raise TypeError(
154
+ f"Cannot use `{self}` resource type annotation "
155
+ "without actual type argument."
156
+ )
157
+
158
+ # None interface is not allowed
159
+ if interface in {None, NoneType}:
160
+ raise TypeError(f"Missing `{self}` provider return annotation.")
161
+
162
+ # Set the interface
163
+ self._interface = interface
164
+
165
+ def _resolve_interface(self, interface: Any, signature: inspect.Signature) -> Any:
166
+ """Resolve the interface of the callable provider."""
167
+ interface = signature.return_annotation
168
+ if interface is inspect.Signature.empty:
169
+ return None
170
+ return get_typed_annotation(
171
+ interface,
172
+ self._call_globals,
173
+ module=self._call_module,
174
+ )
175
+
176
+ def _detect_parameters(self, signature: inspect.Signature) -> None:
177
+ """Detect the parameters of the callable provider."""
178
+ self._parameters = [
179
+ parameter.replace(
180
+ annotation=get_typed_annotation(
181
+ parameter.annotation,
182
+ self._call_globals,
183
+ module=self._call_module,
184
+ )
185
+ )
186
+ for parameter in signature.parameters.values()
187
+ ]
anydi/_scanner.py CHANGED
@@ -3,13 +3,13 @@ from __future__ import annotations
3
3
  import importlib
4
4
  import inspect
5
5
  import pkgutil
6
+ from collections.abc import Iterable
6
7
  from dataclasses import dataclass
7
8
  from types import ModuleType
8
9
  from typing import (
9
10
  TYPE_CHECKING,
10
11
  Any,
11
12
  Callable,
12
- Iterable,
13
13
  TypeVar,
14
14
  Union,
15
15
  cast,
@@ -32,13 +32,6 @@ P = ParamSpec("P")
32
32
 
33
33
  @dataclass(frozen=True)
34
34
  class Dependency:
35
- """Represents a scanned dependency.
36
-
37
- Attributes:
38
- member: The member object that represents the dependency.
39
- module: The module where the dependency is defined.
40
- """
41
-
42
35
  member: Any
43
36
  module: ModuleType
44
37
 
@@ -58,14 +51,7 @@ class Scanner:
58
51
  *,
59
52
  tags: Iterable[str] | None = None,
60
53
  ) -> None:
61
- """Scan packages or modules for decorated members and inject dependencies.
62
-
63
- Args:
64
- packages: A single package or module to scan,
65
- or an iterable of packages or modules to scan.
66
- tags: Optional list of tags to filter the scanned members. Only members
67
- with at least one matching tag will be scanned. Defaults to None.
68
- """
54
+ """Scan packages or modules for decorated members and inject dependencies."""
69
55
  dependencies: list[Dependency] = []
70
56
 
71
57
  if isinstance(packages, Iterable) and not isinstance(packages, str):
@@ -86,16 +72,7 @@ class Scanner:
86
72
  *,
87
73
  tags: Iterable[str] | None = None,
88
74
  ) -> list[Dependency]:
89
- """Scan a package or module for decorated members.
90
-
91
- Args:
92
- package: The package or module to scan.
93
- tags: Optional list of tags to filter the scanned members. Only members
94
- with at least one matching tag will be scanned. Defaults to None.
95
-
96
- Returns:
97
- A list of scanned dependencies.
98
- """
75
+ """Scan a package or module for decorated members."""
99
76
  tags = tags or []
100
77
  if isinstance(package, str):
101
78
  package = importlib.import_module(package)
@@ -118,16 +95,7 @@ class Scanner:
118
95
  def _scan_module(
119
96
  self, module: ModuleType, *, tags: Iterable[str]
120
97
  ) -> list[Dependency]:
121
- """Scan a module for decorated members.
122
-
123
- Args:
124
- module: The module to scan.
125
- tags: List of tags to filter the scanned members. Only members with at
126
- least one matching tag will be scanned.
127
-
128
- Returns:
129
- A list of scanned dependencies.
130
- """
98
+ """Scan a module for decorated members."""
131
99
  dependencies: list[Dependency] = []
132
100
 
133
101
  for _, member in inspect.getmembers(module):
@@ -156,11 +124,7 @@ class Scanner:
156
124
  continue
157
125
 
158
126
  # Get by Marker
159
- if inspect.isclass(member):
160
- parameters = get_typed_parameters(member.__init__)
161
- else:
162
- parameters = get_typed_parameters(member)
163
- for parameter in parameters:
127
+ for parameter in get_typed_parameters(member):
164
128
  if is_marker(parameter.default):
165
129
  dependencies.append(
166
130
  self._create_dependency(member=member, module=module)
@@ -170,15 +134,7 @@ class Scanner:
170
134
  return dependencies
171
135
 
172
136
  def _create_dependency(self, member: Any, module: ModuleType) -> Dependency:
173
- """Create a `Dependency` object from the scanned member and module.
174
-
175
- Args:
176
- member: The scanned member.
177
- module: The module containing the scanned member.
178
-
179
- Returns:
180
- A `ScannedDependency` object.
181
- """
137
+ """Create a `Dependency` object from the scanned member and module."""
182
138
  if hasattr(member, "__wrapped__"):
183
139
  member = member.__wrapped__
184
140
  return Dependency(member=member, module=module)
@@ -190,7 +146,7 @@ class InjectDecoratorArgs(NamedTuple):
190
146
 
191
147
 
192
148
  @overload
193
- def injectable(obj: Callable[P, T]) -> Callable[P, T]: ...
149
+ def injectable(func: Callable[P, T]) -> Callable[P, T]: ...
194
150
 
195
151
 
196
152
  @overload
@@ -200,26 +156,16 @@ def injectable(
200
156
 
201
157
 
202
158
  def injectable(
203
- obj: Callable[P, T] | None = None,
159
+ func: Callable[P, T] | None = None,
204
160
  tags: Iterable[str] | None = None,
205
161
  ) -> Callable[[Callable[P, T]], Callable[P, T]] | Callable[P, T]:
206
- """Decorator for marking a function or method as requiring dependency injection.
207
-
208
- Args:
209
- obj: The target function or method to be decorated.
210
- tags: Optional tags to associate with the injection point.
211
-
212
- Returns:
213
- If `obj` is provided, returns the decorated target function or method.
214
- If `obj` is not provided, returns a decorator that can be used to mark
215
- a function or method as requiring dependency injection.
216
- """
162
+ """Decorator for marking a function or method as requiring dependency injection."""
217
163
 
218
- def decorator(obj: Callable[P, T]) -> Callable[P, T]:
219
- setattr(obj, "__injectable__", InjectDecoratorArgs(wrapped=True, tags=tags))
220
- return obj
164
+ def decorator(inner: Callable[P, T]) -> Callable[P, T]:
165
+ setattr(inner, "__injectable__", InjectDecoratorArgs(wrapped=True, tags=tags))
166
+ return inner
221
167
 
222
- if obj is None:
168
+ if func is None:
223
169
  return decorator
224
170
 
225
- return decorator(obj)
171
+ return decorator(func)
anydi/_types.py CHANGED
@@ -1,19 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
- from dataclasses import dataclass
5
- from functools import cached_property
6
- from typing import Any, Callable, Type, TypeVar, Union
4
+ from typing import Annotated, Any, TypeVar, Union
7
5
 
8
- from typing_extensions import Annotated, Literal, Self, TypeAlias
9
-
10
- from ._utils import get_full_qualname, get_typed_parameters
6
+ from typing_extensions import Literal, Self, TypeAlias
11
7
 
12
8
  Scope = Literal["transient", "singleton", "request"]
13
9
 
14
10
  T = TypeVar("T")
15
- AnyInterface: TypeAlias = Union[Type[Any], Annotated[Any, ...]]
16
- Interface: TypeAlias = Type[T]
11
+ AnyInterface: TypeAlias = Union[type[Any], Annotated[Any, ...]]
12
+ Interface: TypeAlias = type[T]
17
13
 
18
14
 
19
15
  class Marker:
@@ -39,98 +35,3 @@ class Event:
39
35
  def is_event_type(obj: Any) -> bool:
40
36
  """Checks if an object is an event type."""
41
37
  return inspect.isclass(obj) and issubclass(obj, Event)
42
-
43
-
44
- @dataclass(frozen=True)
45
- class Provider:
46
- """Represents a provider object.
47
-
48
- Attributes:
49
- obj: The callable object that serves as the provider.
50
- scope: The scope of the provider.
51
- """
52
-
53
- obj: Callable[..., Any]
54
- scope: Scope
55
-
56
- def __str__(self) -> str:
57
- """Returns a string representation of the provider.
58
-
59
- Returns:
60
- The string representation of the provider.
61
- """
62
- return self.name
63
-
64
- @cached_property
65
- def name(self) -> str:
66
- """Returns the full qualified name of the provider object.
67
-
68
- Returns:
69
- The full qualified name of the provider object.
70
- """
71
- return get_full_qualname(self.obj)
72
-
73
- @cached_property
74
- def parameters(self) -> list[inspect.Parameter]:
75
- """Returns the parameters of the provider as a mapping.
76
-
77
- Returns:
78
- The parameters of the provider.
79
- """
80
- return get_typed_parameters(self.obj)
81
-
82
- @cached_property
83
- def is_class(self) -> bool:
84
- """Checks if the provider object is a class.
85
-
86
- Returns:
87
- True if the provider object is a class, False otherwise.
88
- """
89
- return inspect.isclass(self.obj)
90
-
91
- @cached_property
92
- def is_function(self) -> bool:
93
- """Checks if the provider object is a function.
94
-
95
- Returns:
96
- True if the provider object is a function, False otherwise.
97
- """
98
- return (inspect.isfunction(self.obj) or inspect.ismethod(self.obj)) and not (
99
- self.is_resource
100
- )
101
-
102
- @cached_property
103
- def is_coroutine(self) -> bool:
104
- """Checks if the provider object is a coroutine function.
105
-
106
- Returns:
107
- True if the provider object is a coroutine function, False otherwise.
108
- """
109
- return inspect.iscoroutinefunction(self.obj)
110
-
111
- @cached_property
112
- def is_generator(self) -> bool:
113
- """Checks if the provider object is a generator function.
114
-
115
- Returns:
116
- True if the provider object is a resource, False otherwise.
117
- """
118
- return inspect.isgeneratorfunction(self.obj)
119
-
120
- @cached_property
121
- def is_async_generator(self) -> bool:
122
- """Checks if the provider object is an async generator function.
123
-
124
- Returns:
125
- True if the provider object is an async resource, False otherwise.
126
- """
127
- return inspect.isasyncgenfunction(self.obj)
128
-
129
- @property
130
- def is_resource(self) -> bool:
131
- """Checks if the provider object is a sync or async generator function.
132
-
133
- Returns:
134
- True if the provider object is a resource, False otherwise.
135
- """
136
- return self.is_generator or self.is_async_generator
anydi/_utils.py CHANGED
@@ -6,8 +6,9 @@ import builtins
6
6
  import functools
7
7
  import importlib
8
8
  import inspect
9
+ import re
9
10
  import sys
10
- from typing import Any, AsyncIterator, Callable, ForwardRef, Iterator, TypeVar
11
+ from typing import Any, Callable, ForwardRef, TypeVar
11
12
 
12
13
  from typing_extensions import ParamSpec, get_args, get_origin
13
14
 
@@ -23,28 +24,23 @@ P = ParamSpec("P")
23
24
 
24
25
  def get_full_qualname(obj: Any) -> str:
25
26
  """Get the fully qualified name of an object."""
26
- qualname = getattr(obj, "__qualname__", None)
27
- module = getattr(obj, "__module__", None)
28
-
29
- if qualname is None:
30
- qualname = type(obj).__qualname__
31
-
32
- if module is None:
33
- module = type(obj).__module__
34
-
35
- if module == builtins.__name__:
36
- return qualname
27
+ # Get module and qualname with defaults to handle non-types directly
28
+ module = getattr(obj, "__module__", type(obj).__module__)
29
+ qualname = getattr(obj, "__qualname__", type(obj).__qualname__)
37
30
 
38
31
  origin = get_origin(obj)
39
-
32
+ # If origin exists, handle generics recursively
40
33
  if origin:
41
- args = ", ".join(
42
- get_full_qualname(arg) if not isinstance(arg, str) else f'"{arg}"'
43
- for arg in get_args(obj)
44
- )
34
+ args = ", ".join(get_full_qualname(arg) for arg in get_args(obj))
45
35
  return f"{get_full_qualname(origin)}[{args}]"
46
36
 
47
- return f"{module}.{qualname}"
37
+ # Substitute standard library prefixes for clarity
38
+ full_qualname = f"{module}.{qualname}"
39
+ return re.sub(
40
+ r"\b(builtins|typing|typing_extensions|collections\.abc|types)\.",
41
+ "",
42
+ full_qualname,
43
+ )
48
44
 
49
45
 
50
46
  def is_builtin_type(tp: type[Any]) -> bool:
@@ -52,71 +48,33 @@ def is_builtin_type(tp: type[Any]) -> bool:
52
48
  return tp.__module__ == builtins.__name__
53
49
 
54
50
 
55
- def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
56
- if sys.version_info < (3, 9):
57
- return type_._evaluate(globalns, localns)
58
- return type_._evaluate(globalns, localns, recursive_guard=frozenset())
59
-
60
-
61
51
  def get_typed_annotation(
62
- annotation: Any,
63
- globalns: dict[str, Any],
64
- module: Any = None,
65
- is_class: bool = False,
52
+ annotation: Any, globalns: dict[str, Any], module: Any = None
66
53
  ) -> Any:
67
- """Get the typed annotation of a parameter."""
54
+ """Get the typed annotation of a callable object."""
68
55
  if isinstance(annotation, str):
69
- if sys.version_info >= (3, 10, 2):
70
- annotation = ForwardRef(annotation, module=module, is_class=is_class)
71
- elif sys.version_info >= (3, 10, 0):
72
- annotation = ForwardRef(annotation, module=module)
56
+ if sys.version_info >= (3, 10):
57
+ ref = ForwardRef(annotation, module=module)
73
58
  else:
74
- annotation = ForwardRef(annotation)
75
- annotation = evaluate_forwardref(annotation, globalns, {})
59
+ ref = ForwardRef(annotation)
60
+ annotation = ref._evaluate(globalns, globalns, recursive_guard=frozenset()) # noqa
76
61
  return annotation
77
62
 
78
63
 
79
- def get_typed_return_annotation(obj: Callable[..., Any]) -> Any:
80
- """Get the typed return annotation of a callable object."""
81
- signature = inspect.signature(obj)
82
- annotation = signature.return_annotation
83
- if annotation is inspect.Signature.empty:
84
- return None
85
- globalns = getattr(obj, "__globals__", {})
86
- module = getattr(obj, "__module__", None)
87
- is_class = inspect.isclass(obj)
88
- return get_typed_annotation(annotation, globalns, module=module, is_class=is_class)
89
-
90
-
91
64
  def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
92
65
  """Get the typed parameters of a callable object."""
93
66
  globalns = getattr(obj, "__globals__", {})
94
67
  module = getattr(obj, "__module__", None)
95
- is_class = inspect.isclass(obj)
96
68
  return [
97
69
  parameter.replace(
98
70
  annotation=get_typed_annotation(
99
- parameter.annotation,
100
- globalns,
101
- module=module,
102
- is_class=is_class,
71
+ parameter.annotation, globalns, module=module
103
72
  )
104
73
  )
105
- for name, parameter in inspect.signature(obj).parameters.items()
74
+ for parameter in inspect.signature(obj).parameters.values()
106
75
  ]
107
76
 
108
77
 
109
- _resource_origins = (
110
- get_origin(Iterator),
111
- get_origin(AsyncIterator),
112
- )
113
-
114
-
115
- def has_resource_origin(origin: Any) -> bool:
116
- """Check if the given origin is a resource origin."""
117
- return origin in _resource_origins
118
-
119
-
120
78
  async def run_async(
121
79
  func: Callable[P, T],
122
80
  /,
@@ -135,15 +93,6 @@ async def run_async(
135
93
  def import_string(dotted_path: str) -> Any:
136
94
  """
137
95
  Import a module or a specific attribute from a module using its dotted string path.
138
-
139
- Args:
140
- dotted_path: The dotted path to the object to import.
141
-
142
- Returns:
143
- object: The imported module or attribute/class/function.
144
-
145
- Raises:
146
- ImportError: If the import fails.
147
96
  """
148
97
  try:
149
98
  module_path, _, attribute_name = dotted_path.rpartition(".")
anydi/ext/_utils.py CHANGED
@@ -4,9 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import inspect
6
6
  import logging
7
- from typing import Any, Callable
7
+ from typing import Annotated, Any, Callable
8
8
 
9
- from typing_extensions import Annotated, get_args, get_origin
9
+ from typing_extensions import get_args, get_origin
10
10
 
11
11
  from anydi import Container
12
12
  from anydi._utils import get_full_qualname
@@ -32,7 +32,7 @@ def patch_annotated_parameter(parameter: inspect.Parameter) -> inspect.Parameter
32
32
  """Patch an annotated parameter to resolve the default value."""
33
33
  if not (
34
34
  get_origin(parameter.annotation) is Annotated
35
- and parameter.default is parameter.empty
35
+ and parameter.default is inspect.Parameter.empty
36
36
  ):
37
37
  return parameter
38
38
 
@@ -63,13 +63,7 @@ def patch_annotated_parameter(parameter: inspect.Parameter) -> inspect.Parameter
63
63
  def patch_call_parameter(
64
64
  call: Callable[..., Any], parameter: inspect.Parameter, container: Container
65
65
  ) -> None:
66
- """Patch a parameter to inject dependencies using AnyDI.
67
-
68
- Args:
69
- call: The call function.
70
- parameter: The parameter to patch.
71
- container: The AnyDI container.
72
- """
66
+ """Patch a parameter to inject dependencies using AnyDI."""
73
67
  parameter = patch_annotated_parameter(parameter)
74
68
 
75
69
  if not isinstance(parameter.default, HasInterface):
@@ -84,6 +78,6 @@ def patch_call_parameter(
84
78
  "first call because it is running in non-strict mode."
85
79
  )
86
80
  else:
87
- container._validate_injected_parameter(call, parameter) # noqa
81
+ container._injector._validate_injected_parameter(call, parameter) # noqa
88
82
 
89
83
  parameter.default.interface = parameter.annotation
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Sequence
3
+ from collections.abc import Sequence
4
4
 
5
5
  from django.conf import settings
6
6
  from typing_extensions import TypedDict
@@ -2,14 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterator
4
4
  from functools import wraps
5
- from typing import Any
5
+ from typing import Annotated, Any
6
6
 
7
7
  from django.conf import settings
8
8
  from django.core.cache import BaseCache, caches
9
9
  from django.db import connections
10
10
  from django.db.backends.base.base import BaseDatabaseWrapper
11
11
  from django.urls import URLPattern, URLResolver, get_resolver
12
- from typing_extensions import Annotated, get_origin
12
+ from typing_extensions import get_origin
13
13
 
14
14
  from anydi import Container
15
15
 
anydi/ext/django/apps.py CHANGED
@@ -17,7 +17,7 @@ from ._utils import inject_urlpatterns, register_components, register_settings
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
19
 
20
- class ContainerConfig(AppConfig): # type: ignore[misc]
20
+ class ContainerConfig(AppConfig):
21
21
  name = "anydi.ext.django"
22
22
  label = "anydi_django"
23
23