anydi 0.41.0__py3-none-any.whl → 0.43.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/_context.py CHANGED
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  from typing_extensions import Self
9
9
 
10
- from ._utils import AsyncRLock, run_async
10
+ from ._async import AsyncRLock, run_sync
11
11
 
12
12
 
13
13
  class InstanceContext:
@@ -78,7 +78,7 @@ class InstanceContext:
78
78
  exc_tb: TracebackType | None,
79
79
  ) -> bool:
80
80
  """Exit the context asynchronously."""
81
- sync_exit = await run_async(self.__exit__, exc_type, exc_val, exc_tb)
81
+ sync_exit = await run_sync(self.__exit__, exc_type, exc_val, exc_tb)
82
82
  async_exit = await self._async_stack.__aexit__(exc_type, exc_val, exc_tb)
83
83
  return bool(sync_exit) or bool(async_exit)
84
84
 
anydi/_decorators.py ADDED
@@ -0,0 +1,122 @@
1
+ from collections.abc import Iterable
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ Any,
5
+ Callable,
6
+ Concatenate,
7
+ ParamSpec,
8
+ Protocol,
9
+ TypedDict,
10
+ TypeGuard,
11
+ TypeVar,
12
+ overload,
13
+ )
14
+
15
+ if TYPE_CHECKING:
16
+ from ._module import Module
17
+
18
+
19
+ from ._scope import Scope
20
+
21
+ T = TypeVar("T")
22
+ P = ParamSpec("P")
23
+
24
+ ClassT = TypeVar("ClassT", bound=type)
25
+ ModuleT = TypeVar("ModuleT", bound="Module")
26
+
27
+
28
+ class ProvidedMetadata(TypedDict):
29
+ """Metadata for classes marked as provided by AnyDI."""
30
+
31
+ scope: Scope
32
+
33
+
34
+ def provided(*, scope: Scope) -> Callable[[ClassT], ClassT]:
35
+ """Decorator for marking a class as provided by AnyDI with a specific scope."""
36
+
37
+ def decorator(cls: ClassT) -> ClassT:
38
+ cls.__provided__ = ProvidedMetadata(scope=scope)
39
+ return cls
40
+
41
+ return decorator
42
+
43
+
44
+ # Scoped decorators for class-level providers
45
+ transient = provided(scope="transient")
46
+ request = provided(scope="request")
47
+ singleton = provided(scope="singleton")
48
+
49
+
50
+ class Provided(Protocol):
51
+ __provided__: ProvidedMetadata
52
+
53
+
54
+ def is_provided(cls: Any) -> TypeGuard[type[Provided]]:
55
+ return hasattr(cls, "__provided__")
56
+
57
+
58
+ class ProviderMetadata(TypedDict):
59
+ scope: Scope
60
+ override: bool
61
+
62
+
63
+ def provider(
64
+ *, scope: Scope, override: bool = False
65
+ ) -> Callable[
66
+ [Callable[Concatenate[ModuleT, P], T]], Callable[Concatenate[ModuleT, P], T]
67
+ ]:
68
+ """Decorator for marking a function or method as a provider in a AnyDI module."""
69
+
70
+ def decorator(
71
+ target: Callable[Concatenate[ModuleT, P], T],
72
+ ) -> Callable[Concatenate[ModuleT, P], T]:
73
+ target.__provider__ = ProviderMetadata(scope=scope, override=override) # type: ignore
74
+ return target
75
+
76
+ return decorator
77
+
78
+
79
+ class Provider(Protocol):
80
+ __provider__: ProviderMetadata
81
+
82
+
83
+ def is_provider(obj: Callable[..., Any]) -> TypeGuard[Provider]:
84
+ return hasattr(obj, "__provider__")
85
+
86
+
87
+ class InjectableMetadata(TypedDict):
88
+ tags: Iterable[str] | None
89
+
90
+
91
+ @overload
92
+ def injectable(func: Callable[P, T]) -> Callable[P, T]: ...
93
+
94
+
95
+ @overload
96
+ def injectable(
97
+ *, tags: Iterable[str] | None = None
98
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
99
+
100
+
101
+ def injectable(
102
+ func: Callable[P, T] | None = None,
103
+ tags: Iterable[str] | None = None,
104
+ ) -> Callable[[Callable[P, T]], Callable[P, T]] | Callable[P, T]:
105
+ """Decorator for marking a function or method as requiring dependency injection."""
106
+
107
+ def decorator(inner: Callable[P, T]) -> Callable[P, T]:
108
+ inner.__injectable__ = InjectableMetadata(tags=tags) # type: ignore
109
+ return inner
110
+
111
+ if func is None:
112
+ return decorator
113
+
114
+ return decorator(func)
115
+
116
+
117
+ class Injectable(Protocol):
118
+ __injectable__: InjectableMetadata
119
+
120
+
121
+ def is_injectable(obj: Callable[..., Any]) -> TypeGuard[Injectable]:
122
+ return hasattr(obj, "__injectable__")
anydi/_module.py ADDED
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import inspect
5
+ from typing import TYPE_CHECKING, Any, Callable
6
+
7
+ from ._decorators import ProviderMetadata, is_provider
8
+
9
+ if TYPE_CHECKING:
10
+ from ._container import Container
11
+
12
+
13
+ class ModuleMeta(type):
14
+ """A metaclass used for the Module base class."""
15
+
16
+ def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> Any:
17
+ attrs["providers"] = [
18
+ (name, value.__provider__)
19
+ for name, value in attrs.items()
20
+ if is_provider(value)
21
+ ]
22
+ return super().__new__(cls, name, bases, attrs)
23
+
24
+
25
+ class Module(metaclass=ModuleMeta):
26
+ """A base class for defining AnyDI modules."""
27
+
28
+ providers: list[tuple[str, ProviderMetadata]]
29
+
30
+ def configure(self, container: Container) -> None:
31
+ """Configure the AnyDI container with providers and their dependencies."""
32
+
33
+
34
+ ModuleDef = Module | type[Module] | Callable[["Container"], None] | str
35
+
36
+
37
+ class ModuleRegistrar:
38
+ def __init__(self, container: Container) -> None:
39
+ self._container = container
40
+
41
+ def register(self, module: ModuleDef) -> None:
42
+ """Register a module as a callable, module type, or module instance."""
43
+ # Callable Module
44
+ if inspect.isfunction(module):
45
+ module(self._container)
46
+ return
47
+
48
+ # Module path
49
+ if isinstance(module, str):
50
+ module = self.import_module_from_string(module)
51
+
52
+ # Class based Module or Module type
53
+ if inspect.isclass(module) and issubclass(module, Module):
54
+ module = module()
55
+
56
+ if isinstance(module, Module):
57
+ module.configure(self._container)
58
+ for provider_name, metadata in module.providers:
59
+ obj = getattr(module, provider_name)
60
+ self._container.provider(**metadata)(obj)
61
+ else:
62
+ raise TypeError(
63
+ "The module must be a callable, a module type, or a module instance."
64
+ )
65
+
66
+ @staticmethod
67
+ def import_module_from_string(dotted_path: str) -> Any:
68
+ """Import a module or attribute from a dotted path."""
69
+ try:
70
+ module_path, _, attribute_name = dotted_path.rpartition(".")
71
+ if module_path:
72
+ module = importlib.import_module(module_path)
73
+ return getattr(module, attribute_name)
74
+ else:
75
+ return importlib.import_module(attribute_name)
76
+ except (ImportError, AttributeError) as exc:
77
+ raise ImportError(f"Cannot import '{dotted_path}': {exc}") from exc
anydi/_provider.py ADDED
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import inspect
5
+ from dataclasses import dataclass
6
+ from functools import cached_property
7
+ from typing import Any, Callable, NamedTuple
8
+
9
+ from ._scope import Scope
10
+ from ._typing import NOT_SET
11
+
12
+
13
+ class ProviderKind(enum.IntEnum):
14
+ CLASS = 1
15
+ FUNCTION = 2
16
+ COROUTINE = 3
17
+ GENERATOR = 4
18
+ ASYNC_GENERATOR = 5
19
+
20
+ @classmethod
21
+ def from_call(cls, call: Callable[..., Any]) -> ProviderKind:
22
+ if inspect.isclass(call):
23
+ return cls.CLASS
24
+ if inspect.iscoroutinefunction(call):
25
+ return cls.COROUTINE
26
+ if inspect.isasyncgenfunction(call):
27
+ return cls.ASYNC_GENERATOR
28
+ if inspect.isgeneratorfunction(call):
29
+ return cls.GENERATOR
30
+ if inspect.isfunction(call) or inspect.ismethod(call):
31
+ return cls.FUNCTION
32
+ raise TypeError(
33
+ f"The provider `{call}` is invalid because it is not a callable object."
34
+ )
35
+
36
+ @classmethod
37
+ def is_resource(cls, kind: ProviderKind) -> bool:
38
+ return kind in (cls.GENERATOR, cls.ASYNC_GENERATOR)
39
+
40
+
41
+ @dataclass(kw_only=True, frozen=True)
42
+ class Provider:
43
+ call: Callable[..., Any]
44
+ scope: Scope
45
+ interface: Any
46
+ name: str
47
+ parameters: list[inspect.Parameter]
48
+ kind: ProviderKind
49
+
50
+ def __str__(self) -> str:
51
+ return self.name
52
+
53
+ @cached_property
54
+ def is_class(self) -> bool:
55
+ return self.kind == ProviderKind.CLASS
56
+
57
+ @cached_property
58
+ def is_coroutine(self) -> bool:
59
+ return self.kind == ProviderKind.COROUTINE
60
+
61
+ @cached_property
62
+ def is_generator(self) -> bool:
63
+ return self.kind == ProviderKind.GENERATOR
64
+
65
+ @cached_property
66
+ def is_async_generator(self) -> bool:
67
+ return self.kind == ProviderKind.ASYNC_GENERATOR
68
+
69
+ @cached_property
70
+ def is_async(self) -> bool:
71
+ return self.is_coroutine or self.is_async_generator
72
+
73
+ @cached_property
74
+ def is_resource(self) -> bool:
75
+ return ProviderKind.is_resource(self.kind)
76
+
77
+
78
+ class ProviderDef(NamedTuple):
79
+ call: Callable[..., Any]
80
+ scope: Scope
81
+ interface: Any = NOT_SET
anydi/_scan.py ADDED
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import inspect
5
+ import pkgutil
6
+ from collections.abc import Iterable
7
+ from dataclasses import dataclass
8
+ from types import ModuleType
9
+ from typing import TYPE_CHECKING, Any, Callable, Union
10
+
11
+ from ._decorators import is_injectable
12
+ from ._typing import get_typed_parameters, is_marker
13
+
14
+ if TYPE_CHECKING:
15
+ from ._container import Container
16
+
17
+ Package = Union[ModuleType, str]
18
+ PackageOrIterable = Union[Package, Iterable[Package]]
19
+
20
+
21
+ @dataclass(kw_only=True)
22
+ class ScannedDependency:
23
+ member: Any
24
+ module: ModuleType
25
+
26
+ def __post_init__(self) -> None:
27
+ # Unwrap decorated functions if necessary
28
+ if hasattr(self.member, "__wrapped__"):
29
+ self.member = self.member.__wrapped__
30
+
31
+
32
+ class Scanner:
33
+ def __init__(self, container: Container) -> None:
34
+ self._container = container
35
+
36
+ def scan(
37
+ self, /, packages: PackageOrIterable, *, tags: Iterable[str] | None = None
38
+ ) -> None:
39
+ """Scan packages or modules for decorated members and inject dependencies."""
40
+ if isinstance(packages, (ModuleType, str)):
41
+ scan_packages: Iterable[Package] = [packages]
42
+ else:
43
+ scan_packages = packages
44
+
45
+ dependencies = [
46
+ dependency
47
+ for package in scan_packages
48
+ for dependency in self._scan_package(package, tags=tags)
49
+ ]
50
+
51
+ for dependency in dependencies:
52
+ decorated = self._container.inject()(dependency.member)
53
+ setattr(dependency.module, dependency.member.__name__, decorated)
54
+
55
+ def _scan_package(
56
+ self, package: Package, *, tags: Iterable[str] | None = None
57
+ ) -> list[ScannedDependency]:
58
+ """Scan a package or module for decorated members."""
59
+ tags = list(tags) if tags else []
60
+
61
+ if isinstance(package, str):
62
+ package = importlib.import_module(package)
63
+
64
+ if not hasattr(package, "__path__"):
65
+ return self._scan_module(package, tags=tags)
66
+
67
+ dependencies: list[ScannedDependency] = []
68
+ for module_info in pkgutil.walk_packages(
69
+ package.__path__, prefix=package.__name__ + "."
70
+ ):
71
+ module = importlib.import_module(module_info.name)
72
+ dependencies.extend(self._scan_module(module, tags=tags))
73
+
74
+ return dependencies
75
+
76
+ def _scan_module(
77
+ self, module: ModuleType, *, tags: Iterable[str]
78
+ ) -> list[ScannedDependency]:
79
+ """Scan a module for decorated members."""
80
+ dependencies: list[ScannedDependency] = []
81
+
82
+ for _, member in inspect.getmembers(module, predicate=callable):
83
+ if getattr(member, "__module__", None) != module.__name__:
84
+ continue
85
+
86
+ if self._should_include_member(member, tags=tags):
87
+ dependencies.append(ScannedDependency(member=member, module=module))
88
+
89
+ return dependencies
90
+
91
+ @staticmethod
92
+ def _should_include_member(
93
+ member: Callable[..., Any], *, tags: Iterable[str]
94
+ ) -> bool:
95
+ """Determine if a member should be included based on tags or marker defaults."""
96
+
97
+ if is_injectable(member):
98
+ member_tags = set(member.__injectable__["tags"] or [])
99
+ if tags:
100
+ return bool(set(tags) & member_tags)
101
+ return True # No tags passed → include all injectables
102
+
103
+ # If no tags are passed and not explicitly injectable,
104
+ # check for parameter markers
105
+ if not tags:
106
+ for param in get_typed_parameters(member):
107
+ if is_marker(param.default):
108
+ return True
109
+
110
+ return False
anydi/_scope.py ADDED
@@ -0,0 +1,9 @@
1
+ from typing import Literal
2
+
3
+ Scope = Literal["transient", "singleton", "request"]
4
+
5
+ ALLOWED_SCOPES: dict[Scope, list[Scope]] = {
6
+ "singleton": ["singleton"],
7
+ "request": ["request", "singleton"],
8
+ "transient": ["transient", "request", "singleton"],
9
+ }
@@ -3,29 +3,22 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import builtins
6
- import functools
7
- import importlib
8
6
  import inspect
9
7
  import re
10
8
  import sys
11
9
  from collections.abc import AsyncIterator, Iterator
12
- from types import TracebackType
13
- from typing import Any, Callable, ForwardRef, TypeVar
10
+ from typing import Any, Callable, ForwardRef
14
11
 
15
- import anyio.to_thread
16
- from typing_extensions import ParamSpec, Self, get_args, get_origin
12
+ from typing_extensions import Self, get_args, get_origin
17
13
 
18
14
  try:
19
15
  from types import NoneType
20
16
  except ImportError:
21
- NoneType = type(None) # type: ignore[misc]
17
+ NoneType = type(None)
22
18
 
23
- T = TypeVar("T")
24
- P = ParamSpec("P")
25
19
 
26
-
27
- def get_full_qualname(obj: Any) -> str:
28
- """Get the fully qualified name of an object."""
20
+ def type_repr(obj: Any) -> str:
21
+ """Get a string representation of a type or object."""
29
22
  if isinstance(obj, str):
30
23
  return obj
31
24
 
@@ -36,8 +29,8 @@ def get_full_qualname(obj: Any) -> str:
36
29
  origin = get_origin(obj)
37
30
  # If origin exists, handle generics recursively
38
31
  if origin:
39
- args = ", ".join(get_full_qualname(arg) for arg in get_args(obj))
40
- return f"{get_full_qualname(origin)}[{args}]"
32
+ args = ", ".join(type_repr(arg) for arg in get_args(obj))
33
+ return f"{type_repr(origin)}[{args}]"
41
34
 
42
35
  # Substitute standard library prefixes for clarity
43
36
  full_qualname = f"{module}.{qualname}"
@@ -100,62 +93,49 @@ def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
100
93
  ]
