jetpytools 1.7.4__py3-none-any.whl → 2.0.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.
Potentially problematic release.
This version of jetpytools might be problematic. Click here for more details.
- jetpytools/_metadata.py +1 -1
- jetpytools/enums/base.py +1 -7
- jetpytools/enums/other.py +5 -5
- jetpytools/exceptions/base.py +8 -31
- jetpytools/exceptions/generic.py +2 -2
- jetpytools/functions/funcs.py +18 -19
- jetpytools/functions/normalize.py +16 -18
- jetpytools/types/builtins.py +12 -70
- jetpytools/types/file.py +2 -6
- jetpytools/types/funcs.py +5 -6
- jetpytools/types/generic.py +5 -5
- jetpytools/types/supports.py +24 -39
- jetpytools/types/utils.py +413 -278
- jetpytools/utils/funcs.py +13 -10
- jetpytools/utils/math.py +3 -5
- jetpytools/utils/ranges.py +9 -50
- {jetpytools-1.7.4.dist-info → jetpytools-2.0.1.dist-info}/METADATA +3 -3
- jetpytools-2.0.1.dist-info/RECORD +33 -0
- jetpytools-1.7.4.dist-info/RECORD +0 -33
- {jetpytools-1.7.4.dist-info → jetpytools-2.0.1.dist-info}/WHEEL +0 -0
- {jetpytools-1.7.4.dist-info → jetpytools-2.0.1.dist-info}/licenses/LICENSE +0 -0
jetpytools/types/utils.py
CHANGED
|
@@ -4,28 +4,28 @@ import sys
|
|
|
4
4
|
from contextlib import suppress
|
|
5
5
|
from functools import wraps
|
|
6
6
|
from inspect import Signature
|
|
7
|
-
from inspect import _empty as empty_param
|
|
8
7
|
from typing import (
|
|
9
8
|
TYPE_CHECKING,
|
|
10
9
|
Any,
|
|
11
10
|
Callable,
|
|
12
11
|
ClassVar,
|
|
13
12
|
Concatenate,
|
|
14
|
-
Generator,
|
|
15
13
|
Generic,
|
|
16
14
|
Iterable,
|
|
17
15
|
Iterator,
|
|
18
16
|
Mapping,
|
|
19
17
|
NoReturn,
|
|
18
|
+
ParamSpec,
|
|
20
19
|
Protocol,
|
|
20
|
+
Self,
|
|
21
21
|
Sequence,
|
|
22
22
|
cast,
|
|
23
23
|
overload,
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
-
from typing_extensions import
|
|
26
|
+
from typing_extensions import TypeVar, deprecated
|
|
27
27
|
|
|
28
|
-
from .builtins import
|
|
28
|
+
from .builtins import KwargsT
|
|
29
29
|
|
|
30
30
|
__all__ = [
|
|
31
31
|
"KwargsNotNone",
|
|
@@ -38,12 +38,11 @@ __all__ = [
|
|
|
38
38
|
"get_subclasses",
|
|
39
39
|
"inject_kwargs_params",
|
|
40
40
|
"inject_self",
|
|
41
|
-
"to_singleton",
|
|
42
41
|
]
|
|
43
42
|
# ruff: noqa: N801
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
def copy_signature(target:
|
|
45
|
+
def copy_signature[F: Callable[..., Any]](target: F, /) -> Callable[[Callable[..., Any]], F]:
|
|
47
46
|
"""
|
|
48
47
|
Utility function to copy the signature of one function to another one.
|
|
49
48
|
|
|
@@ -71,292 +70,441 @@ def copy_signature(target: F0, /) -> Callable[[Callable[..., Any]], F0]:
|
|
|
71
70
|
# another thing
|
|
72
71
|
"""
|
|
73
72
|
|
|
74
|
-
def decorator(wrapped: Callable[..., Any]) ->
|
|
75
|
-
return cast(
|
|
73
|
+
def decorator(wrapped: Callable[..., Any]) -> F:
|
|
74
|
+
return cast(F, wrapped)
|
|
76
75
|
|
|
77
76
|
return decorator
|
|
78
77
|
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@staticmethod
|
|
83
|
-
def __call__(*args: P.args, **kwargs: P.kwargs) -> R_co: ...
|
|
79
|
+
type _InnerInjectSelfType = dict[_InjectSelfMeta, dict[_InjectSelfMeta, _InnerInjectSelfType]]
|
|
80
|
+
_inject_self_cls: _InnerInjectSelfType = {}
|
|
84
81
|
|
|
85
|
-
@overload
|
|
86
|
-
@staticmethod
|
|
87
|
-
def __call__(self: T_co, *args: P.args, **kwargs: P.kwargs) -> R_co: # type: ignore[misc]
|
|
88
|
-
...
|
|
89
82
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
class _InjectSelfMeta(type):
|
|
84
|
+
"""
|
|
85
|
+
Metaclass used to manage subclass relationships and type flattening for the `inject_self` hierarchy.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
_subclasses: frozenset[_InjectSelfMeta]
|
|
89
|
+
"""All descendant metaclasses of a given `inject_self` subclass."""
|
|
93
90
|
|
|
91
|
+
def __new__[MetaSelf: _InjectSelfMeta](
|
|
92
|
+
mcls: type[MetaSelf], name: str, bases: tuple[type, ...], namespace: dict[str, Any], /, **kwargs: Any
|
|
93
|
+
) -> MetaSelf:
|
|
94
|
+
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
clsd = _inject_self_cls.setdefault(cls, {})
|
|
96
97
|
|
|
98
|
+
for k, v in _inject_self_cls.items():
|
|
99
|
+
if k in namespace.values():
|
|
100
|
+
clsd[k] = v
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
cache: bool | None
|
|
100
|
-
signature: Signature | None
|
|
101
|
-
init_kwargs: list[str] | None
|
|
102
|
-
first_key: str | None
|
|
102
|
+
cls._subclasses = frozenset(cls._flatten_cls())
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
return cls
|
|
105
|
+
|
|
106
|
+
def _flatten_cls(cls, d: _InnerInjectSelfType | None = None) -> Iterator[_InjectSelfMeta]:
|
|
105
107
|
"""
|
|
106
|
-
|
|
108
|
+
Recursively flatten and yield all nested inject_self metaclass relationships.
|
|
107
109
|
|
|
108
|
-
:param
|
|
109
|
-
:
|
|
110
|
+
:param d: Optional inner dictionary representing the hierarchy level.
|
|
111
|
+
:yield: Each `_InjectSelfMeta` subclass found in the hierarchy.
|
|
110
112
|
"""
|
|
113
|
+
if d is None:
|
|
114
|
+
d = _inject_self_cls[cls]
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
for k, v in d.items():
|
|
117
|
+
yield k
|
|
118
|
+
yield from cls._flatten_cls(v)
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
def __instancecheck__(cls, instance: Any) -> bool:
|
|
121
|
+
"""Allow isinstance() checks to succeed for any flattened subclass."""
|
|
122
|
+
return any(type.__instancecheck__(t, instance) for t in cls._subclasses)
|
|
116
123
|
|
|
117
|
-
|
|
124
|
+
def __subclasscheck__(cls, subclass: type) -> bool:
|
|
125
|
+
"""Allow issubclass() checks to succeed for any flattened subclass."""
|
|
126
|
+
return any(type.__subclasscheck__(t, subclass) for t in cls._subclasses)
|
|
118
127
|
|
|
119
|
-
self.signature = self.first_key = self.init_kwargs = None
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
_T = TypeVar("_T")
|
|
130
|
+
_T0 = TypeVar("_T0")
|
|
123
131
|
|
|
124
|
-
|
|
132
|
+
_T_co = TypeVar("_T_co", covariant=True)
|
|
133
|
+
_T0_co = TypeVar("_T0_co", covariant=True)
|
|
134
|
+
_T1_co = TypeVar("_T1_co", covariant=True)
|
|
125
135
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class_type: type[T | type[T]] | Any, # type: ignore[valid-type]
|
|
130
|
-
) -> injected_self_func[T_co, P, R_co]:
|
|
131
|
-
if not self.signature or not self.first_key:
|
|
132
|
-
self.signature = Signature.from_callable(self.function, eval_str=True)
|
|
133
|
-
self.first_key = next(iter(list(self.signature.parameters.keys())), None)
|
|
134
|
-
|
|
135
|
-
if isinstance(self, inject_self.init_kwargs):
|
|
136
|
-
from ..exceptions import CustomValueError
|
|
136
|
+
_R_co = TypeVar("_R_co", covariant=True)
|
|
137
|
+
_R0_co = TypeVar("_R0_co", covariant=True)
|
|
138
|
+
_R1_co = TypeVar("_R1_co", covariant=True)
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
140
|
+
_T_Any = TypeVar("_T_Any", default=Any)
|
|
141
|
+
_T0_Any = TypeVar("_T0_Any", default=Any)
|
|
142
|
+
|
|
143
|
+
_P = ParamSpec("_P")
|
|
144
|
+
_P0 = ParamSpec("_P0")
|
|
145
|
+
_P1 = ParamSpec("_P1")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class _InjectedSelfFunc(Protocol[_T_co, _P, _R_co]):
|
|
149
|
+
"""
|
|
150
|
+
Protocol defining the callable interface for wrapped functions under `inject_self`.
|
|
151
|
+
|
|
152
|
+
This allows the injected function to be called in any of the following forms:
|
|
153
|
+
- As a normal function: `f(*args, **kwargs)`
|
|
154
|
+
- As a bound method: `f(self, *args, **kwargs)`
|
|
155
|
+
- As a class method: `f(cls, *args, **kwargs)`
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
@overload
|
|
159
|
+
def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ...
|
|
160
|
+
@overload
|
|
161
|
+
def __call__(_self, self: _T_co, /, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ... # type: ignore[misc] # noqa: N805
|
|
162
|
+
@overload
|
|
163
|
+
def __call__(self, cls: type[_T_co], /, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ... # pyright: ignore[reportGeneralTypeIssues]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
_self_objects_cache = dict[type[Any], Any]()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class _InjectSelfBase(Generic[_T_co, _P, _R_co]):
|
|
170
|
+
"""
|
|
171
|
+
Base descriptor implementation for `inject_self`.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
__isabstractmethod__ = False
|
|
175
|
+
__slots__ = ("_function", "_init_signature", "_signature", "args", "kwargs")
|
|
176
|
+
|
|
177
|
+
_signature: Signature | None
|
|
178
|
+
_init_signature: Signature | None
|
|
179
|
+
|
|
180
|
+
def __init__(self, function: Callable[Concatenate[_T_co, _P], _R_co], /, *args: Any, **kwargs: Any) -> None:
|
|
181
|
+
"""
|
|
182
|
+
Initialize the inject_self descriptor.
|
|
183
|
+
|
|
184
|
+
:param function: The function or method to wrap.
|
|
185
|
+
:param *args: Positional arguments to pass when instantiating the target class.
|
|
186
|
+
:param **kwargs: Keyword arguments to pass when instantiating the target class.
|
|
187
|
+
"""
|
|
188
|
+
self._function = function
|
|
189
|
+
|
|
190
|
+
self._signature = self._init_signature = None
|
|
191
|
+
|
|
192
|
+
self.args = args
|
|
193
|
+
self.kwargs = kwargs
|
|
194
|
+
|
|
195
|
+
def __get__(self, instance: Any | None, owner: type | None = None) -> _InjectedSelfFunc[_T_co, _P, _R_co]: # pyright: ignore[reportGeneralTypeIssues]
|
|
196
|
+
"""
|
|
197
|
+
Return a wrapped callable that automatically injects an instance as the first argument when called.
|
|
198
|
+
"""
|
|
199
|
+
if owner is None:
|
|
200
|
+
owner = type(instance)
|
|
201
|
+
|
|
202
|
+
@wraps(self._function)
|
|
203
|
+
def _wrapper(*args: Any, **kwargs: Any) -> _R_co:
|
|
204
|
+
"""
|
|
205
|
+
Call wrapper that performs the actual injection of `self`.
|
|
206
|
+
"""
|
|
207
|
+
if args and isinstance((first_arg := args[0]), (owner, type(owner))):
|
|
208
|
+
# Instance or class explicitly provided as first argument
|
|
209
|
+
obj = first_arg if isinstance(first_arg, owner) else first_arg() # type: ignore[operator]
|
|
210
|
+
args = args[1:]
|
|
211
|
+
elif instance is None:
|
|
212
|
+
# Accessed via class
|
|
213
|
+
obj, kwargs = self._handle_class_access(owner, kwargs)
|
|
173
214
|
else:
|
|
174
|
-
|
|
215
|
+
# Accessed via instance
|
|
216
|
+
obj = instance
|
|
175
217
|
|
|
176
|
-
return self.
|
|
218
|
+
return self._function(obj, *args, **kwargs) # type: ignore[arg-type]
|
|
177
219
|
|
|
178
220
|
return _wrapper
|
|
179
221
|
|
|
180
|
-
def
|
|
181
|
-
|
|
222
|
+
def _handle_class_access[T](self, owner: type[T], kwargs: dict[str, Any]) -> tuple[T, dict[str, Any]]:
|
|
223
|
+
"""
|
|
224
|
+
Handle logic when the descriptor is accessed from the class level.
|
|
225
|
+
|
|
226
|
+
:param owner: The class object owning the descriptor.
|
|
227
|
+
:param kwargs: Keyword arguments passed to the wrapped function.
|
|
228
|
+
:return: A tuple of `(self_object, updated_kwargs)`.
|
|
229
|
+
"""
|
|
230
|
+
if isinstance(self, inject_self.cached):
|
|
231
|
+
# Cached instance creation
|
|
232
|
+
try:
|
|
233
|
+
return _self_objects_cache[owner], kwargs
|
|
234
|
+
except KeyError:
|
|
235
|
+
return _self_objects_cache.setdefault(owner, owner()), kwargs
|
|
236
|
+
|
|
237
|
+
if isinstance(self, (inject_self.init_kwargs, inject_self.init_kwargs.clean)):
|
|
238
|
+
# Constructor accepts forwarded kwargs
|
|
239
|
+
has_kwargs = any(
|
|
240
|
+
param.kind in (param.VAR_KEYWORD, param.KEYWORD_ONLY)
|
|
241
|
+
for param in self.__signature__.parameters.values()
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if not has_kwargs:
|
|
245
|
+
from ..exceptions import CustomValueError
|
|
246
|
+
|
|
247
|
+
raise CustomValueError(
|
|
248
|
+
f"Function {self._function.__name__} doesn't accept keyword arguments.",
|
|
249
|
+
"inject_self.init_kwargs",
|
|
250
|
+
self._function,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if not self._init_signature:
|
|
254
|
+
self._init_signature = Signature.from_callable(owner)
|
|
255
|
+
|
|
256
|
+
init_kwargs = self.kwargs | {k: kwargs[k] for k in kwargs.keys() & self._init_signature.parameters.keys()}
|
|
257
|
+
|
|
258
|
+
obj = owner(*self.args, **init_kwargs)
|
|
259
|
+
|
|
260
|
+
if isinstance(self, inject_self.init_kwargs.clean):
|
|
261
|
+
# Clean up forwarded kwargs
|
|
262
|
+
kwargs = {k: v for k, v in kwargs.items() if k not in self._init_signature.parameters}
|
|
263
|
+
|
|
264
|
+
return obj, kwargs
|
|
265
|
+
|
|
266
|
+
return owner(*self.args, **self.kwargs), kwargs
|
|
267
|
+
|
|
268
|
+
def __call__(self_, self: _T_co, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: # type: ignore[misc] # noqa: N805
|
|
269
|
+
return self_.__get__(self, type(self))(*args, **kwargs)
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def __func__(self) -> Callable[Concatenate[_T_co, _P], _R_co]:
|
|
273
|
+
"""Return the original wrapped function."""
|
|
274
|
+
return self._function
|
|
182
275
|
|
|
183
276
|
@property
|
|
184
277
|
def __signature__(self) -> Signature:
|
|
185
|
-
|
|
278
|
+
"""Return (and cache) the signature of the wrapped function."""
|
|
279
|
+
if not self._signature:
|
|
280
|
+
self._signature = Signature.from_callable(self._function)
|
|
281
|
+
return self._signature
|
|
186
282
|
|
|
187
283
|
@classmethod
|
|
188
|
-
def with_args(
|
|
284
|
+
def with_args[T0, **P0, R0](
|
|
189
285
|
cls, *args: Any, **kwargs: Any
|
|
190
|
-
) -> Callable[[Callable[Concatenate[
|
|
191
|
-
"""
|
|
286
|
+
) -> Callable[[Callable[Concatenate[T0, P0], R0]], inject_self[T0, P0, R0]]:
|
|
287
|
+
"""
|
|
288
|
+
Decorator factory to construct an `inject_self` or subclass (`cached`, `init_kwargs`, etc.)
|
|
289
|
+
with specific instantiation arguments.
|
|
290
|
+
"""
|
|
192
291
|
|
|
292
|
+
# TODO: The precise subclass type cannot be expressed yet when the class is itself generic.
|
|
193
293
|
def _wrapper(function: Callable[Concatenate[T0, P0], R0]) -> inject_self[T0, P0, R0]:
|
|
194
|
-
|
|
195
|
-
inj.args = args
|
|
196
|
-
inj.kwargs = kwargs
|
|
197
|
-
return inj # type: ignore
|
|
294
|
+
return cls(function, *args, **kwargs) # type: ignore[return-value, arg-type]
|
|
198
295
|
|
|
199
296
|
return _wrapper
|
|
200
297
|
|
|
201
298
|
|
|
202
|
-
class inject_self(
|
|
203
|
-
"""
|
|
299
|
+
class inject_self(_InjectSelfBase[_T_co, _P, _R_co], metaclass=_InjectSelfMeta):
|
|
300
|
+
"""
|
|
301
|
+
Descriptor that ensures the wrapped function always has a constructed `self`.
|
|
302
|
+
|
|
303
|
+
When accessed via a class, it will automatically instantiate an object before calling the function.
|
|
304
|
+
When accessed via an instance, it simply binds.
|
|
305
|
+
|
|
306
|
+
Subclasses such as `cached`, `init_kwargs`, and `init_kwargs.clean`
|
|
307
|
+
define variations in how the injected object is created or reused.
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
__slots__ = ()
|
|
204
311
|
|
|
205
|
-
class cached(
|
|
312
|
+
class cached(_InjectSelfBase[_T0_co, _P0, _R0_co], metaclass=_InjectSelfMeta):
|
|
206
313
|
"""
|
|
207
|
-
|
|
208
|
-
|
|
314
|
+
Variant of `inject_self` that caches the constructed instance.
|
|
315
|
+
|
|
316
|
+
The first time the method is accessed via the class, a `self` object is created and stored.
|
|
317
|
+
Subsequent calls reuse it.
|
|
209
318
|
"""
|
|
210
319
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
320
|
+
__slots__ = ()
|
|
321
|
+
|
|
322
|
+
class property(Generic[_T1_co, _P1, _R1_co], metaclass=_InjectSelfMeta):
|
|
323
|
+
"""Property variant of `inject_self.cached` that auto-calls the wrapped method."""
|
|
214
324
|
|
|
215
|
-
|
|
216
|
-
return self.function.__get__(class_obj, class_type)()
|
|
325
|
+
__slots__ = ("__func__",)
|
|
217
326
|
|
|
218
|
-
|
|
327
|
+
def __init__(self, function: Callable[[_T1_co], _R1_co], /) -> None:
|
|
328
|
+
self.__func__ = inject_self.cached(function)
|
|
329
|
+
|
|
330
|
+
def __get__(self, instance: _T1_co | None, owner: type[_T1_co]) -> _R1_co: # pyright: ignore[reportGeneralTypeIssues]
|
|
331
|
+
"""Return the result of calling the cached method without arguments."""
|
|
332
|
+
return self.__func__.__get__(instance, owner)()
|
|
333
|
+
|
|
334
|
+
class init_kwargs(_InjectSelfBase[_T0_co, _P0, _R0_co], metaclass=_InjectSelfMeta):
|
|
219
335
|
"""
|
|
220
|
-
|
|
221
|
-
|
|
336
|
+
Variant of `inject_self` that forwards function keyword arguments to the class constructor
|
|
337
|
+
when instantiating `self`.
|
|
222
338
|
"""
|
|
223
339
|
|
|
224
|
-
|
|
225
|
-
def clean(cls, function: Callable[Concatenate[T1_co, P1], R1_co]) -> inject_self[T1_co, P1, R1_co]:
|
|
226
|
-
"""Wrap a method, pass kwargs to the constructor and remove them from actual **kwargs."""
|
|
227
|
-
inj = cls(function) # type: ignore
|
|
228
|
-
inj.clean_kwargs = True
|
|
229
|
-
return inj # type: ignore
|
|
340
|
+
__slots__ = ()
|
|
230
341
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
342
|
+
class clean(_InjectSelfBase[_T1_co, _P1, _R1_co], metaclass=_InjectSelfMeta):
|
|
343
|
+
"""
|
|
344
|
+
Variant of `inject_self.init_kwargs` that removes any forwarded kwargs from the final function call
|
|
345
|
+
after using them for construction.
|
|
346
|
+
"""
|
|
234
347
|
|
|
235
|
-
|
|
236
|
-
return self.function.__get__(class_obj, class_type)()
|
|
348
|
+
__slots__ = ()
|
|
237
349
|
|
|
350
|
+
class property(Generic[_T0_co, _R0_co], metaclass=_InjectSelfMeta):
|
|
351
|
+
"""Property variant of `inject_self` that auto-calls the wrapped method."""
|
|
238
352
|
|
|
239
|
-
|
|
240
|
-
def __call__(self: T_co, *args: P.args, **kwargs: P.kwargs) -> R_co:
|
|
241
|
-
raise NotImplementedError
|
|
353
|
+
__slots__ = ("__func__",)
|
|
242
354
|
|
|
355
|
+
def __init__(self, function: Callable[[_T0_co], _R0_co], /) -> None:
|
|
356
|
+
self.__func__ = inject_self(function)
|
|
243
357
|
|
|
244
|
-
|
|
245
|
-
|
|
358
|
+
def __get__(self, instance: _T0_co | None, owner: type[_T0_co]) -> _R0_co: # pyright: ignore[reportGeneralTypeIssues]
|
|
359
|
+
"""Return the result of calling the injected method without arguments."""
|
|
360
|
+
return self.__func__.__get__(instance, owner)()
|
|
246
361
|
|
|
247
|
-
_kwargs_name = "kwargs"
|
|
248
362
|
|
|
249
|
-
|
|
250
|
-
|
|
363
|
+
class _InjectKwargsParamsBase(Generic[_T_co, _P, _R_co]):
|
|
364
|
+
"""
|
|
365
|
+
Base descriptor implementation for `inject_kwargs_params`.
|
|
366
|
+
"""
|
|
251
367
|
|
|
252
|
-
|
|
368
|
+
__isabstractmethod__ = False
|
|
369
|
+
__slots__ = ("_function", "_signature")
|
|
253
370
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
self.signature = Signature.from_callable(self.function, eval_str=True)
|
|
371
|
+
_kwargs_name = "kwargs"
|
|
372
|
+
_signature: Signature | None
|
|
257
373
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
):
|
|
262
|
-
from ..exceptions import CustomValueError
|
|
374
|
+
def __init__(self, func: Callable[Concatenate[_T_co, _P], _R_co], /) -> None:
|
|
375
|
+
"""
|
|
376
|
+
Initialize the inject_kwargs_params descriptor.
|
|
263
377
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
378
|
+
:param function: The target function or method whose parameters will be injected
|
|
379
|
+
from the instance's `self.kwargs` mapping.
|
|
380
|
+
"""
|
|
381
|
+
self._function = func
|
|
382
|
+
self._signature = None
|
|
267
383
|
|
|
268
|
-
|
|
384
|
+
@overload
|
|
385
|
+
def __get__(self, instance: None, owner: type) -> Self: ...
|
|
386
|
+
@overload
|
|
387
|
+
def __get__(self, instance: Any, owner: type | None = None) -> Callable[_P, _R_co]: ...
|
|
388
|
+
def __get__(self, instance: Any | None, owner: type | None = None) -> Any:
|
|
389
|
+
"""
|
|
390
|
+
Descriptor binding logic.
|
|
269
391
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
assert this.signature
|
|
392
|
+
When accessed via an instance, returns a wrapped function that injects values from `instance.<_kwargs_name>`
|
|
393
|
+
into matching function parameters.
|
|
273
394
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
395
|
+
When accessed via the class, returns the descriptor itself.
|
|
396
|
+
"""
|
|
397
|
+
if instance is None:
|
|
398
|
+
return self
|
|
277
399
|
|
|
278
|
-
|
|
279
|
-
|
|
400
|
+
if not hasattr(instance, self._kwargs_name):
|
|
401
|
+
from ..exceptions import CustomRuntimeError
|
|
280
402
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
403
|
+
raise CustomRuntimeError(
|
|
404
|
+
f'Missing attribute "{self._kwargs_name}" on {type(instance).__name__}.', owner, self.__class__
|
|
405
|
+
)
|
|
284
406
|
|
|
285
|
-
|
|
286
|
-
|
|
407
|
+
@wraps(self._function)
|
|
408
|
+
def wrapper(*args: Any, **kwargs: Any) -> _R_co:
|
|
409
|
+
"""
|
|
410
|
+
Wrapper that performs parameter injection before calling the wrapped function.
|
|
411
|
+
"""
|
|
412
|
+
injectable_kwargs = dict(getattr(instance, self._kwargs_name))
|
|
287
413
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
continue
|
|
414
|
+
if not injectable_kwargs:
|
|
415
|
+
return self._function(instance, *args, **kwargs)
|
|
291
416
|
|
|
292
|
-
|
|
417
|
+
args_list = [instance, *args]
|
|
418
|
+
kwargs = kwargs.copy()
|
|
293
419
|
|
|
294
|
-
|
|
420
|
+
for i, (name, param) in enumerate(self.__signature__.parameters.items()):
|
|
421
|
+
if name not in injectable_kwargs:
|
|
295
422
|
continue
|
|
296
423
|
|
|
297
|
-
|
|
298
|
-
if args[i] != value.default:
|
|
299
|
-
continue
|
|
424
|
+
value_from_kwargs = injectable_kwargs.pop(name)
|
|
300
425
|
|
|
301
|
-
|
|
426
|
+
if i < len(args_list):
|
|
427
|
+
# Positional arg case
|
|
428
|
+
if args_list[i] == param.default:
|
|
429
|
+
args_list[i] = value_from_kwargs
|
|
302
430
|
else:
|
|
303
|
-
|
|
304
|
-
|
|
431
|
+
# Keyword arg case
|
|
432
|
+
if kwargs.get(name, param.default) == param.default:
|
|
433
|
+
kwargs[name] = value_from_kwargs
|
|
305
434
|
|
|
306
|
-
|
|
435
|
+
# Merge leftover kwargs if subclass allows
|
|
436
|
+
if isinstance(self, inject_kwargs_params.add_to_kwargs):
|
|
437
|
+
kwargs |= injectable_kwargs
|
|
307
438
|
|
|
308
|
-
|
|
309
|
-
kwargs |= this_kwargs
|
|
439
|
+
return self._function(*tuple(args_list), **kwargs)
|
|
310
440
|
|
|
311
|
-
|
|
441
|
+
return wrapper
|
|
312
442
|
|
|
313
|
-
|
|
443
|
+
def __call__(self_, self: _T_co, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: # type: ignore[misc] # noqa: N805
|
|
444
|
+
return self_.__get__(self, type(self))(*args, **kwargs)
|
|
314
445
|
|
|
315
|
-
|
|
316
|
-
|
|
446
|
+
@property
|
|
447
|
+
def __func__(self) -> Callable[Concatenate[_T_co, _P], _R_co]:
|
|
448
|
+
"""Return the original wrapped function."""
|
|
449
|
+
return self._function
|
|
317
450
|
|
|
318
451
|
@property
|
|
319
452
|
def __signature__(self) -> Signature:
|
|
320
|
-
|
|
453
|
+
"""Return (and cache) the signature of the wrapped function."""
|
|
454
|
+
if not self._signature:
|
|
455
|
+
self._signature = Signature.from_callable(self._function)
|
|
456
|
+
return self._signature
|
|
321
457
|
|
|
322
458
|
@classmethod
|
|
323
|
-
def with_name
|
|
324
|
-
|
|
325
|
-
|
|
459
|
+
def with_name[T0, **P0, R0](
|
|
460
|
+
cls, kwargs_name: str = "kwargs"
|
|
461
|
+
) -> Callable[[Callable[Concatenate[T0, P0], R0]], inject_kwargs_params[T0, P0, R0]]:
|
|
462
|
+
"""
|
|
463
|
+
Decorator factory that creates a subclass of `inject_kwargs_params` with a custom name
|
|
464
|
+
for the keyword argument store.
|
|
465
|
+
"""
|
|
466
|
+
ns = cls.__dict__.copy()
|
|
467
|
+
ns["_kwargs_name"] = kwargs_name
|
|
468
|
+
|
|
469
|
+
custom_cls = type(cls.__name__, cls.__bases__, ns)
|
|
326
470
|
|
|
327
|
-
|
|
471
|
+
# TODO: The precise subclass type cannot be expressed yet when the class is itself generic.
|
|
472
|
+
def _wrapper(function: Callable[Concatenate[T0, P0], R0]) -> inject_kwargs_params[T0, P0, R0]:
|
|
473
|
+
return custom_cls(function) # pyright: ignore[reportReturnType, reportArgumentType]
|
|
328
474
|
|
|
475
|
+
return _wrapper
|
|
329
476
|
|
|
330
|
-
if TYPE_CHECKING: # love you mypy...
|
|
331
477
|
|
|
332
|
-
|
|
333
|
-
|
|
478
|
+
class inject_kwargs_params(_InjectKwargsParamsBase[_T_co, _P, _R_co]):
|
|
479
|
+
"""
|
|
480
|
+
Descriptor that injects parameters into functions based on an instance's keyword mapping.
|
|
334
481
|
|
|
335
|
-
|
|
336
|
-
|
|
482
|
+
When a method wrapped with `@inject_kwargs_params` is called, the descriptor inspects the function's signature
|
|
483
|
+
and replaces any arguments matching keys in `self.kwargs` (or another mapping defined by `_kwargs_name`)
|
|
484
|
+
if their values equal the parameter's default.
|
|
485
|
+
"""
|
|
337
486
|
|
|
338
|
-
|
|
487
|
+
__slots__ = ()
|
|
339
488
|
|
|
340
|
-
|
|
341
|
-
|
|
489
|
+
class add_to_kwargs(_InjectKwargsParamsBase[_T0_co, _P0, _R0_co]):
|
|
490
|
+
"""
|
|
491
|
+
Variant of `inject_kwargs_params` that merges unused entries from `self.kwargs` into the keyword arguments
|
|
492
|
+
passed to the target function.
|
|
342
493
|
|
|
343
|
-
|
|
344
|
-
|
|
494
|
+
This allows additional context or configuration values to be forwarded without requiring explicit parameters.
|
|
495
|
+
"""
|
|
345
496
|
|
|
497
|
+
__slots__ = ()
|
|
346
498
|
|
|
347
|
-
class complex_hash(Generic[T]):
|
|
348
|
-
"""
|
|
349
|
-
Decorator for classes to add a ``__hash__`` method to them.
|
|
350
499
|
|
|
351
|
-
|
|
352
|
-
""
|
|
500
|
+
class _ComplexHash[**P, R]:
|
|
501
|
+
__slots__ = "func"
|
|
353
502
|
|
|
354
|
-
def
|
|
355
|
-
|
|
356
|
-
def __hash__(self) -> int:
|
|
357
|
-
return complex_hash.hash(self.__class__.__name__, *(getattr(self, key) for key in self.__annotations__))
|
|
503
|
+
def __init__(self, func: Callable[P, R]) -> None:
|
|
504
|
+
self.func = func
|
|
358
505
|
|
|
359
|
-
|
|
506
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
507
|
+
return self.func(*args, **kwargs)
|
|
360
508
|
|
|
361
509
|
@staticmethod
|
|
362
510
|
def hash(*args: Any) -> int:
|
|
@@ -380,7 +528,24 @@ class complex_hash(Generic[T]):
|
|
|
380
528
|
return hash("_".join(values))
|
|
381
529
|
|
|
382
530
|
|
|
383
|
-
|
|
531
|
+
@_ComplexHash
|
|
532
|
+
def complex_hash[T](cls: type[T]) -> type[T]:
|
|
533
|
+
"""
|
|
534
|
+
Decorator for classes to add a ``__hash__`` method to them.
|
|
535
|
+
|
|
536
|
+
Especially useful for NamedTuples.
|
|
537
|
+
"""
|
|
538
|
+
|
|
539
|
+
def __hash__(self: T) -> int: # noqa: N807
|
|
540
|
+
return complex_hash.hash(self.__class__.__name__, *(getattr(self, key) for key in self.__annotations__))
|
|
541
|
+
|
|
542
|
+
ns = cls.__dict__.copy()
|
|
543
|
+
ns["__hash__"] = __hash__
|
|
544
|
+
|
|
545
|
+
return type(cls.__name__, (cls,), ns) # pyright: ignore[reportReturnType]
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def get_subclasses[T](family: type[T], exclude: Sequence[type[T]] = []) -> list[type[T]]:
|
|
384
549
|
"""
|
|
385
550
|
Get all subclasses of a given type.
|
|
386
551
|
|
|
@@ -391,7 +556,7 @@ def get_subclasses(family: type[T], exclude: Sequence[type[T]] = []) -> list[typ
|
|
|
391
556
|
:return: List of all subclasses of "family".
|
|
392
557
|
"""
|
|
393
558
|
|
|
394
|
-
def _subclasses(cls: type[T]) ->
|
|
559
|
+
def _subclasses(cls: type[T]) -> Iterator[type[T]]:
|
|
395
560
|
for subclass in cls.__subclasses__():
|
|
396
561
|
yield from _subclasses(subclass)
|
|
397
562
|
if subclass in exclude:
|
|
@@ -401,24 +566,20 @@ def get_subclasses(family: type[T], exclude: Sequence[type[T]] = []) -> list[typ
|
|
|
401
566
|
return list(set(_subclasses(family)))
|
|
402
567
|
|
|
403
568
|
|
|
404
|
-
|
|
405
|
-
_T0_Any = TypeVar("_T0_Any", default=Any)
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
class classproperty_base(Generic[T, R_co, _T_Any]):
|
|
569
|
+
class classproperty_base(Generic[_T, _R_co, _T_Any]):
|
|
409
570
|
__isabstractmethod__: bool = False
|
|
410
571
|
|
|
411
|
-
fget: Callable[[type[
|
|
412
|
-
fset: Callable[Concatenate[type[
|
|
413
|
-
fdel: Callable[[type[
|
|
572
|
+
fget: Callable[[type[_T]], _R_co]
|
|
573
|
+
fset: Callable[Concatenate[type[_T], _T_Any, ...], None] | None
|
|
574
|
+
fdel: Callable[[type[_T]], None] | None
|
|
414
575
|
|
|
415
576
|
def __init__(
|
|
416
577
|
self,
|
|
417
|
-
fget: Callable[[type[
|
|
418
|
-
fset: Callable[Concatenate[type[
|
|
419
|
-
| classmethod[
|
|
578
|
+
fget: Callable[[type[_T]], _R_co] | classmethod[_T, ..., _R_co],
|
|
579
|
+
fset: Callable[Concatenate[type[_T], _T_Any, ...], None]
|
|
580
|
+
| classmethod[_T, Concatenate[_T_Any, ...], None]
|
|
420
581
|
| None = None,
|
|
421
|
-
fdel: Callable[[type[
|
|
582
|
+
fdel: Callable[[type[_T]], None] | classmethod[_T, ..., None] | None = None,
|
|
422
583
|
doc: str | None = None,
|
|
423
584
|
) -> None:
|
|
424
585
|
self.fget = fget.__func__ if isinstance(fget, classmethod) else fget
|
|
@@ -431,69 +592,67 @@ class classproperty_base(Generic[T, R_co, _T_Any]):
|
|
|
431
592
|
def __set_name__(self, owner: object, name: str) -> None:
|
|
432
593
|
self.__name__ = name
|
|
433
594
|
|
|
434
|
-
def _get_cache(self,
|
|
595
|
+
def _get_cache(self, owner: type[_T]) -> dict[str, Any]:
|
|
435
596
|
cache_key = getattr(self, "cache_key")
|
|
436
597
|
|
|
437
|
-
if not hasattr(
|
|
438
|
-
setattr(
|
|
598
|
+
if not hasattr(owner, cache_key):
|
|
599
|
+
setattr(owner, cache_key, {})
|
|
439
600
|
|
|
440
|
-
return getattr(
|
|
601
|
+
return getattr(owner, cache_key)
|
|
441
602
|
|
|
442
|
-
def __get__(self,
|
|
443
|
-
if
|
|
444
|
-
|
|
445
|
-
elif type_ is None:
|
|
446
|
-
raise NotImplementedError("Both obj and type_ are None")
|
|
603
|
+
def __get__(self, instance: Any | None, owner: type | None = None) -> _R_co:
|
|
604
|
+
if owner is None:
|
|
605
|
+
owner = type(instance)
|
|
447
606
|
|
|
448
607
|
if not isinstance(self, classproperty.cached):
|
|
449
|
-
return self.fget(
|
|
608
|
+
return self.fget(owner)
|
|
450
609
|
|
|
451
|
-
if self.__name__ in (cache := self._get_cache(
|
|
610
|
+
if self.__name__ in (cache := self._get_cache(owner)):
|
|
452
611
|
return cache[self.__name__]
|
|
453
612
|
|
|
454
|
-
value = self.fget(
|
|
613
|
+
value = self.fget(owner)
|
|
455
614
|
cache[self.__name__] = value
|
|
456
615
|
return value
|
|
457
616
|
|
|
458
|
-
def __set__(self,
|
|
617
|
+
def __set__(self, instance: Any, value: _T_Any) -> None:
|
|
459
618
|
if not self.fset:
|
|
460
619
|
raise AttributeError(
|
|
461
|
-
f'classproperty with getter "{self.__name__}" of "{
|
|
620
|
+
f'classproperty with getter "{self.__name__}" of "{instance.__class__.__name__}" object has no setter.'
|
|
462
621
|
)
|
|
463
622
|
|
|
464
|
-
|
|
623
|
+
owner = type(instance)
|
|
465
624
|
|
|
466
625
|
if not isinstance(self, classproperty.cached):
|
|
467
|
-
return self.fset(
|
|
626
|
+
return self.fset(owner, value)
|
|
468
627
|
|
|
469
|
-
if self.__name__ in (cache := self._get_cache(
|
|
628
|
+
if self.__name__ in (cache := self._get_cache(owner)):
|
|
470
629
|
del cache[self.__name__]
|
|
471
630
|
|
|
472
|
-
self.fset(
|
|
631
|
+
self.fset(owner, value)
|
|
473
632
|
|
|
474
|
-
def __delete__(self,
|
|
633
|
+
def __delete__(self, instance: Any) -> None:
|
|
475
634
|
if not self.fdel:
|
|
476
635
|
raise AttributeError(
|
|
477
|
-
f'classproperty with getter "{self.__name__}" of "{
|
|
636
|
+
f'classproperty with getter "{self.__name__}" of "{instance.__class__.__name__}" object has no deleter.'
|
|
478
637
|
)
|
|
479
638
|
|
|
480
|
-
|
|
639
|
+
owner = type(instance)
|
|
481
640
|
|
|
482
641
|
if not isinstance(self, classproperty.cached):
|
|
483
|
-
return self.fdel(
|
|
642
|
+
return self.fdel(owner)
|
|
484
643
|
|
|
485
|
-
if self.__name__ in (cache := self._get_cache(
|
|
644
|
+
if self.__name__ in (cache := self._get_cache(owner)):
|
|
486
645
|
del cache[self.__name__]
|
|
487
646
|
|
|
488
|
-
self.fdel(
|
|
647
|
+
self.fdel(owner)
|
|
489
648
|
|
|
490
649
|
|
|
491
|
-
class classproperty(classproperty_base[
|
|
650
|
+
class classproperty(classproperty_base[_T, _R_co, _T_Any]):
|
|
492
651
|
"""
|
|
493
652
|
A combination of `classmethod` and `property`.
|
|
494
653
|
"""
|
|
495
654
|
|
|
496
|
-
class cached(classproperty_base[
|
|
655
|
+
class cached(classproperty_base[_T0, _R0_co, _T0_Any]):
|
|
497
656
|
"""
|
|
498
657
|
A combination of `classmethod` and `property`.
|
|
499
658
|
|
|
@@ -526,7 +685,7 @@ class classproperty(classproperty_base[T, R_co, _T_Any]):
|
|
|
526
685
|
del cache[name]
|
|
527
686
|
|
|
528
687
|
|
|
529
|
-
class cachedproperty(property, Generic[
|
|
688
|
+
class cachedproperty(property, Generic[_R_co, _T_Any]):
|
|
530
689
|
"""
|
|
531
690
|
Wrapper for a one-time get property, that will be cached.
|
|
532
691
|
|
|
@@ -547,17 +706,17 @@ class cachedproperty(property, Generic[R_co, _T_Any]):
|
|
|
547
706
|
|
|
548
707
|
def __init__(
|
|
549
708
|
self,
|
|
550
|
-
fget: Callable[[Any],
|
|
709
|
+
fget: Callable[[Any], _R_co],
|
|
551
710
|
fset: Callable[[Any, _T_Any], None] | None = None,
|
|
552
711
|
fdel: Callable[[Any], None] | None = None,
|
|
553
712
|
doc: str | None = None,
|
|
554
713
|
) -> None: ...
|
|
555
714
|
|
|
556
|
-
def getter(self, fget: Callable[...,
|
|
715
|
+
def getter(self, fget: Callable[..., _R_co]) -> cachedproperty[_R_co, _T_Any]: ...
|
|
557
716
|
|
|
558
|
-
def setter(self, fset: Callable[[Any, _T_Any], None]) -> cachedproperty[
|
|
717
|
+
def setter(self, fset: Callable[[Any, _T_Any], None]) -> cachedproperty[_R_co, _T_Any]: ...
|
|
559
718
|
|
|
560
|
-
def deleter(self, fdel: Callable[..., None]) -> cachedproperty[
|
|
719
|
+
def deleter(self, fdel: Callable[..., None]) -> cachedproperty[_R_co, _T_Any]: ...
|
|
561
720
|
|
|
562
721
|
if sys.version_info < (3, 13):
|
|
563
722
|
|
|
@@ -569,7 +728,7 @@ class cachedproperty(property, Generic[R_co, _T_Any]):
|
|
|
569
728
|
def __get__(self, instance: None, owner: type | None = None) -> Self: ...
|
|
570
729
|
|
|
571
730
|
@overload
|
|
572
|
-
def __get__(self, instance: Any, owner: type | None = None) ->
|
|
731
|
+
def __get__(self, instance: Any, owner: type | None = None) -> _R_co: ...
|
|
573
732
|
|
|
574
733
|
def __get__(self, instance: Any, owner: type | None = None) -> Any:
|
|
575
734
|
if instance is None:
|
|
@@ -629,18 +788,20 @@ class cachedproperty(property, Generic[R_co, _T_Any]):
|
|
|
629
788
|
class KwargsNotNone(KwargsT):
|
|
630
789
|
"""Remove all None objects from this kwargs dict."""
|
|
631
790
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return KwargsT(**{key: value for key, value in KwargsT(*args, **kwargs).items() if value is not None})
|
|
791
|
+
@copy_signature(KwargsT.__init__)
|
|
792
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
793
|
+
super().__init__({key: value for key, value in dict(*args, **kwargs).items() if value is not None})
|
|
636
794
|
|
|
637
795
|
|
|
638
796
|
class SingletonMeta(type):
|
|
639
797
|
_instances: ClassVar[dict[type[Any], Any]] = {}
|
|
640
798
|
_singleton_init: bool
|
|
641
799
|
|
|
642
|
-
def __new__
|
|
643
|
-
|
|
800
|
+
def __new__[MetaSelf: SingletonMeta](
|
|
801
|
+
mcls: type[MetaSelf], name: str, bases: tuple[type, ...], namespace: dict[str, Any], **kwargs: Any
|
|
802
|
+
) -> MetaSelf:
|
|
803
|
+
namespace["_singleton_init"] = kwargs.pop("init", False)
|
|
804
|
+
return super().__new__(mcls, name, bases, namespace, **kwargs)
|
|
644
805
|
|
|
645
806
|
def __call__(cls, *args: Any, **kwargs: Any) -> SingletonMeta:
|
|
646
807
|
if cls not in cls._instances:
|
|
@@ -654,34 +815,7 @@ class SingletonMeta(type):
|
|
|
654
815
|
class Singleton(metaclass=SingletonMeta):
|
|
655
816
|
"""Handy class to inherit to have the SingletonMeta metaclass."""
|
|
656
817
|
|
|
657
|
-
|
|
658
|
-
class to_singleton_impl:
|
|
659
|
-
_ts_args = tuple[str, ...]()
|
|
660
|
-
_ts_kwargs: Mapping[str, Any] = {}
|
|
661
|
-
_add_classes = tuple[type, ...]()
|
|
662
|
-
|
|
663
|
-
def __new__(_cls, cls: type[T]) -> T: # type: ignore
|
|
664
|
-
if _cls._add_classes:
|
|
665
|
-
|
|
666
|
-
class rcls(cls, *_cls._add_classes): # type: ignore
|
|
667
|
-
...
|
|
668
|
-
else:
|
|
669
|
-
rcls = cls # type: ignore
|
|
670
|
-
|
|
671
|
-
return rcls(*_cls._ts_args, **_cls._ts_kwargs)
|
|
672
|
-
|
|
673
|
-
@classmethod
|
|
674
|
-
def with_args(cls, *args: Any, **kwargs: Any) -> type[to_singleton]:
|
|
675
|
-
class _inner_singl(cls): # type: ignore
|
|
676
|
-
_ts_args = args
|
|
677
|
-
_ts_kwargs = kwargs
|
|
678
|
-
|
|
679
|
-
return _inner_singl
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
class to_singleton(to_singleton_impl):
|
|
683
|
-
class as_property(to_singleton_impl):
|
|
684
|
-
_add_classes = (property,)
|
|
818
|
+
__slots__ = ()
|
|
685
819
|
|
|
686
820
|
|
|
687
821
|
class LinearRangeLut(Mapping[int, int]):
|
|
@@ -706,13 +840,14 @@ class LinearRangeLut(Mapping[int, int]):
|
|
|
706
840
|
if self._misses_n > 2:
|
|
707
841
|
self._ranges_idx_lut = self._ranges_idx_lut[missed_hit:] + self._ranges_idx_lut[:missed_hit]
|
|
708
842
|
|
|
709
|
-
return idx
|
|
843
|
+
return idx # pyright: ignore[reportPossiblyUnboundVariable]
|
|
710
844
|
|
|
711
845
|
def __len__(self) -> int:
|
|
712
846
|
return len(self.ranges)
|
|
713
847
|
|
|
714
848
|
def __iter__(self) -> Iterator[int]:
|
|
715
|
-
|
|
849
|
+
for i in range(len(self)):
|
|
850
|
+
yield i
|
|
716
851
|
|
|
717
852
|
def __setitem__(self, n: int, _range: range) -> NoReturn:
|
|
718
853
|
raise NotImplementedError
|