python-injection 0.11.0__py3-none-any.whl → 0.12.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.
- injection/__init__.py +8 -9
- injection/__init__.pyi +48 -13
- injection/_core/__init__.py +1 -1
- injection/_core/common/asynchronous.py +49 -0
- injection/_core/common/invertible.py +3 -4
- injection/_core/common/lazy.py +0 -5
- injection/_core/common/type.py +2 -5
- injection/_core/descriptors.py +3 -3
- injection/_core/hook.py +13 -10
- injection/_core/injectables.py +98 -0
- injection/_core/module.py +246 -165
- injection/integrations/fastapi.py +17 -12
- injection/testing/__init__.pyi +1 -1
- {python_injection-0.11.0.dist-info → python_injection-0.12.1.dist-info}/METADATA +9 -12
- python_injection-0.12.1.dist-info/RECORD +24 -0
- {python_injection-0.11.0.dist-info → python_injection-0.12.1.dist-info}/WHEEL +1 -1
- injection/integrations/blacksheep.py +0 -34
- python_injection-0.11.0.dist-info/RECORD +0 -23
injection/__init__.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from ._core.descriptors import LazyInstance
|
2
|
-
from ._core.
|
2
|
+
from ._core.injectables import Injectable
|
3
|
+
from ._core.module import Mode, Module, Priority, mod
|
3
4
|
|
4
5
|
__all__ = (
|
5
6
|
"Injectable",
|
@@ -7,6 +8,9 @@ __all__ = (
|
|
7
8
|
"Mode",
|
8
9
|
"Module",
|
9
10
|
"Priority",
|
11
|
+
"afind_instance",
|
12
|
+
"aget_instance",
|
13
|
+
"aget_lazy_instance",
|
10
14
|
"constant",
|
11
15
|
"find_instance",
|
12
16
|
"get_instance",
|
@@ -19,14 +23,9 @@ __all__ = (
|
|
19
23
|
"singleton",
|
20
24
|
)
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
return Module.default()
|
26
|
-
|
27
|
-
return Module.from_name(name)
|
28
|
-
|
29
|
-
|
26
|
+
afind_instance = mod().afind_instance
|
27
|
+
aget_instance = mod().aget_instance
|
28
|
+
aget_lazy_instance = mod().aget_lazy_instance
|
30
29
|
constant = mod().constant
|
31
30
|
find_instance = mod().find_instance
|
32
31
|
get_instance = mod().get_instance
|
injection/__init__.pyi
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from abc import abstractmethod
|
2
|
-
from collections.abc import Callable
|
2
|
+
from collections.abc import Awaitable, Callable
|
3
3
|
from contextlib import ContextDecorator
|
4
4
|
from enum import Enum
|
5
5
|
from logging import Logger
|
@@ -21,6 +21,9 @@ from ._core.module import ModeStr, PriorityStr
|
|
21
21
|
|
22
22
|
_: Module = ...
|
23
23
|
|
24
|
+
afind_instance = _.afind_instance
|
25
|
+
aget_instance = _.aget_instance
|
26
|
+
aget_lazy_instance = _.aget_lazy_instance
|
24
27
|
constant = _.constant
|
25
28
|
find_instance = _.find_instance
|
26
29
|
get_instance = _.get_instance
|
@@ -53,59 +56,59 @@ class Module:
|
|
53
56
|
def __contains__(self, cls: _InputType[Any], /) -> bool: ...
|
54
57
|
@property
|
55
58
|
def is_locked(self) -> bool: ...
|
56
|
-
def inject[**P, T](self, wrapped: Callable[P, T] = ..., /)
|
59
|
+
def inject[**P, T](self, wrapped: Callable[P, T] = ..., /) -> Any:
|
57
60
|
"""
|
58
61
|
Decorator applicable to a class or function. Inject function dependencies using
|
59
62
|
parameter type annotations. If applied to a class, the dependencies resolved
|
60
63
|
will be those of the `__init__` method.
|
61
64
|
"""
|
62
65
|
|
63
|
-
def injectable[**P, T](
|
66
|
+
def injectable[**P, T](
|
64
67
|
self,
|
65
|
-
wrapped: Callable[P, T] = ...,
|
68
|
+
wrapped: Callable[P, T] | Callable[P, Awaitable[T]] = ...,
|
66
69
|
/,
|
67
70
|
*,
|
68
71
|
cls: _InjectableFactory[T] = ...,
|
69
72
|
inject: bool = ...,
|
70
73
|
on: _TypeInfo[T] = ...,
|
71
74
|
mode: Mode | ModeStr = ...,
|
72
|
-
):
|
75
|
+
) -> Any:
|
73
76
|
"""
|
74
77
|
Decorator applicable to a class or function. It is used to indicate how the
|
75
78
|
injectable will be constructed. At injection time, a new instance will be
|
76
79
|
injected each time.
|
77
80
|
"""
|
78
81
|
|
79
|
-
def singleton[**P, T](
|
82
|
+
def singleton[**P, T](
|
80
83
|
self,
|
81
|
-
wrapped: Callable[P, T] = ...,
|
84
|
+
wrapped: Callable[P, T] | Callable[P, Awaitable[T]] = ...,
|
82
85
|
/,
|
83
86
|
*,
|
84
87
|
inject: bool = ...,
|
85
88
|
on: _TypeInfo[T] = ...,
|
86
89
|
mode: Mode | ModeStr = ...,
|
87
|
-
):
|
90
|
+
) -> Any:
|
88
91
|
"""
|
89
92
|
Decorator applicable to a class or function. It is used to indicate how the
|
90
93
|
singleton will be constructed. At injection time, the injected instance will
|
91
94
|
always be the same.
|
92
95
|
"""
|
93
96
|
|
94
|
-
def should_be_injectable[T](self, wrapped: type[T] = ..., /)
|
97
|
+
def should_be_injectable[T](self, wrapped: type[T] = ..., /) -> Any:
|
95
98
|
"""
|
96
99
|
Decorator applicable to a class. It is used to specify whether an injectable
|
97
100
|
should be registered. Raise an exception at injection time if the class isn't
|
98
101
|
registered.
|
99
102
|
"""
|
100
103
|
|
101
|
-
def constant[T](
|
104
|
+
def constant[T](
|
102
105
|
self,
|
103
106
|
wrapped: type[T] = ...,
|
104
107
|
/,
|
105
108
|
*,
|
106
109
|
on: _TypeInfo[T] = ...,
|
107
110
|
mode: Mode | ModeStr = ...,
|
108
|
-
):
|
111
|
+
) -> Any:
|
109
112
|
"""
|
110
113
|
Decorator applicable to a class or function. It is used to indicate how the
|
111
114
|
constant is constructed. At injection time, the injected instance will always
|
@@ -131,12 +134,25 @@ class Module:
|
|
131
134
|
wrapped: Callable[P, T],
|
132
135
|
/,
|
133
136
|
) -> Callable[P, T]: ...
|
137
|
+
async def afind_instance[T](self, cls: _InputType[T]) -> T: ...
|
134
138
|
def find_instance[T](self, cls: _InputType[T]) -> T:
|
135
139
|
"""
|
136
140
|
Function used to retrieve an instance associated with the type passed in
|
137
141
|
parameter or an exception will be raised.
|
138
142
|
"""
|
139
143
|
|
144
|
+
@overload
|
145
|
+
async def aget_instance[T, Default](
|
146
|
+
self,
|
147
|
+
cls: _InputType[T],
|
148
|
+
default: Default,
|
149
|
+
) -> T | Default: ...
|
150
|
+
@overload
|
151
|
+
async def aget_instance[T](
|
152
|
+
self,
|
153
|
+
cls: _InputType[T],
|
154
|
+
default: None = ...,
|
155
|
+
) -> T | None: ...
|
140
156
|
@overload
|
141
157
|
def get_instance[T, Default](
|
142
158
|
self,
|
@@ -149,12 +165,28 @@ class Module:
|
|
149
165
|
"""
|
150
166
|
|
151
167
|
@overload
|
152
|
-
def get_instance[T
|
168
|
+
def get_instance[T](
|
153
169
|
self,
|
154
170
|
cls: _InputType[T],
|
155
171
|
default: None = ...,
|
156
172
|
) -> T | None: ...
|
157
173
|
@overload
|
174
|
+
def aget_lazy_instance[T, Default](
|
175
|
+
self,
|
176
|
+
cls: _InputType[T],
|
177
|
+
default: Default,
|
178
|
+
*,
|
179
|
+
cache: bool = ...,
|
180
|
+
) -> Awaitable[T | Default]: ...
|
181
|
+
@overload
|
182
|
+
def aget_lazy_instance[T](
|
183
|
+
self,
|
184
|
+
cls: _InputType[T],
|
185
|
+
default: None = ...,
|
186
|
+
*,
|
187
|
+
cache: bool = ...,
|
188
|
+
) -> Awaitable[T | None]: ...
|
189
|
+
@overload
|
158
190
|
def get_lazy_instance[T, Default](
|
159
191
|
self,
|
160
192
|
cls: _InputType[T],
|
@@ -172,7 +204,7 @@ class Module:
|
|
172
204
|
"""
|
173
205
|
|
174
206
|
@overload
|
175
|
-
def get_lazy_instance[T
|
207
|
+
def get_lazy_instance[T](
|
176
208
|
self,
|
177
209
|
cls: _InputType[T],
|
178
210
|
default: None = ...,
|
@@ -229,6 +261,7 @@ class Module:
|
|
229
261
|
Function to unlock the module by deleting cached instances of singletons.
|
230
262
|
"""
|
231
263
|
|
264
|
+
async def all_ready(self) -> None: ...
|
232
265
|
def add_logger(self, logger: Logger) -> Self: ...
|
233
266
|
@classmethod
|
234
267
|
def from_name(cls, name: str) -> Module:
|
@@ -253,6 +286,8 @@ class Injectable[T](Protocol):
|
|
253
286
|
def is_locked(self) -> bool: ...
|
254
287
|
def unlock(self) -> None: ...
|
255
288
|
@abstractmethod
|
289
|
+
async def aget_instance(self) -> T: ...
|
290
|
+
@abstractmethod
|
256
291
|
def get_instance(self) -> T: ...
|
257
292
|
|
258
293
|
@final
|
injection/_core/__init__.py
CHANGED
@@ -49,5 +49,5 @@ def standardize_input_classes[T](
|
|
49
49
|
@Locator.static_hooks.on_update
|
50
50
|
def standardize_classes[T](*_: Any, **__: Any) -> HookGenerator[Updater[T]]:
|
51
51
|
updater = yield
|
52
|
-
updater.classes =
|
52
|
+
updater.classes = frozenset(standardize_types(*updater.classes))
|
53
53
|
return updater
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from collections.abc import Awaitable, Callable, Generator
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Any, NoReturn, Protocol, runtime_checkable
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
8
|
+
class SimpleAwaitable[T](Awaitable[T]):
|
9
|
+
callable: Callable[..., Awaitable[T]]
|
10
|
+
|
11
|
+
def __await__(self) -> Generator[Any, Any, T]:
|
12
|
+
return self.callable().__await__()
|
13
|
+
|
14
|
+
|
15
|
+
@runtime_checkable
|
16
|
+
class Caller[**P, T](Protocol):
|
17
|
+
__slots__ = ()
|
18
|
+
|
19
|
+
@abstractmethod
|
20
|
+
async def acall(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
21
|
+
raise NotImplementedError
|
22
|
+
|
23
|
+
@abstractmethod
|
24
|
+
def call(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
25
|
+
raise NotImplementedError
|
26
|
+
|
27
|
+
|
28
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
29
|
+
class AsyncCaller[**P, T](Caller[P, T]):
|
30
|
+
callable: Callable[P, Awaitable[T]]
|
31
|
+
|
32
|
+
async def acall(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
33
|
+
return await self.callable(*args, **kwargs)
|
34
|
+
|
35
|
+
def call(self, /, *args: P.args, **kwargs: P.kwargs) -> NoReturn:
|
36
|
+
raise RuntimeError(
|
37
|
+
"Synchronous call isn't supported for an asynchronous Callable."
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
42
|
+
class SyncCaller[**P, T](Caller[P, T]):
|
43
|
+
callable: Callable[P, T]
|
44
|
+
|
45
|
+
async def acall(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
46
|
+
return self.callable(*args, **kwargs)
|
47
|
+
|
48
|
+
def call(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
49
|
+
return self.callable(*args, **kwargs)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from abc import abstractmethod
|
2
2
|
from collections.abc import Callable
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import Protocol,
|
4
|
+
from typing import Protocol, runtime_checkable
|
5
5
|
|
6
6
|
|
7
7
|
@runtime_checkable
|
@@ -13,8 +13,7 @@ class Invertible[T](Protocol):
|
|
13
13
|
|
14
14
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
15
15
|
class SimpleInvertible[T](Invertible[T]):
|
16
|
-
|
16
|
+
callable: Callable[..., T]
|
17
17
|
|
18
|
-
@override
|
19
18
|
def __invert__(self) -> T:
|
20
|
-
return self.
|
19
|
+
return self.callable()
|
injection/_core/common/lazy.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from collections.abc import Callable, Iterator, Mapping
|
2
2
|
from types import MappingProxyType
|
3
|
-
from typing import override
|
4
3
|
|
5
4
|
from injection._core.common.invertible import Invertible
|
6
5
|
|
@@ -14,7 +13,6 @@ class Lazy[T](Invertible[T]):
|
|
14
13
|
def __init__(self, factory: Callable[..., T]) -> None:
|
15
14
|
self.__setup_cache(factory)
|
16
15
|
|
17
|
-
@override
|
18
16
|
def __invert__(self) -> T:
|
19
17
|
return next(self.__iterator)
|
20
18
|
|
@@ -44,15 +42,12 @@ class LazyMapping[K, V](Mapping[K, V]):
|
|
44
42
|
def __init__(self, iterator: Iterator[tuple[K, V]]) -> None:
|
45
43
|
self.__lazy = Lazy(lambda: MappingProxyType(dict(iterator)))
|
46
44
|
|
47
|
-
@override
|
48
45
|
def __getitem__(self, key: K, /) -> V:
|
49
46
|
return (~self.__lazy)[key]
|
50
47
|
|
51
|
-
@override
|
52
48
|
def __iter__(self) -> Iterator[K]:
|
53
49
|
yield from ~self.__lazy
|
54
50
|
|
55
|
-
@override
|
56
51
|
def __len__(self) -> int:
|
57
52
|
return len(~self.__lazy)
|
58
53
|
|
injection/_core/common/type.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
from collections.abc import
|
2
|
-
from inspect import
|
1
|
+
from collections.abc import Callable, Iterable, Iterator
|
2
|
+
from inspect import isfunction
|
3
3
|
from types import GenericAlias, UnionType
|
4
4
|
from typing import (
|
5
5
|
Annotated,
|
@@ -24,9 +24,6 @@ def get_return_types(*args: TypeInfo[Any]) -> Iterator[InputType[Any]]:
|
|
24
24
|
inner_args = arg
|
25
25
|
|
26
26
|
elif isfunction(arg) and (return_type := get_type_hints(arg).get("return")):
|
27
|
-
if iscoroutinefunction(arg):
|
28
|
-
return_type = Awaitable[return_type] # type: ignore[valid-type]
|
29
|
-
|
30
27
|
inner_args = (return_type,)
|
31
28
|
|
32
29
|
else:
|
injection/_core/descriptors.py
CHANGED
@@ -2,7 +2,7 @@ from typing import Self
|
|
2
2
|
|
3
3
|
from injection._core.common.invertible import Invertible
|
4
4
|
from injection._core.common.type import InputType
|
5
|
-
from injection._core.module import Module
|
5
|
+
from injection._core.module import Module, mod
|
6
6
|
|
7
7
|
|
8
8
|
class LazyInstance[T]:
|
@@ -11,8 +11,8 @@ class LazyInstance[T]:
|
|
11
11
|
__value: Invertible[T]
|
12
12
|
|
13
13
|
def __init__(self, cls: InputType[T], module: Module | None = None) -> None:
|
14
|
-
module = module or
|
15
|
-
self.__value = module.get_lazy_instance(cls, default=NotImplemented)
|
14
|
+
module = module or mod()
|
15
|
+
self.__value = module.get_lazy_instance(cls, default=NotImplemented)
|
16
16
|
|
17
17
|
def __get__(
|
18
18
|
self,
|
injection/_core/hook.py
CHANGED
@@ -2,12 +2,13 @@ import itertools
|
|
2
2
|
from collections.abc import Callable, Generator, Iterator
|
3
3
|
from dataclasses import dataclass, field
|
4
4
|
from inspect import isclass, isgeneratorfunction
|
5
|
-
from typing import Any, Self
|
5
|
+
from typing import Any, Self, TypeGuard
|
6
6
|
|
7
7
|
from injection.exceptions import HookError
|
8
8
|
|
9
9
|
type HookGenerator[T] = Generator[None, T, T]
|
10
|
-
type
|
10
|
+
type HookGeneratorFunction[**P, T] = Callable[P, HookGenerator[T]]
|
11
|
+
type HookFunction[**P, T] = Callable[P, T] | HookGeneratorFunction[P, T]
|
11
12
|
|
12
13
|
|
13
14
|
@dataclass(eq=False, frozen=True, slots=True)
|
@@ -18,12 +19,12 @@ class Hook[**P, T]:
|
|
18
19
|
repr=False,
|
19
20
|
)
|
20
21
|
|
21
|
-
def __call__(
|
22
|
+
def __call__(
|
22
23
|
self,
|
23
24
|
wrapped: HookFunction[P, T] | type[HookFunction[P, T]] | None = None,
|
24
25
|
/,
|
25
|
-
):
|
26
|
-
def decorator(wp
|
26
|
+
) -> Any:
|
27
|
+
def decorator(wp: Any) -> Any:
|
27
28
|
self.add(wp() if isclass(wp) else wp)
|
28
29
|
return wp
|
29
30
|
|
@@ -48,11 +49,11 @@ class Hook[**P, T]:
|
|
48
49
|
handler: Callable[P, T],
|
49
50
|
function: HookFunction[P, T],
|
50
51
|
) -> Callable[P, T]:
|
51
|
-
if not cls.
|
52
|
+
if not cls.__is_hook_generator_function(function):
|
52
53
|
return function # type: ignore[return-value]
|
53
54
|
|
54
55
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
55
|
-
hook: HookGenerator[T] = function(*args, **kwargs)
|
56
|
+
hook: HookGenerator[T] = function(*args, **kwargs)
|
56
57
|
|
57
58
|
try:
|
58
59
|
next(hook)
|
@@ -88,9 +89,11 @@ class Hook[**P, T]:
|
|
88
89
|
return handler
|
89
90
|
|
90
91
|
@staticmethod
|
91
|
-
def
|
92
|
-
|
93
|
-
|
92
|
+
def __is_hook_generator_function[**_P, _T](
|
93
|
+
function: HookFunction[_P, _T],
|
94
|
+
) -> TypeGuard[HookGeneratorFunction[_P, _T]]:
|
95
|
+
for fn in function, getattr(function, "__call__", None):
|
96
|
+
if isgeneratorfunction(fn):
|
94
97
|
return True
|
95
98
|
|
96
99
|
return False
|
@@ -0,0 +1,98 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from collections.abc import MutableMapping
|
3
|
+
from contextlib import suppress
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Any, ClassVar, NoReturn, Protocol, runtime_checkable
|
6
|
+
|
7
|
+
from injection._core.common.asynchronous import Caller
|
8
|
+
from injection._core.common.threading import synchronized
|
9
|
+
from injection.exceptions import InjectionError
|
10
|
+
|
11
|
+
|
12
|
+
@runtime_checkable
|
13
|
+
class Injectable[T](Protocol):
|
14
|
+
__slots__ = ()
|
15
|
+
|
16
|
+
@property
|
17
|
+
def is_locked(self) -> bool:
|
18
|
+
return False
|
19
|
+
|
20
|
+
def unlock(self) -> None:
|
21
|
+
return
|
22
|
+
|
23
|
+
@abstractmethod
|
24
|
+
async def aget_instance(self) -> T:
|
25
|
+
raise NotImplementedError
|
26
|
+
|
27
|
+
@abstractmethod
|
28
|
+
def get_instance(self) -> T:
|
29
|
+
raise NotImplementedError
|
30
|
+
|
31
|
+
|
32
|
+
@dataclass(repr=False, frozen=True, slots=True)
|
33
|
+
class BaseInjectable[T](Injectable[T], ABC):
|
34
|
+
factory: Caller[..., T]
|
35
|
+
|
36
|
+
|
37
|
+
class SimpleInjectable[T](BaseInjectable[T]):
|
38
|
+
__slots__ = ()
|
39
|
+
|
40
|
+
async def aget_instance(self) -> T:
|
41
|
+
return await self.factory.acall()
|
42
|
+
|
43
|
+
def get_instance(self) -> T:
|
44
|
+
return self.factory.call()
|
45
|
+
|
46
|
+
|
47
|
+
class SingletonInjectable[T](BaseInjectable[T]):
|
48
|
+
__slots__ = ("__dict__",)
|
49
|
+
|
50
|
+
__key: ClassVar[str] = "$instance"
|
51
|
+
|
52
|
+
@property
|
53
|
+
def cache(self) -> MutableMapping[str, Any]:
|
54
|
+
return self.__dict__
|
55
|
+
|
56
|
+
@property
|
57
|
+
def is_locked(self) -> bool:
|
58
|
+
return self.__key in self.cache
|
59
|
+
|
60
|
+
def unlock(self) -> None:
|
61
|
+
self.cache.clear()
|
62
|
+
|
63
|
+
async def aget_instance(self) -> T:
|
64
|
+
with suppress(KeyError):
|
65
|
+
return self.__check_instance()
|
66
|
+
|
67
|
+
with synchronized():
|
68
|
+
instance = await self.factory.acall()
|
69
|
+
self.__set_instance(instance)
|
70
|
+
|
71
|
+
return instance
|
72
|
+
|
73
|
+
def get_instance(self) -> T:
|
74
|
+
with suppress(KeyError):
|
75
|
+
return self.__check_instance()
|
76
|
+
|
77
|
+
with synchronized():
|
78
|
+
instance = self.factory.call()
|
79
|
+
self.__set_instance(instance)
|
80
|
+
|
81
|
+
return instance
|
82
|
+
|
83
|
+
def __check_instance(self) -> T:
|
84
|
+
return self.cache[self.__key]
|
85
|
+
|
86
|
+
def __set_instance(self, value: T) -> None:
|
87
|
+
self.cache[self.__key] = value
|
88
|
+
|
89
|
+
|
90
|
+
@dataclass(repr=False, frozen=True, slots=True)
|
91
|
+
class ShouldBeInjectable[T](Injectable[T]):
|
92
|
+
cls: type[T]
|
93
|
+
|
94
|
+
async def aget_instance(self) -> T:
|
95
|
+
return self.get_instance()
|
96
|
+
|
97
|
+
def get_instance(self) -> NoReturn:
|
98
|
+
raise InjectionError(f"`{self.cls}` should be an injectable.")
|