101
94
 
102
95
 
103
- async def run_async(
104
- func: Callable[P, T],
105
- /,
106
- *args: P.args,
107
- **kwargs: P.kwargs,
108
- ) -> T:
109
- """Runs the given function asynchronously using the `anyio` library."""
110
- return await anyio.to_thread.run_sync(functools.partial(func, *args, **kwargs))
111
-
112
-
113
- def import_string(dotted_path: str) -> Any:
114
- """
115
- Import a module or a specific attribute from a module using its dotted string path.
116
- """
117
- try:
118
- module_path, _, attribute_name = dotted_path.rpartition(".")
119
- if module_path:
120
- module = importlib.import_module(module_path)
121
- return getattr(module, attribute_name)
122
- else:
123
- return importlib.import_module(attribute_name)
124
- except (ImportError, AttributeError) as exc:
125
- raise ImportError(f"Cannot import '{dotted_path}': {exc}") from exc
126
-
96
+ class _Marker:
97
+ """A marker class for marking dependencies."""
127
98
 
128
- class AsyncRLock:
129
- def __init__(self) -> None:
130
- self._lock = anyio.Lock()
131
- self._owner: anyio.TaskInfo | None = None
132
- self._count = 0
99
+ __slots__ = ()
133
100
 
134
- async def acquire(self) -> None:
135
- current_task = anyio.get_current_task()
136
- if self._owner == current_task:
137
- self._count += 1
138
- else:
139
- await self._lock.acquire()
140
- self._owner = current_task
141
- self._count = 1
142
-
143
- def release(self) -> None:
144
- if self._owner != anyio.get_current_task():
145
- raise RuntimeError("Lock can only be released by the owner")
146
- self._count -= 1
147
- if self._count == 0:
148
- self._owner = None
149
- self._lock.release()
150
-
151
- async def __aenter__(self) -> Self:
152
- await self.acquire()
101
+ def __call__(self) -> Self:
153
102
  return self
