pythonwrench 0.6.4__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.
- pythonwrench/__init__.py +490 -0
- pythonwrench/__main__.py +7 -0
- pythonwrench/_core.py +192 -0
- pythonwrench/abc.py +30 -0
- pythonwrench/argparse/__init__.py +81 -0
- pythonwrench/argparse/dataclass_.py +284 -0
- pythonwrench/argparse/parsers.py +619 -0
- pythonwrench/cast.py +247 -0
- pythonwrench/checksum.py +427 -0
- pythonwrench/collections/__init__.py +104 -0
- pythonwrench/collections/collections.py +900 -0
- pythonwrench/collections/prop.py +104 -0
- pythonwrench/collections/reducers.py +330 -0
- pythonwrench/concurrent.py +73 -0
- pythonwrench/csv.py +12 -0
- pythonwrench/dataclasses.py +117 -0
- pythonwrench/datetime.py +17 -0
- pythonwrench/difflib.py +39 -0
- pythonwrench/disk_cache.py +615 -0
- pythonwrench/entrypoints/info.py +44 -0
- pythonwrench/entrypoints/safe_rmdir.py +98 -0
- pythonwrench/entrypoints/tree.py +113 -0
- pythonwrench/enum.py +55 -0
- pythonwrench/functools.py +234 -0
- pythonwrench/hashlib.py +95 -0
- pythonwrench/importlib.py +243 -0
- pythonwrench/inspect.py +69 -0
- pythonwrench/json.py +12 -0
- pythonwrench/jsonl.py +12 -0
- pythonwrench/logging.py +252 -0
- pythonwrench/math.py +107 -0
- pythonwrench/os.py +226 -0
- pythonwrench/pickle.py +12 -0
- pythonwrench/random.py +60 -0
- pythonwrench/re.py +139 -0
- pythonwrench/semver.py +406 -0
- pythonwrench/serialization/__init__.py +70 -0
- pythonwrench/serialization/_core.py +70 -0
- pythonwrench/serialization/csv.py +493 -0
- pythonwrench/serialization/json.py +178 -0
- pythonwrench/serialization/jsonl.py +215 -0
- pythonwrench/serialization/pickle.py +186 -0
- pythonwrench/time.py +34 -0
- pythonwrench/typing/__init__.py +125 -0
- pythonwrench/typing/checks.py +551 -0
- pythonwrench/typing/classes.py +251 -0
- pythonwrench/warnings.py +118 -0
- pythonwrench-0.6.4.dist-info/METADATA +242 -0
- pythonwrench-0.6.4.dist-info/RECORD +52 -0
- pythonwrench-0.6.4.dist-info/WHEEL +4 -0
- pythonwrench-0.6.4.dist-info/entry_points.txt +10 -0
- pythonwrench-0.6.4.dist-info/licenses/LICENSE +21 -0
pythonwrench/cast.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from argparse import Namespace
|
|
5
|
+
from collections import Counter
|
|
6
|
+
from datetime import date
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from functools import partial
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from re import Pattern
|
|
11
|
+
from typing import (
|
|
12
|
+
Any,
|
|
13
|
+
Callable,
|
|
14
|
+
Dict,
|
|
15
|
+
Hashable,
|
|
16
|
+
Iterable,
|
|
17
|
+
Mapping,
|
|
18
|
+
Optional,
|
|
19
|
+
TypeVar,
|
|
20
|
+
overload,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from pythonwrench._core import ClassOrTuple, Predicate, _FunctionRegistry
|
|
24
|
+
from pythonwrench.functools import identity
|
|
25
|
+
from pythonwrench.typing import (
|
|
26
|
+
DataclassInstance,
|
|
27
|
+
NamedTupleInstance,
|
|
28
|
+
T_BuiltinScalar,
|
|
29
|
+
is_builtin_scalar,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = ["register_as_builtin_fn", "as_builtin"]
|
|
33
|
+
|
|
34
|
+
T = TypeVar("T")
|
|
35
|
+
K = TypeVar("K", bound=Hashable)
|
|
36
|
+
V = TypeVar("V")
|
|
37
|
+
|
|
38
|
+
_AS_BUILTIN_REGISTRY = _FunctionRegistry[Any]()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@overload
|
|
42
|
+
def register_as_builtin_fn(
|
|
43
|
+
class_or_tuple: ClassOrTuple,
|
|
44
|
+
*,
|
|
45
|
+
custom_predicate: None = None,
|
|
46
|
+
priority: int = 0,
|
|
47
|
+
) -> Callable:
|
|
48
|
+
"""Perform the register as builtin fn operation."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@overload
|
|
53
|
+
def register_as_builtin_fn(
|
|
54
|
+
class_or_tuple: None = None,
|
|
55
|
+
*,
|
|
56
|
+
custom_predicate: Predicate,
|
|
57
|
+
priority: int = 0,
|
|
58
|
+
) -> Callable:
|
|
59
|
+
"""Perform the register as builtin fn operation."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def register_as_builtin_fn(
|
|
64
|
+
class_or_tuple: Optional[ClassOrTuple] = None,
|
|
65
|
+
*,
|
|
66
|
+
custom_predicate: Optional[Predicate] = None,
|
|
67
|
+
priority: int = 0,
|
|
68
|
+
) -> Callable:
|
|
69
|
+
"""Decorator to add an as_builtin function.
|
|
70
|
+
|
|
71
|
+
Example
|
|
72
|
+
-------
|
|
73
|
+
>>> import numpy as np
|
|
74
|
+
>>> @register_as_builtin_fn(np.ndarray)
|
|
75
|
+
>>> def my_checksum_for_numpy(x: np.ndarray):
|
|
76
|
+
>>> return x.tolist()
|
|
77
|
+
>>> pw.as_builtin([np.array([1, 2]), [3, 4]])
|
|
78
|
+
... [[1, 2], [3, 4]]
|
|
79
|
+
"""
|
|
80
|
+
return _AS_BUILTIN_REGISTRY.register_decorator(
|
|
81
|
+
class_or_tuple,
|
|
82
|
+
custom_predicate=custom_predicate,
|
|
83
|
+
priority=priority,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
_AS_BUILTIN_REGISTRY.register(
|
|
88
|
+
identity,
|
|
89
|
+
custom_predicate=partial(is_builtin_scalar, strict=True),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@register_as_builtin_fn(Counter)
|
|
94
|
+
def _counter_to_builtin(x: Counter, **kwargs) -> Dict[Any, int]:
|
|
95
|
+
"""Perform the counter to builtin operation."""
|
|
96
|
+
return dict(x)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@register_as_builtin_fn(date)
|
|
100
|
+
def _date_to_builtin(x: date, **kwargs) -> str:
|
|
101
|
+
"""Perform the date to builtin operation."""
|
|
102
|
+
return str(x)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@register_as_builtin_fn(Path)
|
|
106
|
+
def _path_to_builtin(x: Path, **kwargs) -> str:
|
|
107
|
+
"""Perform the path to builtin operation."""
|
|
108
|
+
return str(x)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@register_as_builtin_fn(Enum)
|
|
112
|
+
def _enum_to_builtin(x: Enum, **kwargs) -> str:
|
|
113
|
+
"""Perform the enum to builtin operation."""
|
|
114
|
+
return x.name
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@register_as_builtin_fn(Pattern)
|
|
118
|
+
def _pattern_to_builtin(x: Pattern, **kwargs) -> str:
|
|
119
|
+
"""Perform the pattern to builtin operation."""
|
|
120
|
+
return x.pattern
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@register_as_builtin_fn(Namespace)
|
|
124
|
+
def _namespace_to_builtin(x: Namespace, **kwargs) -> Any:
|
|
125
|
+
"""Perform the namespace to builtin operation."""
|
|
126
|
+
return as_builtin(x.__dict__, **kwargs)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@register_as_builtin_fn(DataclassInstance)
|
|
130
|
+
def _dataclass_to_builtin(x: DataclassInstance, **kwargs) -> Any:
|
|
131
|
+
# IMPORTANT note : we do not use dataclasses.asdict() because it also converts attributes like Counter, but not do dicts
|
|
132
|
+
"""Perform the dataclass to builtin operation."""
|
|
133
|
+
field_names = x.__dataclass_fields__.keys()
|
|
134
|
+
xdict = {name: as_builtin(getattr(x, name), **kwargs) for name in field_names}
|
|
135
|
+
return xdict
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@register_as_builtin_fn(NamedTupleInstance)
|
|
139
|
+
def _namedtuple_to_builtin(x: NamedTupleInstance, **kwargs) -> Any:
|
|
140
|
+
"""Perform the namedtuple to builtin operation."""
|
|
141
|
+
return as_builtin(x._asdict(), **kwargs)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@register_as_builtin_fn(Mapping, priority=-100)
|
|
145
|
+
def _mapping_to_builtin(x: Mapping, **kwargs) -> Any:
|
|
146
|
+
"""Perform the mapping to builtin operation."""
|
|
147
|
+
return {as_builtin(k, **kwargs): as_builtin(v, **kwargs) for k, v in x.items()}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@register_as_builtin_fn(Iterable, priority=-200)
|
|
151
|
+
def _iterable_to_builtin(x: Iterable, **kwargs) -> Any:
|
|
152
|
+
"""Perform the iterable to builtin operation."""
|
|
153
|
+
return [as_builtin(xi, **kwargs) for xi in x]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@overload
|
|
157
|
+
def as_builtin(x: Counter, **kwargs) -> Dict[Any, int]:
|
|
158
|
+
"""Perform the as builtin operation."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@overload
|
|
163
|
+
def as_builtin(x: date, **kwargs) -> str:
|
|
164
|
+
"""Perform the as builtin operation."""
|
|
165
|
+
...
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@overload
|
|
169
|
+
def as_builtin(x: Enum, **kwargs) -> str:
|
|
170
|
+
"""Perform the as builtin operation."""
|
|
171
|
+
...
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@overload
|
|
175
|
+
def as_builtin(x: Path, **kwargs) -> str:
|
|
176
|
+
"""Perform the as builtin operation."""
|
|
177
|
+
...
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@overload
|
|
181
|
+
def as_builtin(x: Pattern, **kwargs) -> str:
|
|
182
|
+
"""Perform the as builtin operation."""
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@overload
|
|
187
|
+
def as_builtin(x: Namespace, **kwargs) -> Dict[str, Any]:
|
|
188
|
+
"""Perform the as builtin operation."""
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@overload
|
|
193
|
+
def as_builtin(x: Mapping[K, V], **kwargs) -> Dict[K, V]:
|
|
194
|
+
"""Perform the as builtin operation."""
|
|
195
|
+
...
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@overload
|
|
199
|
+
def as_builtin(x: DataclassInstance, **kwargs) -> Dict[str, Any]:
|
|
200
|
+
"""Perform the as builtin operation."""
|
|
201
|
+
...
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@overload
|
|
205
|
+
def as_builtin(x: NamedTupleInstance, **kwargs) -> Dict[str, Any]:
|
|
206
|
+
"""Perform the as builtin operation."""
|
|
207
|
+
...
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@overload
|
|
211
|
+
def as_builtin(x: T_BuiltinScalar, **kwargs) -> T_BuiltinScalar:
|
|
212
|
+
"""Perform the as builtin operation."""
|
|
213
|
+
...
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@overload
|
|
217
|
+
def as_builtin(x: Any, **kwargs) -> Any:
|
|
218
|
+
"""Perform the as builtin operation."""
|
|
219
|
+
...
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def as_builtin(x: Any, **kwargs) -> Any:
|
|
223
|
+
"""Convert an object to a sanitized python builtin equivalent recursively.
|
|
224
|
+
|
|
225
|
+
This function can be used to sanitize data before saving to a JSON, YAML or CSV file.
|
|
226
|
+
|
|
227
|
+
Additional objects to convert can be added dynamically with `pythonwrench.register_as_builtin_fn` function decorator.
|
|
228
|
+
|
|
229
|
+
Here is the list of default objects converted to built-in:
|
|
230
|
+
- tuple -> list
|
|
231
|
+
- collections.Counter -> dict
|
|
232
|
+
- datetime.date -> str
|
|
233
|
+
- argparse.Namespace -> dict
|
|
234
|
+
- re.Pattern -> str
|
|
235
|
+
- pathlib.Path -> str
|
|
236
|
+
- enum.Enum -> str
|
|
237
|
+
- Mapping -> dict
|
|
238
|
+
- Iterable -> list
|
|
239
|
+
- Dataclass -> dict
|
|
240
|
+
- NamedTuple -> dict
|
|
241
|
+
|
|
242
|
+
Note: By default, tuple objects are converted to list.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
x: Object to convert to built-in equivalent.
|
|
246
|
+
"""
|
|
247
|
+
return _AS_BUILTIN_REGISTRY.apply(x, **kwargs)
|
pythonwrench/checksum.py
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import functools
|
|
5
|
+
import re
|
|
6
|
+
import struct
|
|
7
|
+
import zlib
|
|
8
|
+
from dataclasses import asdict
|
|
9
|
+
from datetime import date, datetime
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from functools import lru_cache
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from types import FunctionType, MethodType
|
|
14
|
+
from typing import (
|
|
15
|
+
Any,
|
|
16
|
+
Callable,
|
|
17
|
+
Dict,
|
|
18
|
+
Generator,
|
|
19
|
+
Iterable,
|
|
20
|
+
Mapping,
|
|
21
|
+
Optional,
|
|
22
|
+
TypeVar,
|
|
23
|
+
Union,
|
|
24
|
+
get_args,
|
|
25
|
+
get_origin,
|
|
26
|
+
overload,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from pythonwrench._core import ClassOrTuple, Predicate, _FunctionRegistry
|
|
30
|
+
from pythonwrench.functools import function_alias
|
|
31
|
+
from pythonwrench.inspect import get_fullname
|
|
32
|
+
from pythonwrench.typing import (
|
|
33
|
+
DataclassInstance,
|
|
34
|
+
EllipsisType,
|
|
35
|
+
NamedTupleInstance,
|
|
36
|
+
NoneType,
|
|
37
|
+
is_collection_alias,
|
|
38
|
+
is_parameterized,
|
|
39
|
+
is_special_form,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
T = TypeVar("T")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_CHECKSUM_REGISTRY = _FunctionRegistry[int]()
|
|
46
|
+
_CHECKSUM_PROTOCOLS = False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@overload
|
|
50
|
+
def register_checksum_fn(
|
|
51
|
+
class_or_tuple: ClassOrTuple,
|
|
52
|
+
*,
|
|
53
|
+
custom_predicate: None = None,
|
|
54
|
+
priority: int = 0,
|
|
55
|
+
) -> Callable:
|
|
56
|
+
"""Perform the register checksum fn operation."""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@overload
|
|
61
|
+
def register_checksum_fn(
|
|
62
|
+
class_or_tuple: None = None,
|
|
63
|
+
*,
|
|
64
|
+
custom_predicate: Predicate,
|
|
65
|
+
priority: int = 0,
|
|
66
|
+
) -> Callable:
|
|
67
|
+
"""Perform the register checksum fn operation."""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def register_checksum_fn(
|
|
72
|
+
class_or_tuple: Optional[ClassOrTuple] = None,
|
|
73
|
+
*,
|
|
74
|
+
custom_predicate: Optional[Predicate] = None,
|
|
75
|
+
priority: int = 0,
|
|
76
|
+
) -> Callable:
|
|
77
|
+
"""Decorator to add a checksum function.
|
|
78
|
+
|
|
79
|
+
Example
|
|
80
|
+
-------
|
|
81
|
+
>>> import numpy as np
|
|
82
|
+
>>> @register_checksum_fn(np.ndarray)
|
|
83
|
+
>>> def my_checksum_for_numpy(x: np.ndarray):
|
|
84
|
+
>>> return int(x.sum())
|
|
85
|
+
>>> pw.checksum_any(np.array([1, 2])) # calls my_checksum_for_numpy internally, even if array in nested inside a list, dict, etc.
|
|
86
|
+
"""
|
|
87
|
+
return _CHECKSUM_REGISTRY.register_decorator(
|
|
88
|
+
class_or_tuple,
|
|
89
|
+
custom_predicate=custom_predicate,
|
|
90
|
+
priority=priority,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def checksum_any(
|
|
95
|
+
x: Any,
|
|
96
|
+
*,
|
|
97
|
+
isinstance_fn: Callable[[Any, Union[type, tuple]], bool] = isinstance,
|
|
98
|
+
**kwargs,
|
|
99
|
+
) -> int:
|
|
100
|
+
"""Compute checksum integer value from an arbitrary object.
|
|
101
|
+
|
|
102
|
+
Supports most builtin types. Checksum can be used to compare objects.
|
|
103
|
+
Not meant for security/cryptography.
|
|
104
|
+
"""
|
|
105
|
+
return _CHECKSUM_REGISTRY.apply(x, isinstance_fn=isinstance_fn, **kwargs)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@function_alias(checksum_any)
|
|
109
|
+
def checksum_object(*args, **kwargs):
|
|
110
|
+
"""Return a checksum for object."""
|
|
111
|
+
...
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# Terminate functions
|
|
115
|
+
@register_checksum_fn(bool)
|
|
116
|
+
def checksum_bool(x: bool, **kwargs) -> int:
|
|
117
|
+
"""Return a checksum for bool."""
|
|
118
|
+
xint = int(x)
|
|
119
|
+
return _terminate_checksum(
|
|
120
|
+
xint,
|
|
121
|
+
get_fullname(x),
|
|
122
|
+
**kwargs,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@register_checksum_fn(float)
|
|
127
|
+
def checksum_float(x: float, **kwargs) -> int:
|
|
128
|
+
"""Return a checksum for float."""
|
|
129
|
+
xint = __interpret_float_as_int(x)
|
|
130
|
+
return _terminate_checksum(
|
|
131
|
+
xint,
|
|
132
|
+
get_fullname(x),
|
|
133
|
+
**kwargs,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@register_checksum_fn(int)
|
|
138
|
+
def checksum_int(x: int, **kwargs) -> int:
|
|
139
|
+
"""Return a checksum for int."""
|
|
140
|
+
xint = x
|
|
141
|
+
return _terminate_checksum(
|
|
142
|
+
xint,
|
|
143
|
+
get_fullname(x),
|
|
144
|
+
**kwargs,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# Intermediate functions
|
|
149
|
+
@register_checksum_fn(bytearray)
|
|
150
|
+
def checksum_bytearray(x: bytearray, **kwargs) -> int:
|
|
151
|
+
"""Return a checksum for bytearray."""
|
|
152
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
153
|
+
return _checksum_bytes_bytearray(x, **kwargs)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@register_checksum_fn(bytes)
|
|
157
|
+
def checksum_bytes(x: bytes, **kwargs) -> int:
|
|
158
|
+
"""Return a checksum for bytes."""
|
|
159
|
+
return _checksum_bytes_bytearray(x, **kwargs)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@register_checksum_fn(complex)
|
|
163
|
+
def checksum_complex(x: complex, **kwargs) -> int:
|
|
164
|
+
"""Return a checksum for complex."""
|
|
165
|
+
kwargs["accumulator"] = kwargs.get("accumulator", 0) + _cached_checksum_str(
|
|
166
|
+
get_fullname(x)
|
|
167
|
+
)
|
|
168
|
+
return checksum_list_tuple([x.real, x.imag], **kwargs)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@register_checksum_fn(FunctionType)
|
|
172
|
+
def checksum_function(x: FunctionType, **kwargs) -> int:
|
|
173
|
+
"""Return a checksum for function."""
|
|
174
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
175
|
+
return checksum_str(x.__qualname__, **kwargs)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@register_checksum_fn(NoneType)
|
|
179
|
+
def checksum_none(x: None, **kwargs) -> int:
|
|
180
|
+
"""Return a checksum for none."""
|
|
181
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
182
|
+
return checksum_type(x.__class__, **kwargs)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@register_checksum_fn(EllipsisType)
|
|
186
|
+
def checksum_ellipsis(x: None, **kwargs) -> int:
|
|
187
|
+
"""Return a checksum for ellipsis."""
|
|
188
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
189
|
+
return checksum_type(x.__class__, **kwargs)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@register_checksum_fn(str)
|
|
193
|
+
def checksum_str(x: str, **kwargs) -> int:
|
|
194
|
+
"""Return a checksum for str."""
|
|
195
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
196
|
+
return checksum_bytes(x.encode(), **kwargs)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@register_checksum_fn(type)
|
|
200
|
+
def checksum_type(x: type, **kwargs) -> int:
|
|
201
|
+
"""Return a checksum for type."""
|
|
202
|
+
return checksum_str(x.__qualname__, **kwargs)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# Recursive functions
|
|
206
|
+
@register_checksum_fn(DataclassInstance)
|
|
207
|
+
def checksum_dataclass(x: DataclassInstance, **kwargs) -> int:
|
|
208
|
+
"""Return a checksum for dataclass."""
|
|
209
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
210
|
+
return checksum_dict(asdict(x), **kwargs)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@register_checksum_fn(datetime)
|
|
214
|
+
def checksum_datetime(x: datetime, **kwargs) -> int:
|
|
215
|
+
"""Return a checksum for datetime."""
|
|
216
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
217
|
+
return _checksum_iterable(
|
|
218
|
+
[
|
|
219
|
+
x.year,
|
|
220
|
+
x.month,
|
|
221
|
+
x.day,
|
|
222
|
+
x.hour,
|
|
223
|
+
x.minute,
|
|
224
|
+
x.second,
|
|
225
|
+
x.microsecond,
|
|
226
|
+
x.tzinfo,
|
|
227
|
+
x.fold,
|
|
228
|
+
],
|
|
229
|
+
**kwargs,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@register_checksum_fn(date)
|
|
234
|
+
def checksum_date(x: date, **kwargs) -> int:
|
|
235
|
+
"""Return a checksum for date."""
|
|
236
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
237
|
+
return _checksum_iterable([x.year, x.month, x.day], **kwargs)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@register_checksum_fn(dict)
|
|
241
|
+
def checksum_dict(x: dict, **kwargs) -> int:
|
|
242
|
+
"""Return a checksum for dict."""
|
|
243
|
+
return _checksum_mapping(x, **kwargs)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@register_checksum_fn(Enum)
|
|
247
|
+
def checksum_enum(x: Enum, **kwargs) -> int:
|
|
248
|
+
"""Return a checksum for enum."""
|
|
249
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
250
|
+
return _checksum_iterable((x.name, x.value), **kwargs)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@register_checksum_fn((list, tuple))
|
|
254
|
+
def checksum_list_tuple(x: Union[list, tuple], **kwargs) -> int:
|
|
255
|
+
"""Return a checksum for list tuple."""
|
|
256
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
257
|
+
return _checksum_iterable(x, **kwargs)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@register_checksum_fn((set, frozenset))
|
|
261
|
+
def checksum_set(x: Union[set, frozenset], **kwargs) -> int:
|
|
262
|
+
"""Return a checksum for set."""
|
|
263
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
264
|
+
# Simply use sum here, order does not matter
|
|
265
|
+
csum = sum(checksum_any(xi, **kwargs) for xi in x)
|
|
266
|
+
return csum
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@register_checksum_fn(range)
|
|
270
|
+
def checksum_range(x: range, **kwargs) -> int:
|
|
271
|
+
"""Return a checksum for range."""
|
|
272
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
273
|
+
return _checksum_iterable([x.start, x.stop, x.step], **kwargs)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@register_checksum_fn(Generator, priority=100)
|
|
277
|
+
def checksum_generator(x: Generator, **kwargs) -> int:
|
|
278
|
+
"""Return a checksum for generator."""
|
|
279
|
+
msg = f"Cannot compute checksum for the generator object {type(x)=}, it will be consumed."
|
|
280
|
+
raise RuntimeError(msg)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@register_checksum_fn(MethodType)
|
|
284
|
+
def checksum_method(x: MethodType, **kwargs) -> int:
|
|
285
|
+
"""Return a checksum for method."""
|
|
286
|
+
fn = getattr(x.__self__, x.__name__)
|
|
287
|
+
checksums = [
|
|
288
|
+
checksum_any(x.__self__, **kwargs), # type: ignore
|
|
289
|
+
checksum_function(fn, **kwargs),
|
|
290
|
+
]
|
|
291
|
+
return checksum_list_tuple(checksums, **kwargs)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@register_checksum_fn(NamedTupleInstance)
|
|
295
|
+
def checksum_namedtuple(x: NamedTupleInstance, **kwargs) -> int:
|
|
296
|
+
"""Return a checksum for namedtuple."""
|
|
297
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
298
|
+
return checksum_dict(x._asdict(), **kwargs)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@register_checksum_fn(functools.partial)
|
|
302
|
+
def checksum_partial(x: functools.partial, **kwargs) -> int:
|
|
303
|
+
"""Return a checksum for partial."""
|
|
304
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
305
|
+
return checksum_list_tuple((x.func, x.args, x.keywords), **kwargs)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@register_checksum_fn(re.Pattern)
|
|
309
|
+
def checksum_pattern(x: re.Pattern, **kwargs) -> int:
|
|
310
|
+
"""Return a checksum for pattern."""
|
|
311
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
312
|
+
return checksum_str(str(x), **kwargs)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@register_checksum_fn(Path)
|
|
316
|
+
def checksum_path(x: Path, *, resolve_path: bool = False, **kwargs) -> int:
|
|
317
|
+
"""Return a checksum for path."""
|
|
318
|
+
kwargs["resolve_path"] = resolve_path
|
|
319
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
320
|
+
if isinstance(resolve_path, bool) and resolve_path:
|
|
321
|
+
x = x.expanduser().resolve()
|
|
322
|
+
return checksum_str(str(x), **kwargs)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@register_checksum_fn(slice)
|
|
326
|
+
def checksum_slice(x: slice, **kwargs) -> int:
|
|
327
|
+
"""Return a checksum for slice."""
|
|
328
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
329
|
+
return checksum_list_tuple((x.start, x.stop, x.step), **kwargs)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@register_checksum_fn(custom_predicate=is_parameterized)
|
|
333
|
+
def checksum_parametrized(x: Any, **kwargs) -> int:
|
|
334
|
+
"""Return a checksum for parametrized."""
|
|
335
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
336
|
+
return checksum_list_tuple((get_origin(x),) + get_args(x), **kwargs)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@register_checksum_fn(custom_predicate=is_collection_alias)
|
|
340
|
+
def checksum_collection_alias(x: Any, **kwargs) -> int:
|
|
341
|
+
"""Return a checksum for collection alias."""
|
|
342
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
343
|
+
return checksum_str(x._name, **kwargs)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@register_checksum_fn(custom_predicate=is_special_form)
|
|
347
|
+
def checksum_special_form(x: Any, **kwargs) -> int:
|
|
348
|
+
"""Return a checksum for special form."""
|
|
349
|
+
kwargs = _add_type_checksum_to_accumulator(x, kwargs)
|
|
350
|
+
|
|
351
|
+
if hasattr(x, "_name"):
|
|
352
|
+
name = x._name
|
|
353
|
+
elif hasattr(x, "__name__"):
|
|
354
|
+
name = x.__name__
|
|
355
|
+
else:
|
|
356
|
+
msg = f"Unsupported argument {x=} in checksum_special_form."
|
|
357
|
+
raise ValueError(msg)
|
|
358
|
+
|
|
359
|
+
return checksum_str(name, **kwargs)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if _CHECKSUM_PROTOCOLS:
|
|
363
|
+
|
|
364
|
+
@register_checksum_fn(Mapping, priority=-100)
|
|
365
|
+
def checksum_mapping(x: Mapping, **kwargs) -> int:
|
|
366
|
+
"""Return a checksum for mapping."""
|
|
367
|
+
return _checksum_mapping(x, **kwargs)
|
|
368
|
+
|
|
369
|
+
@register_checksum_fn(Iterable, priority=-200)
|
|
370
|
+
def checksum_iterable(x: Iterable, **kwargs) -> int:
|
|
371
|
+
"""Return a checksum for iterable."""
|
|
372
|
+
return _checksum_iterable(x, **kwargs)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# Private functions
|
|
376
|
+
def _checksum_bytes_bytearray(x: Union[bytes, bytearray], **kwargs) -> int:
|
|
377
|
+
"""Perform the checksum bytes bytearray operation."""
|
|
378
|
+
xint = zlib.crc32(x) % (1 << 32)
|
|
379
|
+
return _terminate_checksum(
|
|
380
|
+
xint,
|
|
381
|
+
get_fullname(x),
|
|
382
|
+
**kwargs,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _checksum_iterable(x: Iterable, **kwargs) -> int:
|
|
387
|
+
"""Perform the checksum iterable operation."""
|
|
388
|
+
accumulator = kwargs.pop("accumulator", 0) + _cached_checksum_str(get_fullname(x))
|
|
389
|
+
csum = sum(
|
|
390
|
+
checksum_any(xi, accumulator=accumulator + (i + 1), **kwargs) * (i + 1)
|
|
391
|
+
for i, xi in enumerate(x)
|
|
392
|
+
)
|
|
393
|
+
return csum + accumulator
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _checksum_mapping(x: Mapping, **kwargs) -> int:
|
|
397
|
+
"""Perform the checksum mapping operation."""
|
|
398
|
+
kwargs["accumulator"] = kwargs.get("accumulator", 0) + _cached_checksum_str(
|
|
399
|
+
get_fullname(x)
|
|
400
|
+
)
|
|
401
|
+
return _checksum_iterable(x.items(), **kwargs)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _terminate_checksum(x: int, fullname: str, **kwargs) -> int:
|
|
405
|
+
"""Returns checksum for final value + name + accumulator."""
|
|
406
|
+
return x + _cached_checksum_str(fullname) + kwargs.get("accumulator", 0)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def _add_type_checksum_to_accumulator(x: Any, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
410
|
+
"""Perform the add type checksum to accumulator operation."""
|
|
411
|
+
kwargs["accumulator"] = kwargs.get("accumulator", 0) + _cached_checksum_str(
|
|
412
|
+
get_fullname(x)
|
|
413
|
+
)
|
|
414
|
+
return kwargs
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@lru_cache(maxsize=None)
|
|
418
|
+
def _cached_checksum_str(x: str) -> int:
|
|
419
|
+
"""Perform the cached checksum str operation."""
|
|
420
|
+
return zlib.crc32(x.encode()) % (1 << 32)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def __interpret_float_as_int(x: float) -> int:
|
|
424
|
+
"""Perform the interpret float as int operation."""
|
|
425
|
+
xbytes = struct.pack(">d", x)
|
|
426
|
+
xint = struct.unpack(">q", xbytes)[0]
|
|
427
|
+
return xint
|