jetpytools 1.3.0__tar.gz → 1.5.0__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.3.0 → jetpytools-1.5.0}/PKG-INFO +1 -1
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/_metadata.py +1 -1
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/exceptions/base.py +3 -13
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/functions/funcs.py +46 -2
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/functions/normalize.py +85 -28
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/file.py +8 -2
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/utils.py +22 -57
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/utils/math.py +3 -1
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools.egg-info/PKG-INFO +1 -1
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools.egg-info/SOURCES.txt +3 -1
- {jetpytools-1.3.0 → jetpytools-1.5.0}/setup.cfg +1 -0
- jetpytools-1.5.0/tests/test_funcs.py +12 -0
- jetpytools-1.5.0/tests/test_normalize.py +63 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/LICENSE +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/README.md +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/__init__.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/enums/__init__.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/enums/base.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/enums/other.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/exceptions/__init__.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/exceptions/enum.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/exceptions/file.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/exceptions/generic.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/exceptions/module.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/functions/__init__.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/functions/other.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/py.typed +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/__init__.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/builtins.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/check.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/funcs.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/generic.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/types/supports.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/utils/__init__.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/utils/file.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/utils/funcs.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools/utils/ranges.py +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools.egg-info/dependency_links.txt +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools.egg-info/requires.txt +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/jetpytools.egg-info/top_level.txt +0 -0
- {jetpytools-1.3.0 → jetpytools-1.5.0}/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.
|
|
3
|
+
__version__ = '1.5.0'
|
|
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__
|
|
@@ -72,10 +72,6 @@ class CustomErrorMeta(type):
|
|
|
72
72
|
|
|
73
73
|
return exception
|
|
74
74
|
|
|
75
|
-
if TYPE_CHECKING:
|
|
76
|
-
def __getitem__(self, exception: type[Exception]) -> CustomError:
|
|
77
|
-
...
|
|
78
|
-
|
|
79
75
|
|
|
80
76
|
SelfCErrorMeta = TypeVar('SelfCErrorMeta', bound=CustomErrorMeta)
|
|
81
77
|
|
|
@@ -102,17 +98,11 @@ class CustomError(ExceptionT, metaclass=CustomErrorMeta):
|
|
|
102
98
|
super().__init__(message)
|
|
103
99
|
|
|
104
100
|
def __class_getitem__(cls, exception: str | type[ExceptionT] | ExceptionT) -> CustomError:
|
|
105
|
-
|
|
106
|
-
class inner_exception(cls): # type: ignore
|
|
107
|
-
...
|
|
108
|
-
else:
|
|
109
|
-
if not issubclass(exception, type): # type: ignore
|
|
110
|
-
exception = exception.__class__ # type: ignore
|
|
101
|
+
from warnings import warn
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
...
|
|
103
|
+
warn("Custom error is not subscriptable anymore. Don't use it", DeprecationWarning)
|
|
114
104
|
|
|
115
|
-
return
|
|
105
|
+
return cls()
|
|
116
106
|
|
|
117
107
|
def __call__(
|
|
118
108
|
self,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from typing import Any, Callable, Concatenate, overload
|
|
4
|
+
from inspect import signature
|
|
4
5
|
|
|
5
|
-
from ..exceptions import CustomRuntimeError
|
|
6
|
+
from ..exceptions import CustomRuntimeError, CustomValueError
|
|
6
7
|
from ..types import MISSING, KwargsT, MissingT, P, R, T
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
9
|
-
'iterate', 'fallback', 'kwargs_fallback'
|
|
10
|
+
'iterate', 'fallback', 'kwargs_fallback', 'filter_kwargs'
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
|
|
@@ -150,3 +151,46 @@ def kwargs_fallback( # type: ignore
|
|
|
150
151
|
"""Utility function to return a fallback value from kwargs if value was not found or is None."""
|
|
151
152
|
|
|
152
153
|
return fallback(value, kwargs[0].get(kwargs[1], None), *fallbacks, default=default)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@overload
|
|
157
|
+
def filter_kwargs(func: Callable[..., Any], kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
158
|
+
...
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@overload
|
|
162
|
+
def filter_kwargs(func: Callable[..., Any], **kwargs: Any) -> dict[str, Any]:
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def filter_kwargs(func: Callable[..., Any], kwargs: dict[str, Any] | None = None, **kw: Any) -> dict[str, Any]:
|
|
167
|
+
"""
|
|
168
|
+
Filter kwargs to only include parameters that match the callable's signature, ignoring **kwargs.
|
|
169
|
+
|
|
170
|
+
Examples:
|
|
171
|
+
|
|
172
|
+
>>> def my_func(a: int, b: str, c: bool = True):
|
|
173
|
+
... return a, b, c
|
|
174
|
+
>>> filter_kwargs(my_func, a=1, b="hello", c=False, d="extra")
|
|
175
|
+
{'a': 1, 'b': 'hello', 'c': False}
|
|
176
|
+
>>> filter_kwargs(my_func, {"a": 1, "b": "hello", "c": False, "d": "extra"})
|
|
177
|
+
{'a': 1, 'b': 'hello', 'c': False}
|
|
178
|
+
|
|
179
|
+
:param func: The callable to filter kwargs for.
|
|
180
|
+
:param kwargs: Dictionary of keyword arguments to filter.
|
|
181
|
+
:param **kw: Keyword arguments to filter (used when kwargs is None).
|
|
182
|
+
|
|
183
|
+
:return: A dictionary containing only the kwargs that match the callable's parameters.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
if not (filtered_kwargs := fallback(kwargs, kw)):
|
|
187
|
+
return {}
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
sig = signature(func)
|
|
191
|
+
except Exception as e:
|
|
192
|
+
raise CustomValueError(e.args[0], filter_kwargs, func) from e
|
|
193
|
+
|
|
194
|
+
param_names = {name for name, param in sig.parameters.items() if param.kind != param.VAR_KEYWORD}
|
|
195
|
+
|
|
196
|
+
return {name: value for name, value in filtered_kwargs.items() if name in param_names}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from fractions import Fraction
|
|
4
|
+
import sys
|
|
4
5
|
from typing import Any, Callable, Iterable, Iterator, Sequence, overload
|
|
5
6
|
|
|
6
7
|
from ..types import SoftRange, SoftRangeN, SoftRangesN, StrictRange, SupportsString, T, is_soft_range_n
|
|
8
|
+
from ..exceptions import CustomOverflowError
|
|
7
9
|
|
|
8
10
|
__all__ = [
|
|
9
11
|
'normalize_seq',
|
|
@@ -93,11 +95,13 @@ def flatten(items: Any) -> Iterator[Any]:
|
|
|
93
95
|
yield val
|
|
94
96
|
|
|
95
97
|
|
|
96
|
-
def normalize_range(ranges: SoftRange,
|
|
98
|
+
def normalize_range(ranges: SoftRange, /, exclusive: bool = False) -> Sequence[int]:
|
|
97
99
|
"""
|
|
98
100
|
Normalize ranges represented by a tuple to an iterable of frame numbers.
|
|
99
101
|
|
|
100
102
|
:param ranges: Ranges to normalize.
|
|
103
|
+
:param exclusive: Whether to use exclusive (Python-style) ranges.
|
|
104
|
+
Defaults to False.
|
|
101
105
|
|
|
102
106
|
:return: List of positive frame ranges.
|
|
103
107
|
"""
|
|
@@ -109,12 +113,12 @@ def normalize_range(ranges: SoftRange, /) -> Iterable[int]:
|
|
|
109
113
|
start, stop = ranges
|
|
110
114
|
step = -1 if stop < start else 1
|
|
111
115
|
|
|
112
|
-
return range(start, stop + step, step)
|
|
116
|
+
return range(start, stop + (not exclusive * step), step)
|
|
113
117
|
|
|
114
118
|
return ranges
|
|
115
119
|
|
|
116
120
|
|
|
117
|
-
def normalize_list_to_ranges(flist: Iterable[int], min_length: int = 0) -> list[StrictRange]:
|
|
121
|
+
def normalize_list_to_ranges(flist: Iterable[int], min_length: int = 0, exclusive: bool = False) -> list[StrictRange]:
|
|
118
122
|
flist2 = list[list[int]]()
|
|
119
123
|
flist3 = list[int]()
|
|
120
124
|
|
|
@@ -135,20 +139,26 @@ def normalize_list_to_ranges(flist: Iterable[int], min_length: int = 0) -> list[
|
|
|
135
139
|
|
|
136
140
|
return list(zip(
|
|
137
141
|
[i[0] for i in flist4],
|
|
138
|
-
[i[-1] for
|
|
142
|
+
[i[-1] + exclusive for i in flist4]
|
|
139
143
|
))
|
|
140
144
|
|
|
141
145
|
|
|
142
|
-
def normalize_ranges_to_list(ranges: Iterable[SoftRange]) -> list[int]:
|
|
146
|
+
def normalize_ranges_to_list(ranges: Iterable[SoftRange], exclusive: bool = False) -> list[int]:
|
|
143
147
|
out = list[int]()
|
|
144
148
|
|
|
145
149
|
for srange in ranges:
|
|
146
|
-
out.extend(normalize_range(srange))
|
|
150
|
+
out.extend(normalize_range(srange, exclusive))
|
|
147
151
|
|
|
148
152
|
return out
|
|
149
153
|
|
|
150
154
|
|
|
151
|
-
def normalize_ranges(
|
|
155
|
+
def normalize_ranges(
|
|
156
|
+
ranges: SoftRangeN | SoftRangesN,
|
|
157
|
+
length: int,
|
|
158
|
+
exclusive: bool = False,
|
|
159
|
+
*,
|
|
160
|
+
strict: bool = True
|
|
161
|
+
) -> list[StrictRange]:
|
|
152
162
|
"""
|
|
153
163
|
Normalize ranges to a list of positive ranges.
|
|
154
164
|
|
|
@@ -160,57 +170,104 @@ def normalize_ranges(ranges: SoftRangeN | SoftRangesN, end: int) -> list[StrictR
|
|
|
160
170
|
|
|
161
171
|
.. code-block:: python
|
|
162
172
|
|
|
163
|
-
>>> normalize_ranges((None, None),
|
|
173
|
+
>>> normalize_ranges((None, None), length=1000)
|
|
164
174
|
[(0, 999)]
|
|
165
|
-
>>> normalize_ranges((24, -24),
|
|
175
|
+
>>> normalize_ranges((24, -24), length=1000)
|
|
166
176
|
[(24, 975)]
|
|
167
|
-
>>> normalize_ranges([(24, 100), (80, 150)],
|
|
177
|
+
>>> normalize_ranges([(24, 100), (80, 150)], length=1000)
|
|
168
178
|
[(24, 150)]
|
|
169
179
|
|
|
170
180
|
|
|
171
|
-
:param
|
|
172
|
-
:param
|
|
181
|
+
:param ranges: Frame range or list of frame ranges.
|
|
182
|
+
:param length: Number of frames.
|
|
183
|
+
:param exclusive: Whether to use exclusive (Python-style) ranges.
|
|
184
|
+
Defaults to False.
|
|
185
|
+
:param strict: Whether to enforce strict checking for out-of-range values.
|
|
173
186
|
|
|
174
187
|
:return: List of positive frame ranges.
|
|
175
188
|
"""
|
|
189
|
+
from ..utils import clamp
|
|
176
190
|
|
|
177
191
|
ranges = [ranges] if is_soft_range_n(ranges) else ranges
|
|
178
192
|
|
|
179
|
-
out = []
|
|
193
|
+
out = list[tuple[int, int]]()
|
|
194
|
+
exceptions = list[Exception]()
|
|
180
195
|
|
|
181
196
|
for r in ranges:
|
|
182
197
|
if r is None:
|
|
183
198
|
r = (None, None)
|
|
184
199
|
|
|
185
200
|
if isinstance(r, tuple):
|
|
186
|
-
start,
|
|
201
|
+
start, end = r
|
|
187
202
|
if start is None:
|
|
188
203
|
start = 0
|
|
189
|
-
if
|
|
190
|
-
|
|
204
|
+
if end is None:
|
|
205
|
+
end = length - (not exclusive)
|
|
191
206
|
else:
|
|
192
207
|
start = r
|
|
193
|
-
|
|
208
|
+
end = r + exclusive
|
|
194
209
|
|
|
195
210
|
if start < 0:
|
|
196
|
-
start =
|
|
211
|
+
start = length + start
|
|
212
|
+
|
|
213
|
+
if end < 0:
|
|
214
|
+
end = length + end - (not exclusive)
|
|
215
|
+
|
|
216
|
+
# Always throws an error if start and end are negative
|
|
217
|
+
# or higher than length
|
|
218
|
+
# or start is higher than end (likely mismatched)
|
|
219
|
+
if any([
|
|
220
|
+
start < 0 and end < 0,
|
|
221
|
+
start >= length and end - (not exclusive) > length,
|
|
222
|
+
start >= end + (not exclusive),
|
|
223
|
+
]):
|
|
224
|
+
exception = CustomOverflowError(
|
|
225
|
+
f"Range `{r}` with length `{length}` could not be normalized!", normalize_ranges
|
|
226
|
+
)
|
|
227
|
+
exceptions.append(exception)
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
if strict:
|
|
231
|
+
if start < 0:
|
|
232
|
+
exception = CustomOverflowError(
|
|
233
|
+
f"Start frame `{start}` in range `{r}` with length `{length}` could not be normalized!",
|
|
234
|
+
normalize_ranges
|
|
235
|
+
)
|
|
236
|
+
exceptions.append(exception)
|
|
237
|
+
continue
|
|
238
|
+
if end - (not exclusive) > length:
|
|
239
|
+
exception = CustomOverflowError(
|
|
240
|
+
f"End frame `{end}` in range `{r}` with length `{length}` could not be normalized!",
|
|
241
|
+
normalize_ranges
|
|
242
|
+
)
|
|
243
|
+
exceptions.append(exception)
|
|
244
|
+
continue
|
|
245
|
+
else:
|
|
246
|
+
start = clamp(start, 0, length - 1)
|
|
247
|
+
end = clamp(end, int(exclusive), length - (not exclusive))
|
|
248
|
+
|
|
249
|
+
out.append((start, end))
|
|
197
250
|
|
|
198
|
-
|
|
199
|
-
|
|
251
|
+
if exceptions:
|
|
252
|
+
if sys.version_info >= (3, 11):
|
|
253
|
+
raise ExceptionGroup("Multiple exceptions occurred!", exceptions) # noqa: F821
|
|
200
254
|
|
|
201
|
-
|
|
255
|
+
raise Exception(exceptions)
|
|
202
256
|
|
|
203
|
-
return normalize_list_to_ranges(
|
|
204
|
-
x for start,
|
|
205
|
-
|
|
257
|
+
return normalize_list_to_ranges(
|
|
258
|
+
[x for start, end in out for x in range(start, end + (not exclusive))],
|
|
259
|
+
exclusive=exclusive
|
|
260
|
+
)
|
|
206
261
|
|
|
207
262
|
|
|
208
|
-
def invert_ranges(
|
|
209
|
-
|
|
263
|
+
def invert_ranges(
|
|
264
|
+
ranges: SoftRangeN | SoftRangesN, lengtha: int, lengthb: int | None, exclusive: bool = False
|
|
265
|
+
) -> list[StrictRange]:
|
|
266
|
+
norm_ranges = normalize_ranges(ranges, lengtha if lengthb is None else lengthb, exclusive)
|
|
210
267
|
|
|
211
|
-
b_frames = {*normalize_ranges_to_list(norm_ranges)}
|
|
268
|
+
b_frames = {*normalize_ranges_to_list(norm_ranges, exclusive)}
|
|
212
269
|
|
|
213
|
-
return normalize_list_to_ranges({*range(
|
|
270
|
+
return normalize_list_to_ranges({*range(lengtha)} - b_frames, exclusive=exclusive)
|
|
214
271
|
|
|
215
272
|
|
|
216
273
|
def norm_func_name(func_name: SupportsString | Callable[..., Any]) -> str:
|
|
@@ -2,8 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import fnmatch
|
|
4
4
|
import shutil
|
|
5
|
-
|
|
6
|
-
from os import PathLike, listdir, path, walk
|
|
5
|
+
from os import X_OK, PathLike, access, listdir, path, walk
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
from sys import version_info
|
|
9
8
|
from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, TypeAlias, Union
|
|
@@ -26,6 +25,7 @@ __all__ = [
|
|
|
26
25
|
'SPath', 'SPathLike'
|
|
27
26
|
]
|
|
28
27
|
|
|
28
|
+
|
|
29
29
|
FileDescriptor: TypeAlias = int
|
|
30
30
|
|
|
31
31
|
FilePathType: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes]
|
|
@@ -60,6 +60,7 @@ OpenBinaryMode: TypeAlias = OpenBinaryModeUpdating | OpenBinaryModeReading | Ope
|
|
|
60
60
|
|
|
61
61
|
class SPath(Path):
|
|
62
62
|
"""Modified version of pathlib.Path"""
|
|
63
|
+
|
|
63
64
|
if version_info < (3, 12):
|
|
64
65
|
_flavour = type(Path())._flavour # type: ignore
|
|
65
66
|
|
|
@@ -195,5 +196,10 @@ class SPath(Path):
|
|
|
195
196
|
|
|
196
197
|
return sum(f.stat().st_size for f in self.rglob('*') if f.is_file())
|
|
197
198
|
|
|
199
|
+
def is_executable(self) -> bool:
|
|
200
|
+
"""Check if the path is executable."""
|
|
201
|
+
|
|
202
|
+
return access(self.to_str(), X_OK)
|
|
203
|
+
|
|
198
204
|
|
|
199
205
|
SPathLike = Union[str, PathLike[str], Path, SPath]
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from functools import partial, wraps
|
|
4
4
|
from inspect import Signature
|
|
5
5
|
from inspect import _empty as empty_param
|
|
6
|
-
from inspect import isclass
|
|
7
6
|
from typing import (
|
|
8
7
|
TYPE_CHECKING, Any, Callable, Concatenate, Generator, Generic, Iterable, Iterator, Mapping, NoReturn, Protocol,
|
|
9
8
|
Sequence, TypeVar, cast, overload
|
|
@@ -411,89 +410,55 @@ def get_subclasses(family: type[T], exclude: Sequence[type[T]] = []) -> list[typ
|
|
|
411
410
|
return list(set(_subclasses(family)))
|
|
412
411
|
|
|
413
412
|
|
|
414
|
-
class classproperty(Generic[
|
|
413
|
+
class classproperty(Generic[T, R]):
|
|
415
414
|
"""
|
|
416
415
|
Make a class property. A combination between classmethod and property.
|
|
417
416
|
"""
|
|
418
417
|
|
|
419
418
|
__isabstractmethod__: bool = False
|
|
420
419
|
|
|
421
|
-
class metaclass(type):
|
|
422
|
-
"""This must be set for the decorator to work."""
|
|
423
|
-
|
|
424
|
-
def __setattr__(self, key: str, value: Any) -> None:
|
|
425
|
-
if key in self.__dict__:
|
|
426
|
-
obj = self.__dict__.get(key)
|
|
427
|
-
|
|
428
|
-
if obj and isinstance(obj, classproperty):
|
|
429
|
-
obj.__set__(self, value)
|
|
430
|
-
return
|
|
431
|
-
|
|
432
|
-
super(classproperty.metaclass, self).__setattr__(key, value)
|
|
433
|
-
|
|
434
420
|
def __init__(
|
|
435
421
|
self,
|
|
436
|
-
fget:
|
|
437
|
-
fset:
|
|
438
|
-
fdel:
|
|
422
|
+
fget: Callable[[type[T]], R] | classmethod[T, ..., R],
|
|
423
|
+
fset: Callable[[type[T], R], None] | classmethod[T, Concatenate[R, ...], None] | None = None,
|
|
424
|
+
fdel: Callable[[type[T]], None] | classmethod[T, ..., None] | None = None,
|
|
439
425
|
doc: str | None = None,
|
|
440
426
|
) -> None:
|
|
441
427
|
self.fget = self._wrap(fget)
|
|
442
428
|
self.fset = self._wrap(fset) if fset is not None else fset
|
|
443
429
|
self.fdel = self._wrap(fdel) if fdel is not None else fdel
|
|
444
430
|
|
|
445
|
-
self.
|
|
431
|
+
self.__doc__ = doc
|
|
446
432
|
|
|
447
|
-
def _wrap(self, func:
|
|
448
|
-
if not isinstance(func,
|
|
449
|
-
func = classmethod(func)
|
|
433
|
+
def _wrap(self, func: Callable[..., R1] | classmethod[T, P1, R1]) -> classmethod[T, P1, R1]:
|
|
434
|
+
if not isinstance(func, classmethod):
|
|
435
|
+
func = classmethod(func)
|
|
450
436
|
|
|
451
|
-
return func
|
|
437
|
+
return func
|
|
452
438
|
|
|
453
|
-
def
|
|
454
|
-
self.fget = self._wrap(__fget) # type: ignore
|
|
455
|
-
return self # type: ignore
|
|
456
|
-
|
|
457
|
-
def setter(self, __fset: classmethod[T1, P, None] | Callable[[T1, T2], None]) -> classproperty[P, R, T1, T2, P0]:
|
|
458
|
-
self.fset = self._wrap(__fset) # type: ignore
|
|
459
|
-
return self # type: ignore
|
|
460
|
-
|
|
461
|
-
def deleter(self, __fdel: classmethod[T1, P1, None] | Callable[P1, None]) -> classproperty[P, R, T, T0, P1]:
|
|
462
|
-
self.fdel = self._wrap(__fdel) # type: ignore
|
|
463
|
-
return self # type: ignore
|
|
464
|
-
|
|
465
|
-
def __get__(self, __obj: Any, __type: type | None = None) -> R:
|
|
439
|
+
def __get__(self, __obj: T | None, __type: type | None = None) -> R:
|
|
466
440
|
if __type is None:
|
|
467
441
|
__type = type(__obj)
|
|
468
442
|
|
|
469
|
-
return self.fget.__get__(__obj, __type)()
|
|
470
|
-
|
|
471
|
-
def __set__(self, __obj: Any, __value: Any) -> None:
|
|
472
|
-
from ..exceptions import CustomError
|
|
443
|
+
return self.fget.__get__(__obj, __type)()
|
|
473
444
|
|
|
445
|
+
def __set__(self, __obj: T, __value: Any) -> None:
|
|
474
446
|
if not self.fset:
|
|
475
|
-
raise
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
type_, __obj = __obj, None
|
|
479
|
-
else:
|
|
480
|
-
type_ = type(__obj)
|
|
481
|
-
|
|
482
|
-
return self.fset.__get__(__obj, type_)(__value) # type: ignore[call-arg]
|
|
447
|
+
raise AttributeError(
|
|
448
|
+
f'classproperty with getter "{self.__name__}" of "{__obj.__class__.__name__}" object has no setter.'
|
|
449
|
+
)
|
|
483
450
|
|
|
484
|
-
|
|
485
|
-
from ..exceptions import CustomError
|
|
451
|
+
self.fset.__get__(None, type(__obj))(__value)
|
|
486
452
|
|
|
453
|
+
def __delete__(self, __obj: T) -> None:
|
|
487
454
|
if not self.fdel:
|
|
488
|
-
raise
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
type_, __obj = __obj, None
|
|
492
|
-
else:
|
|
493
|
-
type_ = type(__obj)
|
|
455
|
+
raise AttributeError(
|
|
456
|
+
f'classproperty with getter "{self.__name__}" of "{__obj.__class__.__name__}" object has no deleter.'
|
|
457
|
+
)
|
|
494
458
|
|
|
495
|
-
|
|
459
|
+
self.fdel.__get__(None, type(__obj))()
|
|
496
460
|
|
|
461
|
+
@property
|
|
497
462
|
def __name__(self) -> str:
|
|
498
463
|
return self.fget.__name__
|
|
499
464
|
|
|
@@ -132,6 +132,8 @@ def spline_coeff(
|
|
|
132
132
|
for k in range(i, length + 1):
|
|
133
133
|
matrix[j][k] -= a * matrix[i][k]
|
|
134
134
|
|
|
135
|
+
i = 0
|
|
136
|
+
|
|
135
137
|
for i in range(length + 1):
|
|
136
138
|
if x >= px[i] and x <= px[i + 1]:
|
|
137
139
|
break
|
|
@@ -148,7 +150,7 @@ def spline_coeff(
|
|
|
148
150
|
s += (py[j] / h - h * matrix[j][length] / 6) * (x - px[i])
|
|
149
151
|
s -= (py[i] / h - h * matrix[i][length] / 6) * (x - px[j])
|
|
150
152
|
|
|
151
|
-
return s
|
|
153
|
+
return float(s)
|
|
152
154
|
|
|
153
155
|
|
|
154
156
|
def ndigits(num: Nb) -> int:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from jetpytools import fallback, iterate
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_iterate() -> None:
|
|
7
|
+
assert iterate(5, lambda x: x * 2, 2) == 20
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_fallback() -> None:
|
|
11
|
+
assert fallback(5, 6) == 5
|
|
12
|
+
assert fallback(None, 6) == 6
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from jetpytools import CustomOverflowError, normalize_ranges
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if sys.version_info < (3, 11):
|
|
10
|
+
ExceptionGroup = Exception
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def assert_excinfo_group_contains(
|
|
14
|
+
excinfo: pytest.ExceptionInfo, exception: type[Exception] # type: ignore
|
|
15
|
+
) -> None:
|
|
16
|
+
if sys.version_info < (3, 11):
|
|
17
|
+
assert isinstance(excinfo.value, Exception)
|
|
18
|
+
else:
|
|
19
|
+
assert excinfo.group_contains(exception)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_normalize_ranges() -> None:
|
|
23
|
+
# Inclusive ranges
|
|
24
|
+
assert normalize_ranges((None, None), length=1000, exclusive=False) == [(0, 999)]
|
|
25
|
+
assert normalize_ranges((24, -24), length=1000, exclusive=False) == [(24, 975)]
|
|
26
|
+
assert normalize_ranges((-100, 950), length=1000, exclusive=False) == [(900, 950)]
|
|
27
|
+
assert normalize_ranges([(24, 100), (80, 150)], length=1000, exclusive=False) == [(24, 150)]
|
|
28
|
+
assert normalize_ranges([500], length=1000, exclusive=False) == [(500, 500)]
|
|
29
|
+
|
|
30
|
+
# Exclusive ranges
|
|
31
|
+
assert normalize_ranges((None, None), length=1000, exclusive=True) == [(0, 1000)]
|
|
32
|
+
assert normalize_ranges((24, -24), length=1000, exclusive=True) == [(24, 976)]
|
|
33
|
+
assert normalize_ranges((-100, 950), length=1000, exclusive=True) == [(900, 950)]
|
|
34
|
+
assert normalize_ranges([(24, 100), (80, 150)], length=1000, exclusive=True) == [(24, 150)]
|
|
35
|
+
assert normalize_ranges([500], length=1000, exclusive=True) == [(500, 501)]
|
|
36
|
+
|
|
37
|
+
# Overflow
|
|
38
|
+
with pytest.raises(ExceptionGroup) as excinfo:
|
|
39
|
+
normalize_ranges((500, 1500), length=1000)
|
|
40
|
+
assert_excinfo_group_contains(excinfo, CustomOverflowError)
|
|
41
|
+
|
|
42
|
+
with pytest.raises(ExceptionGroup) as excinfo:
|
|
43
|
+
normalize_ranges((-2000, 500), length=1000)
|
|
44
|
+
assert_excinfo_group_contains(excinfo, CustomOverflowError)
|
|
45
|
+
|
|
46
|
+
with pytest.raises(ExceptionGroup) as excinfo:
|
|
47
|
+
normalize_ranges((-2000, 2000), length=1000)
|
|
48
|
+
assert_excinfo_group_contains(excinfo, CustomOverflowError)
|
|
49
|
+
|
|
50
|
+
assert normalize_ranges((500, 1500), length=1000, exclusive=False, strict=False) == [(500, 999)]
|
|
51
|
+
assert normalize_ranges((500, 1500), length=1000, exclusive=True, strict=False) == [(500, 1000)]
|
|
52
|
+
|
|
53
|
+
assert normalize_ranges((-1500, 500), length=1000, exclusive=False, strict=False) == [(0, 500)]
|
|
54
|
+
assert normalize_ranges((-1500, 500), length=1000, exclusive=True, strict=False) == [(0, 500)]
|
|
55
|
+
|
|
56
|
+
assert normalize_ranges((-2000, 2000), length=1000, exclusive=False, strict=False) == [(0, 999)]
|
|
57
|
+
assert normalize_ranges((-2000, 2000), length=1000, exclusive=True, strict=False) == [(0, 1000)]
|
|
58
|
+
|
|
59
|
+
assert normalize_ranges((0, 0), length=1000, exclusive=False, strict=True) == [(0, 0)]
|
|
60
|
+
|
|
61
|
+
with pytest.raises(ExceptionGroup) as excinfo:
|
|
62
|
+
normalize_ranges((0, 0), length=1000, exclusive=True, strict=True)
|
|
63
|
+
assert_excinfo_group_contains(excinfo, CustomOverflowError)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|