154
103
 
155
- async def __aexit__(
156
- self,
157
- exc_type: type[BaseException] | None,
158
- exc_val: BaseException | None,
159
- exc_tb: TracebackType | None,
160
- ) -> Any:
161
- self.release()
104
+
105
+ def Marker() -> Any:
106
+ return _Marker()
107
+
108
+
109
+ def is_marker(obj: Any) -> bool:
110
+ """Checks if an object is a marker."""
111
+ return isinstance(obj, _Marker)
112
+
113
+
114
+ class Event:
115
+ """Represents an event object."""
116
+
117
+ __slots__ = ()
118
+
119
+
120
+ def is_event_type(obj: Any) -> bool:
121
+ """Checks if an object is an event type."""
122
+ return inspect.isclass(obj) and issubclass(obj, Event)
123
+
124
+
125
+ class _Sentinel:
126
+ __slots__ = ("_name",)
127
+
128
+ def __init__(self, name: str) -> None:
129
+ self._name = name
130
+
131
+ def __repr__(self) -> str:
132
+ return f"<{self._name}>"
133
+
134
+ def __eq__(self, other: object) -> bool:
135
+ return self is other
136
+
137
+ def __hash__(self) -> int:
138
+ return id(self)
139
+
140
+
141
+ NOT_SET = _Sentinel("NOT_SET")
anydi/ext/_utils.py CHANGED
@@ -8,8 +8,8 @@ from typing import Annotated, Any, Callable
8
8
 
