jetpytools 1.2.4__tar.gz → 1.2.6__tar.gz
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-1.2.4 → jetpytools-1.2.6}/PKG-INFO +1 -1
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/_metadata.py +1 -1
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/enums/base.py +5 -7
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/enums/other.py +5 -7
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/exceptions/base.py +10 -5
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/exceptions/generic.py +2 -2
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/functions/normalize.py +24 -26
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/__init__.py +1 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/builtins.py +2 -11
- jetpytools-1.2.6/jetpytools/types/check.py +34 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/file.py +10 -4
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/funcs.py +15 -13
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/utils.py +17 -12
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools.egg-info/PKG-INFO +1 -1
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools.egg-info/SOURCES.txt +1 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/LICENSE +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/README.md +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/__init__.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/enums/__init__.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/exceptions/__init__.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/exceptions/enum.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/exceptions/file.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/exceptions/module.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/functions/__init__.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/functions/funcs.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/functions/other.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/py.typed +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/generic.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/types/supports.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/utils/__init__.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/utils/file.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/utils/funcs.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/utils/math.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools/utils/ranges.py +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools.egg-info/dependency_links.txt +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/jetpytools.egg-info/top_level.txt +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/setup.cfg +0 -0
- {jetpytools-1.2.4 → jetpytools-1.2.6}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Collection of stuff that's useful in general python programming"""
|
|
2
2
|
|
|
3
|
-
__version__ = '1.2.
|
|
3
|
+
__version__ = '1.2.6'
|
|
4
4
|
|
|
5
5
|
__author_name__, __author_email__ = 'Jaded Encoding Thaumaturgy', 'jaded.encoding.thaumaturgy@gmail.com'
|
|
6
6
|
__maintainer_name__, __maintainer_email__ = __author_name__, __author_email__
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from typing_extensions import Self
|
|
5
7
|
|
|
6
8
|
from ..exceptions import CustomValueError, NotFoundEnumValue
|
|
7
9
|
from ..types import FuncExceptT
|
|
8
10
|
|
|
9
11
|
__all__ = [
|
|
10
|
-
'SelfEnum',
|
|
11
12
|
'CustomEnum', 'CustomIntEnum', 'CustomStrEnum'
|
|
12
13
|
]
|
|
13
14
|
|
|
@@ -16,11 +17,11 @@ class CustomEnum(Enum):
|
|
|
16
17
|
"""Base class for custom enums."""
|
|
17
18
|
|
|
18
19
|
@classmethod
|
|
19
|
-
def _missing_(cls
|
|
20
|
+
def _missing_(cls, value: Any) -> Self | None:
|
|
20
21
|
return cls.from_param(value)
|
|
21
22
|
|
|
22
23
|
@classmethod
|
|
23
|
-
def from_param(cls
|
|
24
|
+
def from_param(cls, value: Any, func_except: FuncExceptT | None = None) -> Self | None:
|
|
24
25
|
"""
|
|
25
26
|
Return the enum value from a parameter.
|
|
26
27
|
|
|
@@ -73,6 +74,3 @@ class CustomStrEnum(str, CustomEnum):
|
|
|
73
74
|
"""Base class for custom str enums."""
|
|
74
75
|
|
|
75
76
|
value: str
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
SelfEnum = TypeVar('SelfEnum', bound=CustomEnum)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import overload
|
|
4
|
+
from typing_extensions import Self
|
|
4
5
|
|
|
5
6
|
__all__ = [
|
|
6
7
|
'Coordinate',
|
|
@@ -23,14 +24,14 @@ class Coordinate:
|
|
|
23
24
|
"""Vertical coordinate."""
|
|
24
25
|
|
|
25
26
|
@overload
|
|
26
|
-
def __init__(self
|
|
27
|
+
def __init__(self, other: tuple[int, int] | Self, /) -> None:
|
|
27
28
|
...
|
|
28
29
|
|
|
29
30
|
@overload
|
|
30
|
-
def __init__(self
|
|
31
|
+
def __init__(self, x: int, y: int, /) -> None:
|
|
31
32
|
...
|
|
32
33
|
|
|
33
|
-
def __init__(self
|
|
34
|
+
def __init__(self, x_or_self: int | tuple[int, int] | Self, y: int | None = None, /) -> None:
|
|
34
35
|
from ..exceptions import CustomValueError
|
|
35
36
|
|
|
36
37
|
if isinstance(x_or_self, int):
|
|
@@ -48,9 +49,6 @@ class Coordinate:
|
|
|
48
49
|
self.y = y
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
SelfCoord = TypeVar('SelfCoord', bound=Coordinate)
|
|
52
|
-
|
|
53
|
-
|
|
54
52
|
class Position(Coordinate):
|
|
55
53
|
"""Positive set of an (x,y) offset relative to the top left corner of an area."""
|
|
56
54
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
|
|
4
5
|
from copy import deepcopy
|
|
5
6
|
from typing import TYPE_CHECKING, Any, TypeVar
|
|
6
7
|
|
|
7
|
-
from
|
|
8
|
+
from typing_extensions import Self
|
|
9
|
+
|
|
10
|
+
from ..types import MISSING, FuncExceptT, SupportsString, MissingT
|
|
8
11
|
|
|
9
12
|
__all__ = [
|
|
10
13
|
'CustomError',
|
|
@@ -112,10 +115,12 @@ class CustomError(ExceptionT, metaclass=CustomErrorMeta):
|
|
|
112
115
|
return CustomErrorMeta.setup_exception(inner_exception, exception) # type: ignore
|
|
113
116
|
|
|
114
117
|
def __call__(
|
|
115
|
-
self
|
|
116
|
-
|
|
118
|
+
self,
|
|
119
|
+
message: SupportsString | None | MissingT = MISSING,
|
|
120
|
+
func: FuncExceptT | None | MissingT = MISSING,
|
|
121
|
+
reason: SupportsString | FuncExceptT | None | MissingT = MISSING,
|
|
117
122
|
**kwargs: Any
|
|
118
|
-
) ->
|
|
123
|
+
) -> Self:
|
|
119
124
|
"""
|
|
120
125
|
Copy an existing exception with defaults and instantiate a new one.
|
|
121
126
|
|
|
@@ -129,7 +134,7 @@ class CustomError(ExceptionT, metaclass=CustomErrorMeta):
|
|
|
129
134
|
if message is not MISSING:
|
|
130
135
|
err.message = message
|
|
131
136
|
|
|
132
|
-
if func is not MISSING:
|
|
137
|
+
if func is not MISSING:
|
|
133
138
|
err.func = func
|
|
134
139
|
|
|
135
140
|
if reason is not MISSING:
|
|
@@ -18,8 +18,8 @@ class MismatchError(CustomValueError):
|
|
|
18
18
|
return str(item)
|
|
19
19
|
|
|
20
20
|
@classmethod
|
|
21
|
-
def _reduce(cls, items: Iterable[Any]) -> tuple[str]:
|
|
22
|
-
return tuple
|
|
21
|
+
def _reduce(cls, items: Iterable[Any]) -> tuple[str, ...]:
|
|
22
|
+
return tuple(dict.fromkeys(map(cls._item_to_name, items)).keys())
|
|
23
23
|
|
|
24
24
|
def __init__(
|
|
25
25
|
self, func: FuncExceptT, items: Iterable[Any], message: SupportsString = 'All items must be equal!',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from fractions import Fraction
|
|
4
|
-
from typing import Any, Iterable, Iterator, Sequence, overload
|
|
4
|
+
from typing import Any, Callable, Iterable, Iterator, Sequence, overload
|
|
5
5
|
|
|
6
|
-
from ..types import
|
|
6
|
+
from ..types import SoftRange, SoftRangeN, SoftRangesN, StrictRange, SupportsString, T, is_soft_range_n
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
9
|
'normalize_seq',
|
|
@@ -19,12 +19,12 @@ __all__ = [
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
@overload
|
|
22
|
-
def normalize_seq(val: Sequence[T], length: int) -> list[T]:
|
|
22
|
+
def normalize_seq(val: T | Sequence[T], length: int) -> list[T]:
|
|
23
23
|
...
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@overload
|
|
27
|
-
def normalize_seq(val:
|
|
27
|
+
def normalize_seq(val: Any, length: int) -> list[Any]:
|
|
28
28
|
...
|
|
29
29
|
|
|
30
30
|
|
|
@@ -47,45 +47,43 @@ def normalize_seq(val: T | Sequence[T], length: int) -> list[T]:
|
|
|
47
47
|
return val[:length]
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
_iterables_t = (list, tuple, range, zip, set, map, enumerate)
|
|
51
|
-
|
|
52
|
-
|
|
53
50
|
@overload
|
|
54
|
-
def to_arr(val:
|
|
51
|
+
def to_arr(val: T | Iterable[T]) -> list[T]:
|
|
55
52
|
...
|
|
56
53
|
|
|
57
54
|
|
|
58
55
|
@overload
|
|
59
|
-
def to_arr(val:
|
|
56
|
+
def to_arr(val: Any) -> list[Any]:
|
|
60
57
|
...
|
|
61
58
|
|
|
62
59
|
|
|
63
|
-
def to_arr(val:
|
|
64
|
-
"""
|
|
65
|
-
|
|
60
|
+
def to_arr(val: Any, *, sub: Any = []) -> list[Any]:
|
|
61
|
+
"""
|
|
62
|
+
Normalize any value to a list.
|
|
63
|
+
Bytes and str are not considered iterable and will not be flattened.
|
|
64
|
+
"""
|
|
66
65
|
if sub:
|
|
67
|
-
|
|
66
|
+
import warnings
|
|
67
|
+
warnings.warn("sub is deprecated.", DeprecationWarning)
|
|
68
68
|
|
|
69
|
-
return list(val) if
|
|
69
|
+
return list(val) if (isinstance(val, Iterable) and not isinstance(val, (str, bytes))) else [val]
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
@overload
|
|
73
|
-
def flatten(items:
|
|
73
|
+
def flatten(items: Iterable[Iterable[T]]) -> Iterator[T]:
|
|
74
74
|
...
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
@overload
|
|
78
|
-
def flatten(items:
|
|
78
|
+
def flatten(items: Iterable[Any]) -> Iterator[Any]:
|
|
79
79
|
...
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def flatten(items: Any) -> Any:
|
|
88
|
-
"""Flatten an array of values."""
|
|
82
|
+
def flatten(items: Any) -> Iterator[Any]:
|
|
83
|
+
"""
|
|
84
|
+
Flatten an array of values.
|
|
85
|
+
Bytes and str are not considered iterable and will not be flattened..
|
|
86
|
+
"""
|
|
89
87
|
|
|
90
88
|
for val in items:
|
|
91
89
|
if isinstance(val, Iterable) and not isinstance(val, (str, bytes)):
|
|
@@ -170,13 +168,13 @@ def normalize_ranges(ranges: SoftRangeN | SoftRangesN, end: int) -> list[StrictR
|
|
|
170
168
|
[(24, 150)]
|
|
171
169
|
|
|
172
170
|
|
|
173
|
-
:param clip: Input clip.
|
|
174
171
|
:param franges: Frame range or list of frame ranges.
|
|
172
|
+
:param end: End number.
|
|
175
173
|
|
|
176
174
|
:return: List of positive frame ranges.
|
|
177
175
|
"""
|
|
178
176
|
|
|
179
|
-
ranges = ranges if
|
|
177
|
+
ranges = [ranges] if is_soft_range_n(ranges) else ranges
|
|
180
178
|
|
|
181
179
|
out = []
|
|
182
180
|
|
|
@@ -215,7 +213,7 @@ def invert_ranges(ranges: SoftRangeN | SoftRangesN, enda: int, endb: int | None)
|
|
|
215
213
|
return normalize_list_to_ranges({*range(enda)} - b_frames)
|
|
216
214
|
|
|
217
215
|
|
|
218
|
-
def norm_func_name(func_name: SupportsString |
|
|
216
|
+
def norm_func_name(func_name: SupportsString | Callable[..., Any]) -> str:
|
|
219
217
|
"""Normalize a class, function, or other object to obtain its name"""
|
|
220
218
|
|
|
221
219
|
if isinstance(func_name, str):
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
4
|
-
TYPE_CHECKING, Any, Callable, ParamSpec, Sequence, SupportsFloat, SupportsIndex, TypeAlias, TypeVar, Union
|
|
5
|
-
)
|
|
3
|
+
from typing import Any, Callable, ParamSpec, Sequence, SupportsFloat, SupportsIndex, TypeAlias, TypeVar, Union
|
|
6
4
|
|
|
7
5
|
__all__ = [
|
|
8
6
|
'T', 'T0', 'T1', 'T2', 'T_contra',
|
|
@@ -16,8 +14,6 @@ __all__ = [
|
|
|
16
14
|
|
|
17
15
|
'StrictRange', 'SoftRange', 'SoftRangeN', 'SoftRangesN',
|
|
18
16
|
|
|
19
|
-
'Self',
|
|
20
|
-
|
|
21
17
|
'SingleOrArr', 'SingleOrArrOpt',
|
|
22
18
|
'SingleOrSeq', 'SingleOrSeqOpt',
|
|
23
19
|
|
|
@@ -52,17 +48,12 @@ R2 = TypeVar('R2')
|
|
|
52
48
|
T_contra = TypeVar('T_contra', contravariant=True)
|
|
53
49
|
R_contra = TypeVar('R_contra', contravariant=True)
|
|
54
50
|
|
|
55
|
-
Self = TypeVar('Self')
|
|
56
|
-
|
|
57
51
|
StrictRange: TypeAlias = tuple[int, int]
|
|
58
52
|
SoftRange: TypeAlias = int | StrictRange | Sequence[int]
|
|
59
53
|
|
|
60
54
|
SoftRangeN: TypeAlias = int | tuple[int | None, int | None] | None
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
SoftRangesN: TypeAlias = Sequence[SoftRangeN]
|
|
64
|
-
else:
|
|
65
|
-
SoftRangesN: TypeAlias = list[SoftRangeN]
|
|
56
|
+
SoftRangesN: TypeAlias = Sequence[SoftRangeN]
|
|
66
57
|
|
|
67
58
|
SingleOrArr = Union[T, list[T]]
|
|
68
59
|
SingleOrSeq = Union[T, Sequence[T]]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import Any, Sequence
|
|
2
|
+
|
|
3
|
+
from typing_extensions import TypeIs
|
|
4
|
+
|
|
5
|
+
from .builtins import SoftRange, SoftRangeN, SoftRangesN, StrictRange
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"is_strict_range", "is_soft_range", "is_soft_range_n", "is_soft_ranges_n"
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_strict_range(val: Any) -> TypeIs[StrictRange]:
|
|
13
|
+
return isinstance(val, tuple) and len(val) == 2 and all(isinstance(x, int) for x in val)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_soft_range(val: Any) -> TypeIs[SoftRange]:
|
|
17
|
+
return isinstance(val, int) or is_strict_range(val) or (
|
|
18
|
+
isinstance(val, Sequence) and all(isinstance(x, int) for x in val)
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_soft_range_n(val: Any) -> TypeIs[SoftRangeN]:
|
|
23
|
+
return (
|
|
24
|
+
isinstance(val, int)
|
|
25
|
+
or (isinstance(val, tuple) and len(val) == 2 and all(isinstance(x, int) or x is None for x in val))
|
|
26
|
+
or val is None
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_soft_ranges_n(val: Any) -> TypeIs[SoftRangesN]:
|
|
31
|
+
return (
|
|
32
|
+
isinstance(val, Sequence)
|
|
33
|
+
and all(is_soft_range_n(x) for x in val)
|
|
34
|
+
)
|
|
@@ -2,8 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import fnmatch
|
|
4
4
|
import shutil
|
|
5
|
+
|
|
5
6
|
from os import PathLike, listdir, path, walk
|
|
6
7
|
from pathlib import Path
|
|
8
|
+
from sys import version_info
|
|
7
9
|
from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, TypeAlias, Union
|
|
8
10
|
|
|
9
11
|
__all__ = [
|
|
@@ -58,6 +60,8 @@ OpenBinaryMode: TypeAlias = OpenBinaryModeUpdating | OpenBinaryModeReading | Ope
|
|
|
58
60
|
|
|
59
61
|
class SPath(Path):
|
|
60
62
|
"""Modified version of pathlib.Path"""
|
|
63
|
+
if version_info < (3, 12):
|
|
64
|
+
_flavour = type(Path())._flavour # type: ignore
|
|
61
65
|
|
|
62
66
|
if TYPE_CHECKING:
|
|
63
67
|
def __new__(cls, *args: SPathLike, **kwargs: Any) -> SPath:
|
|
@@ -117,7 +121,7 @@ class SPath(Path):
|
|
|
117
121
|
|
|
118
122
|
from ..functions import to_arr
|
|
119
123
|
|
|
120
|
-
return self.with_stem(sep.join([self.stem, *to_arr(suffixes)]))
|
|
124
|
+
return self.with_stem(sep.join([self.stem, *to_arr(suffixes)]))
|
|
121
125
|
|
|
122
126
|
def is_empty_dir(self) -> bool:
|
|
123
127
|
"""Check if the directory is empty."""
|
|
@@ -172,10 +176,12 @@ class SPath(Path):
|
|
|
172
176
|
|
|
173
177
|
matching_files = self.get_folder().glob(pattern)
|
|
174
178
|
|
|
175
|
-
|
|
179
|
+
try:
|
|
180
|
+
next(matching_files)
|
|
181
|
+
except StopIteration:
|
|
176
182
|
return None
|
|
177
183
|
|
|
178
|
-
return max(matching_files, key=lambda p: p.stat().st_mtime
|
|
184
|
+
return max(matching_files, key=lambda p: p.stat().st_mtime)
|
|
179
185
|
|
|
180
186
|
def get_size(self) -> int:
|
|
181
187
|
"""Get the size of the file or directory in bytes."""
|
|
@@ -190,4 +196,4 @@ class SPath(Path):
|
|
|
190
196
|
return sum(f.stat().st_size for f in self.rglob('*') if f.is_file())
|
|
191
197
|
|
|
192
198
|
|
|
193
|
-
SPathLike = Union[str, Path, SPath]
|
|
199
|
+
SPathLike = Union[str, PathLike[str], Path, SPath]
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from functools import wraps
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, SupportsIndex, TypeAlias, overload
|
|
3
5
|
|
|
4
|
-
from
|
|
6
|
+
from typing_extensions import Self, TypeIs
|
|
5
7
|
|
|
6
|
-
from .builtins import
|
|
8
|
+
from .builtins import P, T
|
|
7
9
|
from .supports import SupportsString
|
|
8
10
|
|
|
9
11
|
__all__ = [
|
|
@@ -13,7 +15,7 @@ __all__ = [
|
|
|
13
15
|
]
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
class StrList(
|
|
18
|
+
class StrList(list[SupportsString]):
|
|
17
19
|
"""Custom class for representing a recursively "stringable" list."""
|
|
18
20
|
|
|
19
21
|
if TYPE_CHECKING:
|
|
@@ -41,7 +43,7 @@ class StrList(List[SupportsString]):
|
|
|
41
43
|
return ' '.join(
|
|
42
44
|
filter(
|
|
43
45
|
None,
|
|
44
|
-
(str(x).strip() for x in flatten(self) if x is not None)
|
|
46
|
+
(str(x).strip() for x in flatten(self) if x is not None)
|
|
45
47
|
)
|
|
46
48
|
)
|
|
47
49
|
|
|
@@ -74,20 +76,22 @@ class SentinelDispatcher:
|
|
|
74
76
|
|
|
75
77
|
return _wrap
|
|
76
78
|
|
|
77
|
-
def filter(self
|
|
79
|
+
def filter(self, items: Iterable[T | Self]) -> Iterator[T]:
|
|
78
80
|
for item in items:
|
|
79
|
-
if item
|
|
81
|
+
if isinstance(item, SentinelDispatcher):
|
|
80
82
|
continue
|
|
81
|
-
|
|
82
|
-
yield item # type: ignore
|
|
83
|
+
yield item
|
|
83
84
|
|
|
84
85
|
@classmethod
|
|
85
|
-
def filter_multi(cls, items: Iterable[T |
|
|
86
|
+
def filter_multi(cls, items: Iterable[T | Self], *sentinels: Self) -> Iterator[T]:
|
|
87
|
+
def _in_sentinels(it: Any) -> TypeIs[SentinelDispatcher]:
|
|
88
|
+
return it in sentinels
|
|
89
|
+
|
|
86
90
|
for item in items:
|
|
87
|
-
if item
|
|
91
|
+
if _in_sentinels(item):
|
|
88
92
|
continue
|
|
89
93
|
|
|
90
|
-
yield item
|
|
94
|
+
yield item
|
|
91
95
|
|
|
92
96
|
def __getattr__(self, name: str) -> SentinelDispatcher:
|
|
93
97
|
if name not in _sentinels:
|
|
@@ -105,5 +109,3 @@ Sentinel = SentinelDispatcher()
|
|
|
105
109
|
SentinelT: TypeAlias = SentinelDispatcher
|
|
106
110
|
|
|
107
111
|
_sentinels = dict[str, SentinelDispatcher]()
|
|
108
|
-
|
|
109
|
-
SelfSentinel = TypeVar('SelfSentinel', bound=SentinelDispatcher)
|
|
@@ -97,10 +97,15 @@ class injected_self_func(Generic[T, P, R], Protocol): # type: ignore[misc]
|
|
|
97
97
|
...
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
self_objects_cache = dict[type
|
|
100
|
+
self_objects_cache = dict[type, Any]()
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
class inject_self_base(Generic[T, P, R]):
|
|
104
|
+
cache: bool | None
|
|
105
|
+
signature: Signature | None
|
|
106
|
+
init_kwargs: list[str] | None
|
|
107
|
+
first_key: str | None
|
|
108
|
+
|
|
104
109
|
def __init__(self, function: Callable[Concatenate[T, P], R], /, *, cache: bool = False) -> None:
|
|
105
110
|
"""
|
|
106
111
|
Wrap ``function`` to always have a self provided to it.
|
|
@@ -126,20 +131,20 @@ class inject_self_base(Generic[T, P, R]):
|
|
|
126
131
|
def __get__(
|
|
127
132
|
self, class_obj: type[T] | T | None, class_type: type[T] | type[type[T]] # type: ignore
|
|
128
133
|
) -> injected_self_func[T, P, R]:
|
|
129
|
-
if not self.signature or not self.first_key:
|
|
130
|
-
self.signature = Signature.from_callable(self.function, eval_str=True)
|
|
131
|
-
self.first_key = next(iter(list(self.signature.parameters.keys())), None)
|
|
134
|
+
if not self.signature or not self.first_key:
|
|
135
|
+
self.signature = Signature.from_callable(self.function, eval_str=True)
|
|
136
|
+
self.first_key = next(iter(list(self.signature.parameters.keys())), None)
|
|
132
137
|
|
|
133
138
|
if isinstance(self, inject_self.init_kwargs):
|
|
134
139
|
from ..exceptions import CustomValueError
|
|
135
140
|
|
|
136
|
-
if 4 not in {x.kind for x in self.signature.parameters.values()}:
|
|
141
|
+
if 4 not in {x.kind for x in self.signature.parameters.values()}:
|
|
137
142
|
raise CustomValueError(
|
|
138
143
|
'This function hasn\'t got any kwargs!', 'inject_self.init_kwargs', self.function
|
|
139
144
|
)
|
|
140
145
|
|
|
141
|
-
self.init_kwargs = list[str](
|
|
142
|
-
k for k, x in self.signature.parameters.items() if x.kind != 4
|
|
146
|
+
self.init_kwargs = list[str](
|
|
147
|
+
k for k, x in self.signature.parameters.items() if x.kind != 4
|
|
143
148
|
)
|
|
144
149
|
|
|
145
150
|
@wraps(self.function)
|
|
@@ -151,15 +156,15 @@ class inject_self_base(Generic[T, P, R]):
|
|
|
151
156
|
if (
|
|
152
157
|
first_arg and (
|
|
153
158
|
(is_obj := isinstance(first_arg, class_type))
|
|
154
|
-
or isinstance(first_arg, type(class_type))
|
|
155
|
-
or first_arg is class_type
|
|
159
|
+
or isinstance(first_arg, type(class_type))
|
|
160
|
+
or first_arg is class_type
|
|
156
161
|
)
|
|
157
162
|
):
|
|
158
163
|
obj = first_arg if is_obj else first_arg()
|
|
159
164
|
if args:
|
|
160
165
|
args = args[1:]
|
|
161
166
|
elif kwargs and self.first_key:
|
|
162
|
-
kwargs.pop(self.first_key)
|
|
167
|
+
kwargs.pop(self.first_key)
|
|
163
168
|
elif class_obj is None:
|
|
164
169
|
if self.cache:
|
|
165
170
|
if class_type not in self_objects_cache:
|
|
@@ -167,7 +172,7 @@ class inject_self_base(Generic[T, P, R]):
|
|
|
167
172
|
else:
|
|
168
173
|
obj = self_objects_cache[class_type]
|
|
169
174
|
elif self.init_kwargs:
|
|
170
|
-
obj = class_type(
|
|
175
|
+
obj = class_type(
|
|
171
176
|
*self.args, **(self.kwargs | {k: v for k, v in kwargs.items() if k not in self.init_kwargs})
|
|
172
177
|
)
|
|
173
178
|
if self.clean_kwargs:
|
|
@@ -473,7 +478,7 @@ class classproperty(Generic[P, R, T, T0, P0]):
|
|
|
473
478
|
|
|
474
479
|
return self.fget.__get__(__obj, __type)() # type: ignore
|
|
475
480
|
|
|
476
|
-
def __set__(self, __obj: Any, __value:
|
|
481
|
+
def __set__(self, __obj: Any, __value: Any) -> None:
|
|
477
482
|
from ..exceptions import CustomError
|
|
478
483
|
|
|
479
484
|
if not self.fset:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|