jetpytools 1.2.3__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.

@@ -0,0 +1,669 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import partial, wraps
4
+ from inspect import Signature
5
+ from inspect import _empty as empty_param
6
+ from inspect import isclass
7
+ from typing import (
8
+ TYPE_CHECKING, Any, Callable, Concatenate, Generator, Generic, Iterable, Iterator, Mapping,
9
+ NoReturn, Protocol, Sequence, TypeVar, cast, overload
10
+ )
11
+
12
+ from .builtins import F0, F1, P0, P1, R0, R1, T0, T1, T2, KwargsT, P, R, T
13
+
14
+ __all__ = [
15
+ 'copy_signature',
16
+
17
+ 'inject_self',
18
+
19
+ 'inject_kwargs_params',
20
+
21
+ 'complex_hash',
22
+
23
+ 'get_subclasses',
24
+
25
+ 'classproperty', 'cachedproperty',
26
+
27
+ 'KwargsNotNone',
28
+
29
+ 'Singleton', 'to_singleton',
30
+
31
+ 'LinearRangeLut'
32
+ ]
33
+
34
+
35
+ class copy_signature(Generic[F0]):
36
+ """
37
+ Type util to copy the signature of one function to another function.\n
38
+ Especially useful for passthrough functions.
39
+
40
+ .. code-block::
41
+
42
+ class SomeClass:
43
+ def __init__(
44
+ self, some: Any, complex: Any, /, *args: Any,
45
+ long: Any, signature: Any, **kwargs: Any
46
+ ) -> None:
47
+ ...
48
+
49
+ class SomeClassChild(SomeClass):
50
+ @copy_signature(SomeClass.__init__)
51
+ def __init__(*args: Any, **kwargs: Any) -> None:
52
+ super().__init__(*args, **kwargs)
53
+ # do some other thing
54
+
55
+ class Example(SomeClass):
56
+ @copy_signature(SomeClass.__init__)
57
+ def __init__(*args: Any, **kwargs: Any) -> None:
58
+ super().__init__(*args, **kwargs)
59
+ # another thing
60
+ """
61
+
62
+ def __init__(self, target: F0) -> None:
63
+ """Copy the signature of ``target``."""
64
+
65
+ def __call__(self, wrapped: Callable[..., Any]) -> F0:
66
+ return cast(F0, wrapped)
67
+
68
+
69
+ class injected_self_func(Generic[T, P, R], Protocol): # type: ignore[misc]
70
+ @overload
71
+ @staticmethod
72
+ def __call__(*args: P.args, **kwargs: P.kwargs) -> R:
73
+ ...
74
+
75
+ @overload
76
+ @staticmethod
77
+ def __call__(self: T, *args: P.args, **kwargs: P.kwargs) -> R:
78
+ ...
79
+
80
+ @overload
81
+ @staticmethod
82
+ def __call__(self: T, _self: T, *args: P.args, **kwargs: P.kwargs) -> R:
83
+ ...
84
+
85
+ @overload
86
+ @staticmethod
87
+ def __call__(cls: type[T], *args: P.args, **kwargs: P.kwargs) -> R:
88
+ ...
89
+
90
+ @overload
91
+ @staticmethod
92
+ def __call__(cls: type[T], _cls: type[T], *args: P.args, **kwargs: P.kwargs) -> R:
93
+ ...
94
+
95
+ @staticmethod
96
+ def __call__(*args: Any, **kwds: Any) -> Any:
97
+ ...
98
+
99
+
100
+ self_objects_cache = dict[type[T], T]() # type: ignore
101
+
102
+
103
+ class inject_self_base(Generic[T, P, R]):
104
+ def __init__(self, function: Callable[Concatenate[T, P], R], /, *, cache: bool = False) -> None:
105
+ """
106
+ Wrap ``function`` to always have a self provided to it.
107
+
108
+ :param function: Method to wrap.
109
+ :param cache: Whether to cache the self object.
110
+ """
111
+
112
+ self.cache = self.init_kwargs = None
113
+
114
+ if isinstance(self, inject_self.cached):
115
+ self.cache = True
116
+
117
+ self.function = function
118
+
119
+ self.signature = self.first_key = self.init_kwargs = None
120
+
121
+ self.args = tuple[Any]()
122
+ self.kwargs = dict[str, Any]()
123
+
124
+ self.clean_kwargs = False
125
+
126
+ def __get__(
127
+ self, class_obj: type[T] | T | None, class_type: type[T] | type[type[T]] # type: ignore
128
+ ) -> injected_self_func[T, P, R]:
129
+ if not self.signature or not self.first_key: # type: ignore
130
+ self.signature = Signature.from_callable(self.function, eval_str=True) # type: ignore
131
+ self.first_key = next(iter(list(self.signature.parameters.keys())), None) # type: ignore
132
+
133
+ if isinstance(self, inject_self.init_kwargs):
134
+ from ..exceptions import CustomValueError
135
+
136
+ if 4 not in {x.kind for x in self.signature.parameters.values()}: # type: ignore
137
+ raise CustomValueError(
138
+ 'This function hasn\'t got any kwargs!', 'inject_self.init_kwargs', self.function
139
+ )
140
+
141
+ self.init_kwargs = list[str]( # type: ignore
142
+ k for k, x in self.signature.parameters.items() if x.kind != 4 # type: ignore
143
+ )
144
+
145
+ @wraps(self.function)
146
+ def _wrapper(*args: Any, **kwargs: Any) -> Any:
147
+ first_arg = (args[0] if args else None) or (
148
+ kwargs.get(self.first_key, None) if self.first_key else None # type: ignore
149
+ )
150
+
151
+ if (
152
+ first_arg and (
153
+ (is_obj := isinstance(first_arg, class_type))
154
+ or isinstance(first_arg, type(class_type)) # noqa
155
+ or first_arg is class_type # noqa
156
+ )
157
+ ):
158
+ obj = first_arg if is_obj else first_arg()
159
+ if args:
160
+ args = args[1:]
161
+ elif kwargs and self.first_key:
162
+ kwargs.pop(self.first_key) # type: ignore
163
+ elif class_obj is None:
164
+ if self.cache:
165
+ if class_type not in self_objects_cache:
166
+ obj = self_objects_cache[class_type] = class_type(*self.args, **self.kwargs)
167
+ else:
168
+ obj = self_objects_cache[class_type]
169
+ elif self.init_kwargs:
170
+ obj = class_type( # type: ignore
171
+ *self.args, **(self.kwargs | {k: v for k, v in kwargs.items() if k not in self.init_kwargs})
172
+ )
173
+ if self.clean_kwargs:
174
+ kwargs = {k: v for k, v in kwargs.items() if k in self.init_kwargs}
175
+ else:
176
+ obj = class_type(*self.args, **self.kwargs)
177
+ else:
178
+ obj = class_obj
179
+
180
+ return self.function(obj, *args, **kwargs)
181
+
182
+ return _wrapper
183
+
184
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
185
+ return self.__get__(None, self)(*args, **kwargs) # type: ignore
186
+
187
+ @property
188
+ def __signature__(self) -> Signature:
189
+ return Signature.from_callable(self.function)
190
+
191
+ @classmethod
192
+ def with_args(
193
+ cls, *args: Any, **kwargs: Any
194
+ ) -> Callable[[Callable[Concatenate[T0, P0], R0]], inject_self[T0, P0, R0]]:
195
+ """Provide custom args to instantiate the ``self`` object with."""
196
+
197
+ def _wrapper(function: Callable[Concatenate[T0, P0], R0]) -> inject_self[T0, P0, R0]:
198
+ inj = cls(function) # type: ignore
199
+ inj.args = args
200
+ inj.kwargs = kwargs
201
+ return inj # type: ignore
202
+ return _wrapper
203
+
204
+
205
+ class inject_self(Generic[T, P, R], inject_self_base[T, P, R]):
206
+ """Wrap a method so it always has a constructed ``self`` provided to it."""
207
+
208
+ class cached(Generic[T0, P0, R0], inject_self_base[T0, P0, R0]):
209
+ """
210
+ Wrap a method so it always has a constructed ``self`` provided to it.
211
+ Once ``self`` is constructed, it will be reused.
212
+ """
213
+
214
+ class property(Generic[T1, R1]):
215
+ def __init__(self, function: Callable[[T1], R1]) -> None:
216
+ self.function = inject_self(function)
217
+
218
+ def __get__(
219
+ self, class_obj: type[T1] | T1 | None, class_type: type[T1] | type[type[T1]] # type: ignore
220
+ ) -> R1:
221
+ return self.function.__get__(class_obj, class_type)()
222
+
223
+ class init_kwargs(Generic[T0, P0, R0], inject_self_base[T0, P0, R0]):
224
+ """
225
+ Wrap a method so it always has a constructed ``self`` provided to it.
226
+ When constructed, kwargs to the function will be passed to the constructor.
227
+ """
228
+
229
+ @classmethod
230
+ def clean(cls, function: Callable[Concatenate[T1, P1], R1]) -> inject_self[T1, P1, R1]:
231
+ """Wrap a method, pass kwargs to the constructor and remove them from actual **kwargs."""
232
+ inj = cls(function) # type: ignore
233
+ inj.clean_kwargs = True
234
+ return inj # type: ignore
235
+
236
+ class property(Generic[T0, R0]):
237
+ def __init__(self, function: Callable[[T0], R0]) -> None:
238
+ self.function = inject_self(function)
239
+
240
+ def __get__(
241
+ self, class_obj: type[T0] | T0 | None, class_type: type[T0] | type[type[T0]] # type: ignore
242
+ ) -> R0:
243
+ return self.function.__get__(class_obj, class_type)()
244
+
245
+
246
+ class inject_kwargs_params_base_func(Generic[T, P, R], Callable[Concatenate[T, P], R]): # type: ignore[misc]
247
+ def __call__(self: T, *args: P.args, **kwargs: P.kwargs) -> R: # type: ignore
248
+ ...
249
+
250
+
251
+ class inject_kwargs_params_base(Generic[T, P, R]):
252
+ _kwargs_name = 'kwargs'
253
+
254
+ def __init__(self, function: Callable[Concatenate[T, P], R]) -> None:
255
+ self.function = function
256
+
257
+ self.signature = None
258
+
259
+ def __get__(
260
+ self, class_obj: T, class_type: type[T]
261
+ ) -> inject_kwargs_params_base_func[T, P, R]:
262
+ if not self.signature:
263
+ self.signature = Signature.from_callable(self.function, eval_str=True) # type: ignore
264
+
265
+ if (
266
+ isinstance(self, inject_kwargs_params.add_to_kwargs) # type: ignore
267
+ and (4 not in {x.kind for x in self.signature.parameters.values()}) # type: ignore
268
+ ):
269
+ from ..exceptions import CustomValueError
270
+
271
+ raise CustomValueError(
272
+ 'This function hasn\'t got any kwargs!', 'inject_kwargs_params.add_to_kwargs', self.function
273
+ )
274
+
275
+ this = self
276
+
277
+ @wraps(self.function)
278
+ def _wrapper(self: T, *_args: P.args, **kwargs: P.kwargs) -> R:
279
+ assert this.signature
280
+
281
+ if class_obj and not isinstance(self, class_type): # type: ignore
282
+ _args = (self, *_args)
283
+ self = class_obj
284
+
285
+ if not hasattr(self, this._kwargs_name):
286
+ from ..exceptions import CustomRuntimeError
287
+
288
+ raise CustomRuntimeError(
289
+ f'This class doesn\'t have any "{this._kwargs_name}" attribute!', reason=self.__class__
290
+ )
291
+
292
+ this_kwargs = self.kwargs.copy()
293
+ args, n_args = list(_args), len(_args)
294
+
295
+ for i, (key, value) in enumerate(this.signature.parameters.items()):
296
+ if key not in this_kwargs:
297
+ continue
298
+
299
+ kw_value = this_kwargs.pop(key)
300
+
301
+ if value.default is empty_param:
302
+ continue
303
+
304
+ if i < n_args:
305
+ if args[i] != value.default:
306
+ continue
307
+
308
+ args[i] = kw_value
309
+ else:
310
+ if key in kwargs and kwargs[key] != value.default:
311
+ continue
312
+
313
+ kwargs[key] = kw_value
314
+
315
+ if isinstance(this, inject_kwargs_params.add_to_kwargs):
316
+ kwargs |= this_kwargs
317
+
318
+ return this.function(self, *args, **kwargs)
319
+
320
+ return _wrapper # type: ignore
321
+
322
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
323
+ return self.__get__(None, self)(*args, **kwargs) # type: ignore
324
+
325
+ @property
326
+ def __signature__(self) -> Signature:
327
+ return Signature.from_callable(self.function)
328
+
329
+ @classmethod
330
+ def with_name(cls, kwargs_name: str) -> type[inject_kwargs_params]: # type: ignore
331
+ class _inner(inject_kwargs_params): # type: ignore
332
+ _kwargs_name = kwargs_name
333
+
334
+ return _inner
335
+
336
+
337
+ if TYPE_CHECKING: # love you mypy...
338
+ class _add_to_kwargs:
339
+ def __call__(self, func: F1) -> F1:
340
+ ...
341
+
342
+ class _inject_kwargs_params:
343
+ def __call__(self, func: F0) -> F0:
344
+ ...
345
+
346
+ add_to_kwargs = _add_to_kwargs()
347
+
348
+ inject_kwargs_params = _inject_kwargs_params()
349
+ else:
350
+ class inject_kwargs_params(Generic[T, P, R], inject_kwargs_params_base[T, P, R]):
351
+ class add_to_kwargs(Generic[T0, P0, R0], inject_kwargs_params_base[T0, P0, R0]):
352
+ ...
353
+
354
+
355
+ class complex_hash(Generic[T]):
356
+ """
357
+ Decorator for classes to add a ``__hash__`` method to them.
358
+
359
+ Especially useful for NamedTuples.
360
+ """
361
+
362
+ def __new__(cls, class_type: T) -> T: # type: ignore
363
+ class inner_class_type(class_type): # type: ignore
364
+ def __hash__(self) -> int:
365
+ return complex_hash.hash(
366
+ self.__class__.__name__, *(
367
+ getattr(self, key) for key in self.__annotations__.keys()
368
+ )
369
+ )
370
+
371
+ return inner_class_type # type: ignore
372
+
373
+ @staticmethod
374
+ def hash(*args: Any) -> int:
375
+ """
376
+ Recursively hash every unhashable object in ``*args``.
377
+
378
+ :param *args: Objects to be hashed.
379
+
380
+ :return: Hash of all the combined objects' hashes.
381
+ """
382
+
383
+ values = list[str]()
384
+ for value in args:
385
+ try:
386
+ new_hash = hash(value)
387
+ except TypeError:
388
+ if isinstance(value, Iterable):
389
+ new_hash = complex_hash.hash(*value)
390
+ else:
391
+ new_hash = hash(str(value))
392
+
393
+ values.append(str(new_hash))
394
+
395
+ return hash('_'.join(values))
396
+
397
+
398
+ def get_subclasses(family: type[T], exclude: Sequence[type[T]] = []) -> list[type[T]]:
399
+ """
400
+ Get all subclasses of a given type.
401
+
402
+ :param family: "Main" type all other classes inherit from.
403
+ :param exclude: Excluded types from the yield. Note that they won't be excluded from search.
404
+ For examples, subclasses of these excluded classes will be yield.
405
+
406
+ :return: List of all subclasses of "family".
407
+ """
408
+
409
+ def _subclasses(cls: type[T]) -> Generator[type[T], None, None]:
410
+ for subclass in cls.__subclasses__():
411
+ yield from _subclasses(subclass)
412
+ if subclass in exclude:
413
+ continue
414
+ yield subclass
415
+
416
+ return list(set(_subclasses(family)))
417
+
418
+
419
+ class classproperty(Generic[P, R, T, T0, P0]):
420
+ """
421
+ Make a class property. A combination between classmethod and property.
422
+ """
423
+
424
+ __isabstractmethod__: bool = False
425
+
426
+ class metaclass(type):
427
+ """This must be set for the decorator to work."""
428
+
429
+ def __setattr__(self, key: str, value: Any) -> None:
430
+ if key in self.__dict__:
431
+ obj = self.__dict__.get(key)
432
+
433
+ if obj and isinstance(obj, classproperty):
434
+ obj.__set__(self, value)
435
+ return
436
+
437
+ super(classproperty.metaclass, self).__setattr__(key, value)
438
+
439
+ def __init__(
440
+ self,
441
+ fget: classmethod[T, P, R] | Callable[P, R],
442
+ fset: classmethod[T, P, None] | Callable[[T, T0], None] | None = None,
443
+ fdel: classmethod[T, P1, None] | Callable[P1, None] | None = None,
444
+ doc: str | None = None,
445
+ ) -> None:
446
+ self.fget = self._wrap(fget)
447
+ self.fset = self._wrap(fset) if fset is not None else fset
448
+ self.fdel = self._wrap(fdel) if fdel is not None else fdel
449
+
450
+ self.doc = doc
451
+
452
+ def _wrap(self, func: classmethod[T1, P1, R1] | Callable[P1, R1]) -> classmethod[T1, P1, R1]:
453
+ if not isinstance(func, (classmethod, staticmethod)):
454
+ func = classmethod(func) # type: ignore
455
+
456
+ return func # type: ignore
457
+
458
+ def getter(self, __fget: classmethod[T, P, R] | Callable[P1, R1]) -> classproperty[P1, R1, T, T0, P0]:
459
+ self.fget = self._wrap(__fget) # type: ignore
460
+ return self # type: ignore
461
+
462
+ def setter(self, __fset: classmethod[T1, P, None] | Callable[[T1, T2], None]) -> classproperty[P, R, T1, T2, P0]:
463
+ self.fset = self._wrap(__fset) # type: ignore
464
+ return self # type: ignore
465
+
466
+ def deleter(self, __fdel: classmethod[T1, P1, None] | Callable[P1, None]) -> classproperty[P, R, T, T0, P1]:
467
+ self.fdel = self._wrap(__fdel) # type: ignore
468
+ return self # type: ignore
469
+
470
+ def __get__(self, __obj: Any, __type: type | None = None) -> R:
471
+ if __type is None:
472
+ __type = type(__obj)
473
+
474
+ return self.fget.__get__(__obj, __type)() # type: ignore
475
+
476
+ def __set__(self, __obj: Any, __value: T1) -> None:
477
+ from ..exceptions import CustomError
478
+
479
+ if not self.fset:
480
+ raise CustomError[AttributeError]("Can't set attribute")
481
+
482
+ if isclass(__obj):
483
+ type_, __obj = __obj, None
484
+ else:
485
+ type_ = type(__obj)
486
+
487
+ return self.fset.__get__(__obj, type_)(__value)
488
+
489
+ def __delete__(self, __obj: Any) -> None:
490
+ from ..exceptions import CustomError
491
+
492
+ if not self.fdel:
493
+ raise CustomError[AttributeError]("Can't delete attribute")
494
+
495
+ if isclass(__obj):
496
+ type_, __obj = __obj, None
497
+ else:
498
+ type_ = type(__obj)
499
+
500
+ return self.fdel.__delete__(__obj, type_)(__obj) # type: ignore
501
+
502
+ def __name__(self) -> str:
503
+ return self.fget.__name__
504
+
505
+
506
+ class cachedproperty(property, Generic[P, R, T, T0, P0]):
507
+ """
508
+ Wrapper for a one-time get property, that will be cached.
509
+
510
+ Keep in mind two things:
511
+
512
+ * The cache is per-object. Don't hold a reference to itself or it will never get garbage collected.
513
+ * Your class has to either manually set __dict__[cachedproperty.cache_key]
514
+ or inherit from cachedproperty.baseclass.
515
+ """
516
+
517
+ __isabstractmethod__: bool = False
518
+
519
+ cache_key = '_jetpt_cachedproperty_cache'
520
+
521
+ class baseclass:
522
+ """Inherit from this class to automatically set the cache dict."""
523
+
524
+ if not TYPE_CHECKING:
525
+ def __new__(cls, *args: Any, **kwargs: Any) -> None:
526
+ try:
527
+ self = super().__new__(cls, *args, **kwargs)
528
+ except TypeError:
529
+ self = super().__new__(cls)
530
+ self.__dict__.__setitem__(cachedproperty.cache_key, dict[str, Any]())
531
+ return self
532
+
533
+ if TYPE_CHECKING:
534
+ def __init__(
535
+ self, fget: Callable[P, R], fset: Callable[[T, T0], None] | None = None,
536
+ fdel: Callable[P0, None] | None = None, doc: str | None = None,
537
+ ) -> None:
538
+ ...
539
+
540
+ def getter(self, __fget: Callable[P1, R1]) -> cachedproperty[P1, R1, T, T0, P0]:
541
+ ...
542
+
543
+ def setter(self, __fset: Callable[[T1, T2], None]) -> cachedproperty[P, R, T1, T2, P0]:
544
+ ...
545
+
546
+ def deleter(self, __fdel: Callable[P1, None]) -> cachedproperty[P, R, T, T0, P1]:
547
+ ...
548
+
549
+ def __get__(self, __obj: Any, __type: type | None = None) -> R:
550
+ if isinstance(self.fget, classproperty):
551
+ function = partial(self.fget.__get__, __obj, __type) # type: ignore
552
+ __obj = __type
553
+
554
+ if not hasattr(__obj, cachedproperty.cache_key):
555
+ setattr(__obj, cachedproperty.cache_key, dict[str, Any]())
556
+
557
+ cache = getattr(__obj, cachedproperty.cache_key)
558
+ name = self.fget.__name__
559
+ else:
560
+ function = self.fget.__get__(__obj, __type) # type: ignore
561
+ cache = __obj.__dict__.get(cachedproperty.cache_key)
562
+ name = function.__name__
563
+
564
+ if name not in cache:
565
+ cache[name] = function()
566
+
567
+ return cache[name] # type: ignore
568
+
569
+
570
+ class KwargsNotNone(KwargsT):
571
+ """Remove all None objects from this kwargs dict."""
572
+
573
+ if not TYPE_CHECKING:
574
+ def __new__(cls, *args: Any, **kwargs: Any) -> KwargsNotNone:
575
+ return KwargsT(**{
576
+ key: value for key, value in KwargsT(*args, **kwargs).items()
577
+ if value is not None
578
+ })
579
+
580
+
581
+ SingleMeta = TypeVar('SingleMeta', bound=type)
582
+
583
+
584
+ class SingletonMeta(type):
585
+ _instances = dict[type[SingleMeta], SingleMeta]() # type: ignore
586
+ _singleton_init: bool
587
+
588
+ def __new__(
589
+ cls: type[SingletonSelf], name: str, bases: tuple[type, ...], namespace: dict[str, Any], **kwargs: Any
590
+ ) -> SingletonSelf:
591
+ return type.__new__(cls, name, bases, namespace | {'_singleton_init': kwargs.pop('init', False)})
592
+
593
+ def __call__(cls: type[SingletonSelf], *args: Any, **kwargs: Any) -> SingletonSelf: # type: ignore
594
+ if cls not in cls._instances:
595
+ cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
596
+ elif cls._singleton_init:
597
+ cls._instances[cls].__init__(*args, **kwargs) # type: ignore
598
+
599
+ return cls._instances[cls]
600
+
601
+
602
+ SingletonSelf = TypeVar('SingletonSelf', bound=SingletonMeta)
603
+
604
+
605
+ class Singleton(metaclass=SingletonMeta):
606
+ """Handy class to inherit to have the SingletonMeta metaclass."""
607
+
608
+
609
+ class to_singleton_impl:
610
+ _ts_args = tuple[str, ...]()
611
+ _ts_kwargs = dict[str, Any]()
612
+ _add_classes = tuple[type, ...]()
613
+
614
+ def __new__(_cls, cls: type[T]) -> T: # type: ignore
615
+ if _cls._add_classes:
616
+ class rcls(cls, *_cls._add_classes): # type: ignore
617
+ ...
618
+ else:
619
+ rcls = cls # type: ignore
620
+
621
+ return rcls(*_cls._ts_args, **_cls._ts_kwargs)
622
+
623
+ @classmethod
624
+ def with_args(cls, *args: Any, **kwargs: Any) -> type[to_singleton]:
625
+ class _inner_singl(cls): # type: ignore
626
+ _ts_args = args
627
+ _ts_kwargs = kwargs
628
+
629
+ return _inner_singl
630
+
631
+
632
+ class to_singleton(to_singleton_impl):
633
+ class as_property(to_singleton_impl):
634
+ _add_classes = (property, )
635
+
636
+
637
+ class LinearRangeLut(Mapping[int, int]):
638
+ __slots__ = ('ranges', '_ranges_idx_lut', '_misses_n')
639
+
640
+ def __init__(self, ranges: Mapping[int, range]) -> None:
641
+ self.ranges = ranges
642
+
643
+ self._ranges_idx_lut = list(self.ranges.items())
644
+ self._misses_n = 0
645
+
646
+ def __getitem__(self, n: int) -> int:
647
+ for missed_hit, (idx, k) in enumerate(self._ranges_idx_lut):
648
+ if n in k:
649
+ break
650
+
651
+ if missed_hit:
652
+ self._misses_n += 1
653
+
654
+ if self._misses_n > 2:
655
+ self._ranges_idx_lut = self._ranges_idx_lut[missed_hit:] + self._ranges_idx_lut[:missed_hit]
656
+
657
+ return idx
658
+
659
+ def __len__(self) -> int:
660
+ return len(self.ranges)
661
+
662
+ def __iter__(self) -> Iterator[int]:
663
+ return iter(range(len(self)))
664
+
665
+ def __setitem__(self, n: int, _range: range) -> NoReturn:
666
+ raise NotImplementedError
667
+
668
+ def __delitem__(self, n: int) -> NoReturn:
669
+ raise NotImplementedError
@@ -0,0 +1,4 @@
1
+ from .file import * # noqa: F401, F403
2
+ from .funcs import * # noqa: F401, F403
3
+ from .math import * # noqa: F401, F403
4
+ from .ranges import * # noqa: F401, F403