9
9
  from typing_extensions import get_args, get_origin
10
10
 
11
- from anydi import Container
12
- from anydi._utils import get_full_qualname
11
+ from anydi._container import Container
12
+ from anydi._typing import type_repr
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
@@ -71,9 +71,9 @@ def patch_call_parameter(
71
71
 
72
72
  if not container.strict and not container.is_registered(parameter.annotation):
73
73
  logger.debug(
74
- f"Callable `{get_full_qualname(call)}` injected parameter "
74
+ f"Callable `{type_repr(call)}` injected parameter "
75
75
  f"`{parameter.name}` with an annotation of "
76
- f"`{get_full_qualname(parameter.annotation)}` "
76
+ f"`{type_repr(parameter.annotation)}` "
77
77
  "is not registered. It will be registered at runtime with the "
78
78
  "first call because it is running in non-strict mode."
79
79
  )
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterator
4
- from functools import wraps
5
4
  from typing import Annotated, Any
6
5
 
7
6
  from django.conf import settings
@@ -9,7 +8,6 @@ from django.core.cache import BaseCache, caches
9
8
  from django.db import connections
10
9
  from django.db.backends.base.base import BaseDatabaseWrapper
11
10
  from django.urls import URLPattern, URLResolver, get_resolver
12
- from typing_extensions import get_origin
13
11
 
14
12
  from anydi import Container
15
13
 
@@ -29,14 +27,11 @@ def register_settings(
29
27
  continue
30
28
 
31
29
  container.register(
32
- Annotated[Any, f"{prefix}{setting_name}"],
30
+ Annotated[type(setting_value), f"{prefix}{setting_name}"],
33
31
  _get_setting_value(setting_value),
34
32
  scope="singleton",
35
33
  )
36
34
 
37
- # Patch AnyDI to resolve Any types for annotated settings
38
- _patch_any_typed_annotated(container, prefix=prefix)
39
-
40
35
 
41
36
  def register_components(container: Container) -> None:
42
37
  """Register Django components into the container."""
@@ -75,7 +70,7 @@ def inject_urlpatterns(container: Container, *, urlconf: str) -> None:
75
70
  if pattern.lookup_str.startswith("ninja."):
76
71
  continue # pragma: no cover
77
72
  pattern.callback = container.inject(pattern.callback)
78
- pattern.callback._injected = True # type: ignore[attr-defined]
73
+ pattern.callback._injected = True # type: ignore
79
74
 
80
75
 
81
76
  def iter_urlpatterns(
@@ -91,38 +86,3 @@ def iter_urlpatterns(
91
86
 
92
87
  def _get_setting_value(value: Any) -> Any:
93
88
  return lambda: value
94
-
95
-
96
- def _any_typed_interface(interface: Any, prefix: str) -> Any:
97
- origin = get_origin(interface)
98
- if origin is not Annotated:
99
- return interface # pragma: no cover
100
- named = interface.__metadata__[-1]
101
-
102
- if isinstance(named, str) and named.startswith(prefix):
103
- _, setting_name = named.rsplit(prefix, maxsplit=1)
104
- return Annotated[Any, f"{prefix}{setting_name}"]
105
- return interface
106
-
107
-
108
- def _patch_any_typed_annotated(container: Container, *, prefix: str) -> None:
109
- def _patch_resolve(resolve: Any) -> Any:
110
- @wraps(resolve)
111
- def wrapper(interface: Any) -> Any:
112
- return resolve(_any_typed_interface(interface, prefix))
113
-
114
- return wrapper
115
-
116
- def _patch_aresolve(resolve: Any) -> Any:
117
- @wraps(resolve)
118
- async def wrapper(interface: Any) -> Any:
119
- return await resolve(_any_typed_interface(interface, prefix))
120
-
121
- return wrapper
122
-
123
- container.resolve = _patch_resolve( # type: ignore[method-assign]
124
- container.resolve
125
- )
126
- container.aresolve = _patch_aresolve( # type: ignore[method-assign]
127
- container.aresolve
128
- )
anydi/ext/django/apps.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import logging
4
3
  import types
5
4
  from typing import Callable, cast
6
5
 
@@ -14,8 +13,6 @@ import anydi
14
13
  from ._settings import get_settings
15
14
  from ._utils import inject_urlpatterns, register_components, register_settings
16
15
 
17
- logger = logging.getLogger(__name__)
18
-
19
16
 
20
17
  class ContainerConfig(AppConfig):
21
18
  name = "anydi.ext.django"
@@ -11,6 +11,6 @@ from ._signature import ViewSignature
11
11
 
12
12
 
13
13
  def patch_ninja() -> None:
14
- operation.ViewSignature = ViewSignature # type: ignore[attr-defined]
15
- operation.Operation = Operation # type: ignore[misc]
16
- operation.AsyncOperation = AsyncOperation # type: ignore[misc]
14
+ operation.ViewSignature = ViewSignature # type: ignore
15
+ operation.Operation = Operation # type: ignore
16
+ operation.AsyncOperation = AsyncOperation # type: ignore