pythonwrench 0.5.0__tar.gz → 0.5.1__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.
- {pythonwrench-0.5.0/src/pythonwrench.egg-info → pythonwrench-0.5.1}/PKG-INFO +4 -3
- pythonwrench-0.5.1/docs/requirements.txt +4 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/pyproject.toml +3 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/__init__.py +9 -1
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/_core.py +1 -1
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/checksum.py +98 -35
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/collections/collections.py +57 -1
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/disk_cache.py +5 -9
- pythonwrench-0.5.1/src/pythonwrench/time.py +31 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/typing/__init__.py +8 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/typing/checks.py +97 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/typing/classes.py +2 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1/src/pythonwrench.egg-info}/PKG-INFO +4 -3
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench.egg-info/SOURCES.txt +2 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench.egg-info/requires.txt +3 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_checksum.py +20 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_collections.py +14 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_typing.py +23 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/LICENSE +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/README.md +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/setup.cfg +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/setup.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/argparse.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/cast.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/collections/__init__.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/collections/reducers.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/concurrent.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/dataclasses.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/entries.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/functools.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/jsonl.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/logging.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/os.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/pickle.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/re.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/semver.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench/warnings.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench.egg-info/entry_points.txt +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/src/pythonwrench.egg-info/top_level.txt +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_abc.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_argparse.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_cast.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_csv.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_difflib.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_disk_cache.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_entries.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_enum.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_functools.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_importlib.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_inspect.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_json.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_logging.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_math.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_os.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_random.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_readme.py +0 -0
- {pythonwrench-0.5.0 → pythonwrench-0.5.1}/tests/test_semver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Python library with tools for typing, manipulating collections, and more!
|
|
5
5
|
Author-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
6
6
|
Maintainer-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
@@ -53,7 +53,8 @@ License-File: LICENSE
|
|
|
53
53
|
Requires-Dist: lazy-loader>=0.4
|
|
54
54
|
Requires-Dist: typing-extensions>=4.10.0
|
|
55
55
|
Provides-Extra: dev
|
|
56
|
-
|
|
56
|
+
Provides-Extra: lazy
|
|
57
|
+
Requires-Dist: lazy-loader>=0.4; extra == "lazy"
|
|
57
58
|
|
|
58
59
|
# pythonwrench
|
|
59
60
|
|
|
@@ -9,7 +9,7 @@ __author_email__ = "labbeti.pub@gmail.com"
|
|
|
9
9
|
__license__ = "MIT"
|
|
10
10
|
__maintainer__ = "Étienne Labbé (Labbeti)"
|
|
11
11
|
__status__ = "Development"
|
|
12
|
-
__version__ = "0.5.
|
|
12
|
+
__version__ = "0.5.1"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
from typing import TYPE_CHECKING
|
|
@@ -187,6 +187,7 @@ if TYPE_CHECKING or lazy is None:
|
|
|
187
187
|
SupportsGetitemIterLen2,
|
|
188
188
|
SupportsGetitemLen,
|
|
189
189
|
SupportsGetitemLen2,
|
|
190
|
+
SupportsIter,
|
|
190
191
|
SupportsIterLen,
|
|
191
192
|
SupportsLen,
|
|
192
193
|
SupportsMatmul,
|
|
@@ -199,6 +200,7 @@ if TYPE_CHECKING or lazy is None:
|
|
|
199
200
|
is_builtin_number,
|
|
200
201
|
is_builtin_obj,
|
|
201
202
|
is_builtin_scalar,
|
|
203
|
+
is_collection_alias,
|
|
202
204
|
is_dataclass_instance,
|
|
203
205
|
is_iterable_bool,
|
|
204
206
|
is_iterable_bytes_or_list,
|
|
@@ -207,7 +209,9 @@ if TYPE_CHECKING or lazy is None:
|
|
|
207
209
|
is_iterable_integral,
|
|
208
210
|
is_iterable_str,
|
|
209
211
|
is_namedtuple_instance,
|
|
212
|
+
is_parameterized,
|
|
210
213
|
is_sequence_str,
|
|
214
|
+
is_special_form,
|
|
211
215
|
is_typed_dict,
|
|
212
216
|
isinstance_generic,
|
|
213
217
|
)
|
|
@@ -397,6 +401,7 @@ else:
|
|
|
397
401
|
"SupportsGetitemIterLen2",
|
|
398
402
|
"SupportsGetitemLen",
|
|
399
403
|
"SupportsGetitemLen2",
|
|
404
|
+
"SupportsIter",
|
|
400
405
|
"SupportsIterLen",
|
|
401
406
|
"SupportsLen",
|
|
402
407
|
"SupportsMatmul",
|
|
@@ -409,6 +414,7 @@ else:
|
|
|
409
414
|
"is_builtin_number",
|
|
410
415
|
"is_builtin_obj",
|
|
411
416
|
"is_builtin_scalar",
|
|
417
|
+
"is_collection_alias",
|
|
412
418
|
"is_dataclass_instance",
|
|
413
419
|
"is_iterable_bool",
|
|
414
420
|
"is_iterable_bytes_or_list",
|
|
@@ -417,7 +423,9 @@ else:
|
|
|
417
423
|
"is_iterable_integral",
|
|
418
424
|
"is_iterable_str",
|
|
419
425
|
"is_namedtuple_instance",
|
|
426
|
+
"is_parameterized",
|
|
420
427
|
"is_sequence_str",
|
|
428
|
+
"is_special_form",
|
|
421
429
|
"is_typed_dict",
|
|
422
430
|
"isinstance_generic",
|
|
423
431
|
],
|
|
@@ -6,6 +6,7 @@ import re
|
|
|
6
6
|
import struct
|
|
7
7
|
import zlib
|
|
8
8
|
from dataclasses import asdict
|
|
9
|
+
from datetime import date, datetime
|
|
9
10
|
from enum import Enum
|
|
10
11
|
from functools import lru_cache
|
|
11
12
|
from pathlib import Path
|
|
@@ -13,12 +14,15 @@ from types import FunctionType, MethodType
|
|
|
13
14
|
from typing import (
|
|
14
15
|
Any,
|
|
15
16
|
Callable,
|
|
17
|
+
Dict,
|
|
16
18
|
Generator,
|
|
17
19
|
Iterable,
|
|
18
20
|
Mapping,
|
|
19
21
|
Optional,
|
|
20
22
|
TypeVar,
|
|
21
23
|
Union,
|
|
24
|
+
get_args,
|
|
25
|
+
get_origin,
|
|
22
26
|
overload,
|
|
23
27
|
)
|
|
24
28
|
|
|
@@ -27,14 +31,19 @@ from pythonwrench.functools import function_alias
|
|
|
27
31
|
from pythonwrench.inspect import get_fullname
|
|
28
32
|
from pythonwrench.typing import (
|
|
29
33
|
DataclassInstance,
|
|
34
|
+
EllipsisType,
|
|
30
35
|
NamedTupleInstance,
|
|
31
36
|
NoneType,
|
|
37
|
+
is_collection_alias,
|
|
38
|
+
is_parameterized,
|
|
39
|
+
is_special_form,
|
|
32
40
|
)
|
|
33
41
|
|
|
34
42
|
T = TypeVar("T")
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
_CHECKSUM_REGISTRY = _FunctionRegistry[int]()
|
|
46
|
+
_CHECKSUM_PROTOCOLS = False
|
|
38
47
|
|
|
39
48
|
|
|
40
49
|
@overload
|
|
@@ -130,9 +139,7 @@ def checksum_int(x: int, **kwargs) -> int:
|
|
|
130
139
|
# Intermediate functions
|
|
131
140
|
@register_checksum_fn(bytearray)
|
|
132
141
|
def checksum_bytearray(x: bytearray, **kwargs) -> int:
|
|
133
|
-
kwargs
|
|
134
|
-
get_fullname(x)
|
|
135
|
-
)
|
|
142
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
136
143
|
return _checksum_bytes_bytearray(x, **kwargs)
|
|
137
144
|
|
|
138
145
|
|
|
@@ -151,25 +158,25 @@ def checksum_complex(x: complex, **kwargs) -> int:
|
|
|
151
158
|
|
|
152
159
|
@register_checksum_fn(FunctionType)
|
|
153
160
|
def checksum_function(x: FunctionType, **kwargs) -> int:
|
|
154
|
-
kwargs
|
|
155
|
-
get_fullname(x)
|
|
156
|
-
)
|
|
161
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
157
162
|
return checksum_str(x.__qualname__, **kwargs)
|
|
158
163
|
|
|
159
164
|
|
|
160
165
|
@register_checksum_fn(NoneType)
|
|
161
166
|
def checksum_none(x: None, **kwargs) -> int:
|
|
162
|
-
kwargs
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
168
|
+
return checksum_type(x.__class__, **kwargs)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@register_checksum_fn(EllipsisType)
|
|
172
|
+
def checksum_ellipsis(x: None, **kwargs) -> int:
|
|
173
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
174
|
+
return checksum_type(x.__class__, **kwargs)
|
|
166
175
|
|
|
167
176
|
|
|
168
177
|
@register_checksum_fn(str)
|
|
169
178
|
def checksum_str(x: str, **kwargs) -> int:
|
|
170
|
-
kwargs
|
|
171
|
-
get_fullname(x)
|
|
172
|
-
)
|
|
179
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
173
180
|
return checksum_bytes(x.encode(), **kwargs)
|
|
174
181
|
|
|
175
182
|
|
|
@@ -181,12 +188,34 @@ def checksum_type(x: type, **kwargs) -> int:
|
|
|
181
188
|
# Recursive functions
|
|
182
189
|
@register_checksum_fn(DataclassInstance)
|
|
183
190
|
def checksum_dataclass(x: DataclassInstance, **kwargs) -> int:
|
|
184
|
-
kwargs
|
|
185
|
-
get_fullname(x)
|
|
186
|
-
)
|
|
191
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
187
192
|
return checksum_dict(asdict(x), **kwargs)
|
|
188
193
|
|
|
189
194
|
|
|
195
|
+
@register_checksum_fn(datetime)
|
|
196
|
+
def checksum_datetime(x: datetime, **kwargs) -> int:
|
|
197
|
+
return _checksum_iterable(
|
|
198
|
+
[
|
|
199
|
+
x.__class__,
|
|
200
|
+
x.year,
|
|
201
|
+
x.month,
|
|
202
|
+
x.day,
|
|
203
|
+
x.hour,
|
|
204
|
+
x.minute,
|
|
205
|
+
x.second,
|
|
206
|
+
x.microsecond,
|
|
207
|
+
x.tzinfo,
|
|
208
|
+
x.fold,
|
|
209
|
+
],
|
|
210
|
+
**kwargs,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@register_checksum_fn(date)
|
|
215
|
+
def checksum_date(x: date, **kwargs) -> int:
|
|
216
|
+
return _checksum_iterable([x.__class__, x.year, x.month, x.day], **kwargs)
|
|
217
|
+
|
|
218
|
+
|
|
190
219
|
@register_checksum_fn(dict)
|
|
191
220
|
def checksum_dict(x: dict, **kwargs) -> int:
|
|
192
221
|
return _checksum_mapping(x, **kwargs)
|
|
@@ -214,9 +243,7 @@ def checksum_set(x: Union[set, frozenset], **kwargs) -> int:
|
|
|
214
243
|
|
|
215
244
|
@register_checksum_fn(range)
|
|
216
245
|
def checksum_range(x: range, **kwargs) -> int:
|
|
217
|
-
kwargs
|
|
218
|
-
get_fullname(x)
|
|
219
|
-
)
|
|
246
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
220
247
|
return _checksum_iterable([x.start, x.stop, x.step], **kwargs)
|
|
221
248
|
|
|
222
249
|
|
|
@@ -238,48 +265,76 @@ def checksum_method(x: MethodType, **kwargs) -> int:
|
|
|
238
265
|
|
|
239
266
|
@register_checksum_fn(NamedTupleInstance)
|
|
240
267
|
def checksum_namedtuple(x: NamedTupleInstance, **kwargs) -> int:
|
|
241
|
-
kwargs
|
|
242
|
-
get_fullname(x)
|
|
243
|
-
)
|
|
268
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
244
269
|
return checksum_dict(x._asdict(), **kwargs)
|
|
245
270
|
|
|
246
271
|
|
|
247
272
|
@register_checksum_fn(functools.partial)
|
|
248
273
|
def checksum_partial(x: functools.partial, **kwargs) -> int:
|
|
249
|
-
kwargs
|
|
250
|
-
get_fullname(x)
|
|
251
|
-
)
|
|
274
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
252
275
|
return checksum_list_tuple((x.func, x.args, x.keywords), **kwargs)
|
|
253
276
|
|
|
254
277
|
|
|
255
278
|
@register_checksum_fn(re.Pattern)
|
|
256
279
|
def checksum_pattern(x: re.Pattern, **kwargs) -> int:
|
|
257
|
-
kwargs
|
|
258
|
-
get_fullname(x)
|
|
259
|
-
)
|
|
280
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
260
281
|
return checksum_str(str(x), **kwargs)
|
|
261
282
|
|
|
262
283
|
|
|
263
284
|
@register_checksum_fn(Path)
|
|
264
285
|
def checksum_path(x: Path, **kwargs) -> int:
|
|
265
|
-
kwargs
|
|
266
|
-
|
|
267
|
-
)
|
|
286
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
287
|
+
|
|
268
288
|
resolve_path = kwargs.get("resolve_path", False)
|
|
269
289
|
if isinstance(resolve_path, bool) and resolve_path:
|
|
270
290
|
x = x.expanduser().resolve()
|
|
271
|
-
|
|
272
291
|
return checksum_str(str(x), **kwargs)
|
|
273
292
|
|
|
274
293
|
|
|
275
294
|
@register_checksum_fn(slice)
|
|
276
295
|
def checksum_slice(x: slice, **kwargs) -> int:
|
|
277
|
-
kwargs
|
|
278
|
-
get_fullname(x)
|
|
279
|
-
)
|
|
296
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
280
297
|
return checksum_list_tuple((x.start, x.stop, x.step), **kwargs)
|
|
281
298
|
|
|
282
299
|
|
|
300
|
+
@register_checksum_fn(custom_predicate=is_parameterized)
|
|
301
|
+
def checksum_parametrized(x: Any, **kwargs) -> int:
|
|
302
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
303
|
+
return checksum_list_tuple((get_origin(x),) + get_args(x), **kwargs)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@register_checksum_fn(custom_predicate=is_collection_alias)
|
|
307
|
+
def checksum_collection_alias(x: Any, **kwargs) -> int:
|
|
308
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
309
|
+
return checksum_str(x._name, **kwargs)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@register_checksum_fn(custom_predicate=is_special_form)
|
|
313
|
+
def checksum_special_form(x: Any, **kwargs) -> int:
|
|
314
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
315
|
+
|
|
316
|
+
if hasattr(x, "_name"):
|
|
317
|
+
name = x._name
|
|
318
|
+
elif hasattr(x, "__name__"):
|
|
319
|
+
name = x.__name__
|
|
320
|
+
else:
|
|
321
|
+
msg = f"Unsupported argument {x=} in checksum_special_form."
|
|
322
|
+
raise ValueError(msg)
|
|
323
|
+
|
|
324
|
+
return checksum_str(name, **kwargs)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
if _CHECKSUM_PROTOCOLS:
|
|
328
|
+
|
|
329
|
+
@register_checksum_fn(Mapping, priority=-100)
|
|
330
|
+
def checksum_mapping(x: Mapping, **kwargs) -> int:
|
|
331
|
+
return _checksum_mapping(x, **kwargs)
|
|
332
|
+
|
|
333
|
+
@register_checksum_fn(Iterable, priority=-200)
|
|
334
|
+
def checksum_iterable(x: Iterable, **kwargs) -> int:
|
|
335
|
+
return _checksum_iterable(x, **kwargs)
|
|
336
|
+
|
|
337
|
+
|
|
283
338
|
# Private functions
|
|
284
339
|
def _checksum_bytes_bytearray(x: Union[bytes, bytearray], **kwargs) -> int:
|
|
285
340
|
xint = zlib.crc32(x) % (1 << 32)
|
|
@@ -307,9 +362,17 @@ def _checksum_mapping(x: Mapping, **kwargs) -> int:
|
|
|
307
362
|
|
|
308
363
|
|
|
309
364
|
def _terminate_checksum(x: int, fullname: str, **kwargs) -> int:
|
|
365
|
+
"""Returns checksum for final value + name + accumulator."""
|
|
310
366
|
return x + _cached_checksum_str(fullname) + kwargs.get("accumulator", 0)
|
|
311
367
|
|
|
312
368
|
|
|
369
|
+
def _add_type_checksum_to_accumulator(x: Any, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
370
|
+
kwargs["accumulator"] = kwargs.get("accumulator", 0) + _cached_checksum_str(
|
|
371
|
+
get_fullname(x)
|
|
372
|
+
)
|
|
373
|
+
return kwargs
|
|
374
|
+
|
|
375
|
+
|
|
313
376
|
@lru_cache(maxsize=None)
|
|
314
377
|
def _cached_checksum_str(x: str) -> int:
|
|
315
378
|
return zlib.crc32(x.encode()) % (1 << 32)
|
|
@@ -597,14 +597,70 @@ def recursive_generator(x: Any) -> Generator[Tuple[Any, int, int], None, None]:
|
|
|
597
597
|
return recursive_generator_impl(x, 0, 0)
|
|
598
598
|
|
|
599
599
|
|
|
600
|
+
@overload
|
|
600
601
|
def sorted_dict(
|
|
601
602
|
x: Mapping[K, V],
|
|
602
603
|
/,
|
|
603
604
|
*,
|
|
605
|
+
by: Literal["key"] = "key",
|
|
604
606
|
key: Optional[Callable[[K], Any]] = None,
|
|
605
607
|
reverse: bool = False,
|
|
608
|
+
) -> Dict[K, V]: ...
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
@overload
|
|
612
|
+
def sorted_dict(
|
|
613
|
+
x: Mapping[K, V],
|
|
614
|
+
/,
|
|
615
|
+
*,
|
|
616
|
+
by: Literal["value"],
|
|
617
|
+
key: Optional[Callable[[V], Any]] = None,
|
|
618
|
+
reverse: bool = False,
|
|
619
|
+
) -> Dict[K, V]: ...
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
@overload
|
|
623
|
+
def sorted_dict(
|
|
624
|
+
x: Mapping[K, V],
|
|
625
|
+
/,
|
|
626
|
+
*,
|
|
627
|
+
by: Literal["item"],
|
|
628
|
+
key: Optional[Callable[[Tuple[K, V]], Any]] = None,
|
|
629
|
+
reverse: bool = False,
|
|
630
|
+
) -> Dict[K, V]: ...
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def sorted_dict(
|
|
634
|
+
x: Mapping[K, V],
|
|
635
|
+
/,
|
|
636
|
+
*,
|
|
637
|
+
by: Literal["key", "value", "item"] = "key",
|
|
638
|
+
key: Optional[Callable[[Any], Any]] = None,
|
|
639
|
+
reverse: bool = False,
|
|
606
640
|
) -> Dict[K, V]:
|
|
607
|
-
|
|
641
|
+
"""Sort a dictionnary by key, value or item."""
|
|
642
|
+
if key is None or by == "item":
|
|
643
|
+
impl_key = key
|
|
644
|
+
|
|
645
|
+
elif by == "key":
|
|
646
|
+
|
|
647
|
+
def by_key_fn(x: Tuple[K, V]) -> Any:
|
|
648
|
+
return key(x[0])
|
|
649
|
+
|
|
650
|
+
impl_key = by_key_fn
|
|
651
|
+
|
|
652
|
+
elif by == "value":
|
|
653
|
+
|
|
654
|
+
def by_value_fn(x: Tuple[K, V]) -> Any:
|
|
655
|
+
return key(x[1])
|
|
656
|
+
|
|
657
|
+
impl_key = by_value_fn
|
|
658
|
+
|
|
659
|
+
else:
|
|
660
|
+
msg = f"Invalid argument {by=}. (expected one of {('key', 'value', 'item')})"
|
|
661
|
+
raise ValueError(msg)
|
|
662
|
+
|
|
663
|
+
return {k: v for k, v in sorted(x.items(), key=impl_key, reverse=reverse)} # type: ignore
|
|
608
664
|
|
|
609
665
|
|
|
610
666
|
def shuffled(
|
|
@@ -362,19 +362,15 @@ def _disk_cache_impl(
|
|
|
362
362
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
363
363
|
# for backward compatibility
|
|
364
364
|
if cache_fname_fmt is None:
|
|
365
|
-
expected = "{fn_name}_{
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
DeprecationWarning,
|
|
369
|
-
)
|
|
365
|
+
expected = "{fn_name}_{csum}{suffix}"
|
|
366
|
+
msg = f"Deprecated argument value {cache_fname_fmt=}. (use {expected} instead)"
|
|
367
|
+
warnings.warn(msg, DeprecationWarning)
|
|
370
368
|
cache_fname_fmt = expected
|
|
371
369
|
|
|
372
370
|
if cache_saving_backend is None:
|
|
373
371
|
expected = "auto"
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
DeprecationWarning,
|
|
377
|
-
)
|
|
372
|
+
msg = f"Deprecated argument value {cache_saving_backend=}. (use {expected} instead)"
|
|
373
|
+
warnings.warn(msg, DeprecationWarning)
|
|
378
374
|
cache_saving_backend = expected
|
|
379
375
|
|
|
380
376
|
if cache_saving_backend == "auto":
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import time
|
|
5
|
+
from typing import Callable, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Ticker:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
*,
|
|
12
|
+
get_time_fn: Callable[[], float] = time.perf_counter,
|
|
13
|
+
prev_tick: Optional[float] = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
if prev_tick is None:
|
|
16
|
+
prev_tick = get_time_fn()
|
|
17
|
+
|
|
18
|
+
super().__init__()
|
|
19
|
+
self._get_time_fn = get_time_fn
|
|
20
|
+
self._prev_tick = prev_tick
|
|
21
|
+
|
|
22
|
+
def tick(self) -> float:
|
|
23
|
+
now = self._get_time_fn()
|
|
24
|
+
duration = now - self._prev_tick
|
|
25
|
+
self._prev_tick = now
|
|
26
|
+
return duration
|
|
27
|
+
|
|
28
|
+
def set_prev_tick(self, prev_tick: Optional[float] = None) -> None:
|
|
29
|
+
if prev_tick is None:
|
|
30
|
+
prev_tick = self._get_time_fn()
|
|
31
|
+
self._prev_tick = prev_tick
|
|
@@ -16,6 +16,7 @@ if TYPE_CHECKING or lazy is None:
|
|
|
16
16
|
is_builtin_number,
|
|
17
17
|
is_builtin_obj,
|
|
18
18
|
is_builtin_scalar,
|
|
19
|
+
is_collection_alias,
|
|
19
20
|
is_dataclass_instance,
|
|
20
21
|
is_iterable_bool,
|
|
21
22
|
is_iterable_bytes_or_list,
|
|
@@ -24,7 +25,9 @@ if TYPE_CHECKING or lazy is None:
|
|
|
24
25
|
is_iterable_integral,
|
|
25
26
|
is_iterable_str,
|
|
26
27
|
is_namedtuple_instance,
|
|
28
|
+
is_parameterized,
|
|
27
29
|
is_sequence_str,
|
|
30
|
+
is_special_form,
|
|
28
31
|
is_typed_dict,
|
|
29
32
|
isinstance_generic,
|
|
30
33
|
)
|
|
@@ -47,6 +50,7 @@ if TYPE_CHECKING or lazy is None:
|
|
|
47
50
|
SupportsGetitemIterLen2,
|
|
48
51
|
SupportsGetitemLen,
|
|
49
52
|
SupportsGetitemLen2,
|
|
53
|
+
SupportsIter,
|
|
50
54
|
SupportsIterLen,
|
|
51
55
|
SupportsLen,
|
|
52
56
|
SupportsMatmul,
|
|
@@ -70,6 +74,7 @@ else:
|
|
|
70
74
|
"is_builtin_number",
|
|
71
75
|
"is_builtin_obj",
|
|
72
76
|
"is_builtin_scalar",
|
|
77
|
+
"is_collection_alias",
|
|
73
78
|
"is_dataclass_instance",
|
|
74
79
|
"is_iterable_bool",
|
|
75
80
|
"is_iterable_bytes_or_list",
|
|
@@ -78,7 +83,9 @@ else:
|
|
|
78
83
|
"is_iterable_integral",
|
|
79
84
|
"is_iterable_str",
|
|
80
85
|
"is_namedtuple_instance",
|
|
86
|
+
"is_parameterized",
|
|
81
87
|
"is_sequence_str",
|
|
88
|
+
"is_special_form",
|
|
82
89
|
"is_typed_dict",
|
|
83
90
|
"isinstance_generic",
|
|
84
91
|
],
|
|
@@ -101,6 +108,7 @@ else:
|
|
|
101
108
|
"SupportsGetitemIterLen2",
|
|
102
109
|
"SupportsGetitemLen",
|
|
103
110
|
"SupportsGetitemLen2",
|
|
111
|
+
"SupportsIter",
|
|
104
112
|
"SupportsIterLen",
|
|
105
113
|
"SupportsLen",
|
|
106
114
|
"SupportsMatmul",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import inspect
|
|
5
5
|
import logging
|
|
6
6
|
import sys
|
|
7
|
+
import typing
|
|
7
8
|
from numbers import Integral
|
|
8
9
|
from types import FunctionType, MethodType
|
|
9
10
|
from typing import (
|
|
@@ -395,3 +396,99 @@ def is_typed_dict(x: Any) -> TypeGuard[type]:
|
|
|
395
396
|
return x.__class__.__name__ == "_TypedDictMeta"
|
|
396
397
|
else:
|
|
397
398
|
return hasattr(x, "__orig_bases__") and TypedDict in x.__orig_bases__
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def is_parameterized(x: Any) -> bool:
|
|
402
|
+
"""Returns True if object is a parametrized object like `Iterable[int]`, `Mapping[str, float]`, etc."""
|
|
403
|
+
return get_origin(x) is not None and len(get_args(x)) > 0
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
_COLLECTION_ALIASES = [
|
|
407
|
+
typing.Iterable,
|
|
408
|
+
typing.Iterator,
|
|
409
|
+
typing.Reversible,
|
|
410
|
+
typing.Generator,
|
|
411
|
+
typing.AsyncIterable,
|
|
412
|
+
typing.AsyncIterator,
|
|
413
|
+
typing.AsyncGenerator,
|
|
414
|
+
typing.Collection,
|
|
415
|
+
typing.Container,
|
|
416
|
+
typing.Sized,
|
|
417
|
+
typing.Sequence,
|
|
418
|
+
typing.MutableSequence,
|
|
419
|
+
typing.Set,
|
|
420
|
+
typing.MutableSet,
|
|
421
|
+
typing.Mapping,
|
|
422
|
+
typing.MutableMapping,
|
|
423
|
+
typing.MappingView,
|
|
424
|
+
typing.KeysView,
|
|
425
|
+
typing.ItemsView,
|
|
426
|
+
typing.ValuesView,
|
|
427
|
+
typing.Awaitable,
|
|
428
|
+
typing.Coroutine,
|
|
429
|
+
typing.Callable,
|
|
430
|
+
typing.Hashable,
|
|
431
|
+
typing_extensions.Iterable,
|
|
432
|
+
typing_extensions.Iterator,
|
|
433
|
+
typing_extensions.Reversible,
|
|
434
|
+
typing_extensions.Generator,
|
|
435
|
+
typing_extensions.AsyncIterable,
|
|
436
|
+
typing_extensions.AsyncIterator,
|
|
437
|
+
typing_extensions.AsyncGenerator,
|
|
438
|
+
typing_extensions.Collection,
|
|
439
|
+
typing_extensions.Container,
|
|
440
|
+
typing_extensions.Sized,
|
|
441
|
+
typing_extensions.Sequence,
|
|
442
|
+
typing_extensions.MutableSequence,
|
|
443
|
+
typing_extensions.Set,
|
|
444
|
+
typing_extensions.MutableSet,
|
|
445
|
+
typing_extensions.Mapping,
|
|
446
|
+
typing_extensions.MutableMapping,
|
|
447
|
+
typing_extensions.MappingView,
|
|
448
|
+
typing_extensions.KeysView,
|
|
449
|
+
typing_extensions.ItemsView,
|
|
450
|
+
typing_extensions.ValuesView,
|
|
451
|
+
typing_extensions.Awaitable,
|
|
452
|
+
typing_extensions.Coroutine,
|
|
453
|
+
typing_extensions.Callable,
|
|
454
|
+
typing_extensions.Hashable,
|
|
455
|
+
]
|
|
456
|
+
|
|
457
|
+
_SPECIAL_FORMS = [] + [
|
|
458
|
+
getattr(module, candidate_name, None)
|
|
459
|
+
for module in (typing, typing_extensions)
|
|
460
|
+
for candidate_name in [
|
|
461
|
+
"Any",
|
|
462
|
+
"NoReturn",
|
|
463
|
+
"Union",
|
|
464
|
+
"Optional",
|
|
465
|
+
"Literal",
|
|
466
|
+
"Final",
|
|
467
|
+
"ClassVar",
|
|
468
|
+
"TypeVar",
|
|
469
|
+
"Annotated",
|
|
470
|
+
"Never",
|
|
471
|
+
"Self",
|
|
472
|
+
"Required",
|
|
473
|
+
"NotRequired",
|
|
474
|
+
"TypeGuard",
|
|
475
|
+
"TypeIs",
|
|
476
|
+
"Concatenate",
|
|
477
|
+
"ParamSpec",
|
|
478
|
+
"ParamSpecArgs",
|
|
479
|
+
"ParamSpecKwargs",
|
|
480
|
+
"TypeVarTuple",
|
|
481
|
+
"Unpack",
|
|
482
|
+
]
|
|
483
|
+
if getattr(module, candidate_name, None) is not None
|
|
484
|
+
]
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def is_collection_alias(x: Any) -> bool:
|
|
488
|
+
"""Returns True if object is a non-parameterized collection alias type."""
|
|
489
|
+
return x in _COLLECTION_ALIASES
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def is_special_form(x: Any) -> bool:
|
|
493
|
+
"""Returns True if object is a typing special form like `Any`."""
|
|
494
|
+
return x in _SPECIAL_FORMS
|
|
@@ -5,6 +5,7 @@ from typing import (
|
|
|
5
5
|
Any,
|
|
6
6
|
ClassVar,
|
|
7
7
|
Dict,
|
|
8
|
+
Iterable,
|
|
8
9
|
Iterator,
|
|
9
10
|
List,
|
|
10
11
|
Protocol,
|
|
@@ -41,6 +42,7 @@ T_BuiltinScalar = TypeVar(
|
|
|
41
42
|
)
|
|
42
43
|
|
|
43
44
|
ListOrTuple = Union[List[_T_Item], Tuple[_T_Item, ...]]
|
|
45
|
+
SupportsIter = Iterable
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
@runtime_checkable
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Python library with tools for typing, manipulating collections, and more!
|
|
5
5
|
Author-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
6
6
|
Maintainer-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
@@ -53,7 +53,8 @@ License-File: LICENSE
|
|
|
53
53
|
Requires-Dist: lazy-loader>=0.4
|
|
54
54
|
Requires-Dist: typing-extensions>=4.10.0
|
|
55
55
|
Provides-Extra: dev
|
|
56
|
-
|
|
56
|
+
Provides-Extra: lazy
|
|
57
|
+
Requires-Dist: lazy-loader>=0.4; extra == "lazy"
|
|
57
58
|
|
|
58
59
|
# pythonwrench
|
|
59
60
|
|
|
@@ -2,6 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
setup.py
|
|
5
|
+
docs/requirements.txt
|
|
5
6
|
src/pythonwrench/__init__.py
|
|
6
7
|
src/pythonwrench/__main__.py
|
|
7
8
|
src/pythonwrench/_core.py
|
|
@@ -30,6 +31,7 @@ src/pythonwrench/pickle.py
|
|
|
30
31
|
src/pythonwrench/random.py
|
|
31
32
|
src/pythonwrench/re.py
|
|
32
33
|
src/pythonwrench/semver.py
|
|
34
|
+
src/pythonwrench/time.py
|
|
33
35
|
src/pythonwrench/warnings.py
|
|
34
36
|
src/pythonwrench.egg-info/PKG-INFO
|
|
35
37
|
src/pythonwrench.egg-info/SOURCES.txt
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
import math
|
|
5
5
|
import random
|
|
6
6
|
import unittest
|
|
7
|
+
from typing import Any, Iterable, Mapping, Tuple
|
|
7
8
|
from unittest import TestCase
|
|
8
9
|
|
|
10
|
+
from typing_extensions import Annotated
|
|
11
|
+
|
|
9
12
|
from pythonwrench.checksum import checksum_any
|
|
10
13
|
from pythonwrench.collections import all_ne
|
|
11
14
|
from pythonwrench.math import nextafter
|
|
@@ -74,6 +77,23 @@ class TestChecksum(TestCase):
|
|
|
74
77
|
assert Color.RED == Color.ROUGE
|
|
75
78
|
assert checksum_any(Color.RED) == checksum_any(Color.ROUGE)
|
|
76
79
|
|
|
80
|
+
def test_typing_classes(self) -> None:
|
|
81
|
+
x = [
|
|
82
|
+
Any,
|
|
83
|
+
Iterable,
|
|
84
|
+
Iterable[int],
|
|
85
|
+
Annotated,
|
|
86
|
+
Annotated[int, "annotation"],
|
|
87
|
+
Tuple[int, ...],
|
|
88
|
+
Tuple[int],
|
|
89
|
+
Tuple[int, int],
|
|
90
|
+
tuple,
|
|
91
|
+
Mapping,
|
|
92
|
+
Mapping[int, int],
|
|
93
|
+
]
|
|
94
|
+
csums = [checksum_any(xi) for xi in x]
|
|
95
|
+
assert all_ne(csums), f"{csums=}"
|
|
96
|
+
|
|
77
97
|
|
|
78
98
|
if __name__ == "__main__":
|
|
79
99
|
unittest.main()
|
|
@@ -25,6 +25,7 @@ from pythonwrench.collections import (
|
|
|
25
25
|
recursive_generator,
|
|
26
26
|
reduce_add,
|
|
27
27
|
shuffled,
|
|
28
|
+
sorted_dict,
|
|
28
29
|
unflat_dict_of_dict,
|
|
29
30
|
unflat_list_of_list,
|
|
30
31
|
union_dicts,
|
|
@@ -325,5 +326,18 @@ class TestReduce(TestCase):
|
|
|
325
326
|
assert reduce_add(args, args, start=1)
|
|
326
327
|
|
|
327
328
|
|
|
329
|
+
class TestSortedDict(TestCase):
|
|
330
|
+
def test_example_1(self) -> None:
|
|
331
|
+
x = {"b": 2, "a": 3, "c": 1}
|
|
332
|
+
result = sorted_dict(x)
|
|
333
|
+
assert result == {"a": 3, "b": 2, "c": 1}
|
|
334
|
+
|
|
335
|
+
result = sorted_dict(x, by="value")
|
|
336
|
+
assert result == {"c": 1, "b": 2, "a": 3}
|
|
337
|
+
|
|
338
|
+
result = sorted_dict(x, reverse=True, key=lambda x: x % 3, by="value")
|
|
339
|
+
assert result == {"a": 3, "c": 1, "b": 2}
|
|
340
|
+
|
|
341
|
+
|
|
328
342
|
if __name__ == "__main__":
|
|
329
343
|
unittest.main()
|
|
@@ -22,14 +22,18 @@ from typing import (
|
|
|
22
22
|
)
|
|
23
23
|
from unittest import TestCase
|
|
24
24
|
|
|
25
|
+
import typing_extensions
|
|
25
26
|
from typing_extensions import NotRequired
|
|
26
27
|
|
|
27
28
|
from pythonwrench.typing import (
|
|
28
29
|
NoneType,
|
|
29
30
|
check_args_types,
|
|
31
|
+
is_collection_alias,
|
|
30
32
|
is_dataclass_instance,
|
|
31
33
|
is_iterable_str,
|
|
32
34
|
is_namedtuple_instance,
|
|
35
|
+
is_parameterized,
|
|
36
|
+
is_special_form,
|
|
33
37
|
is_typed_dict,
|
|
34
38
|
isinstance_generic,
|
|
35
39
|
)
|
|
@@ -101,6 +105,25 @@ class TestChecks(TestCase):
|
|
|
101
105
|
assert not is_dataclass_instance(nt1)
|
|
102
106
|
assert not is_dataclass_instance(nt2)
|
|
103
107
|
|
|
108
|
+
def test_is_parameterized(self) -> None:
|
|
109
|
+
assert is_parameterized(Iterable[int])
|
|
110
|
+
assert is_parameterized(Tuple[int, ...])
|
|
111
|
+
assert not is_parameterized(Iterable)
|
|
112
|
+
assert not is_parameterized(Tuple)
|
|
113
|
+
|
|
114
|
+
def test_is_collection_alias(self) -> None:
|
|
115
|
+
assert not is_collection_alias(Iterable[int])
|
|
116
|
+
assert not is_collection_alias(Tuple[int, ...])
|
|
117
|
+
assert is_collection_alias(Iterable)
|
|
118
|
+
assert not is_collection_alias(Tuple)
|
|
119
|
+
assert not is_collection_alias(list)
|
|
120
|
+
|
|
121
|
+
def test_is_special_form(self) -> None:
|
|
122
|
+
assert not is_special_form(Iterable[int])
|
|
123
|
+
assert not is_special_form(Tuple[int, ...])
|
|
124
|
+
assert is_special_form(Any)
|
|
125
|
+
assert is_special_form(typing_extensions.Any)
|
|
126
|
+
|
|
104
127
|
|
|
105
128
|
class TestIsInstanceGuard(TestCase):
|
|
106
129
|
def test_docstring_examples(self) -> None:
|
|
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
|
|
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
|