pythonwrench 0.4.7__tar.gz → 0.4.9__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.4.7/src/pythonwrench.egg-info → pythonwrench-0.4.9}/PKG-INFO +2 -3
- pythonwrench-0.4.9/docs/requirements.txt +4 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/__init__.py +6 -1
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/cast.py +1 -1
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/checksum.py +6 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/disk_cache.py +86 -20
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/semver.py +68 -58
- {pythonwrench-0.4.7 → pythonwrench-0.4.9/src/pythonwrench.egg-info}/PKG-INFO +2 -3
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/SOURCES.txt +1 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_checksum.py +17 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_semver.py +30 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/LICENSE +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/README.md +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/pyproject.toml +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/setup.cfg +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/setup.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/_core.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/argparse.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/__init__.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/collections.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/reducers.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/concurrent.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/dataclasses.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/entries.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/functools.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/jsonl.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/logging.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/os.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/pickle.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/re.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/typing/__init__.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/typing/checks.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/typing/classes.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/warnings.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/entry_points.txt +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/requires.txt +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/top_level.txt +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_abc.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_argparse.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_cast.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_collections.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_csv.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_difflib.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_disk_cache.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_entries.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_enum.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_functools.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_importlib.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_inspect.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_json.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_logging.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_math.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_os.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_random.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_readme.py +0 -0
- {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_typing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.9
|
|
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>
|
|
@@ -52,7 +52,6 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
License-File: LICENSE
|
|
53
53
|
Requires-Dist: typing-extensions>=4.10.0
|
|
54
54
|
Provides-Extra: dev
|
|
55
|
-
Dynamic: license-file
|
|
56
55
|
|
|
57
56
|
# pythonwrench
|
|
58
57
|
|
|
@@ -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.4.
|
|
12
|
+
__version__ = "0.4.9"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Re-import for language servers
|
|
@@ -165,6 +165,7 @@ from .typing import (
|
|
|
165
165
|
BuiltinScalar,
|
|
166
166
|
DataclassInstance,
|
|
167
167
|
EllipsisType,
|
|
168
|
+
ListOrTuple,
|
|
168
169
|
NamedTupleInstance,
|
|
169
170
|
NoneType,
|
|
170
171
|
SupportsAdd,
|
|
@@ -172,10 +173,14 @@ from .typing import (
|
|
|
172
173
|
SupportsBool,
|
|
173
174
|
SupportsDiv,
|
|
174
175
|
SupportsGetitem,
|
|
176
|
+
SupportsGetitem2,
|
|
175
177
|
SupportsGetitemIterLen,
|
|
178
|
+
SupportsGetitemIterLen2,
|
|
176
179
|
SupportsGetitemLen,
|
|
180
|
+
SupportsGetitemLen2,
|
|
177
181
|
SupportsIterLen,
|
|
178
182
|
SupportsLen,
|
|
183
|
+
SupportsMatmul,
|
|
179
184
|
SupportsMul,
|
|
180
185
|
SupportsOr,
|
|
181
186
|
T_BuiltinNumber,
|
|
@@ -182,7 +182,7 @@ def as_builtin(x: Any, **kwargs) -> Any: ...
|
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
def as_builtin(x: Any, **kwargs) -> Any:
|
|
185
|
-
"""Convert an object to a sanitized python builtin equivalent.
|
|
185
|
+
"""Convert an object to a sanitized python builtin equivalent recursively.
|
|
186
186
|
|
|
187
187
|
This function can be used to sanitize data before saving to a JSON, YAML or CSV file.
|
|
188
188
|
|
|
@@ -6,6 +6,7 @@ import re
|
|
|
6
6
|
import struct
|
|
7
7
|
import zlib
|
|
8
8
|
from dataclasses import asdict
|
|
9
|
+
from enum import Enum
|
|
9
10
|
from functools import lru_cache
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from types import FunctionType, MethodType
|
|
@@ -191,6 +192,11 @@ def checksum_dict(x: dict, **kwargs) -> int:
|
|
|
191
192
|
return _checksum_mapping(x, **kwargs)
|
|
192
193
|
|
|
193
194
|
|
|
195
|
+
@register_checksum_fn(Enum)
|
|
196
|
+
def checksum_enum(x: Enum, **kwargs) -> int:
|
|
197
|
+
return _checksum_iterable((x.__class__, x.name, x.value), **kwargs)
|
|
198
|
+
|
|
199
|
+
|
|
194
200
|
@register_checksum_fn((list, tuple))
|
|
195
201
|
def checksum_list_tuple(x: Union[list, tuple], **kwargs) -> int:
|
|
196
202
|
return _checksum_iterable(x, **kwargs)
|
|
@@ -12,6 +12,7 @@ from typing import (
|
|
|
12
12
|
Any,
|
|
13
13
|
Callable,
|
|
14
14
|
Dict,
|
|
15
|
+
Iterable,
|
|
15
16
|
Literal,
|
|
16
17
|
Optional,
|
|
17
18
|
Tuple,
|
|
@@ -37,6 +38,8 @@ ChecksumFn = Callable[[Tuple[Callable[P, T], Tuple, Dict[str, Any]]], int]
|
|
|
37
38
|
SavingBackend = Literal["csv", "json", "pickle"]
|
|
38
39
|
StoreMode = Literal["outputs_only", "outputs_metadata", "outputs_metadata_inputs"]
|
|
39
40
|
|
|
41
|
+
_DEFAULT_CACHE_STORE_MODE: StoreMode = "outputs_metadata"
|
|
42
|
+
|
|
40
43
|
|
|
41
44
|
class _CacheMeta(TypedDict):
|
|
42
45
|
datetime: str
|
|
@@ -63,10 +66,29 @@ def disk_cache_decorator(
|
|
|
63
66
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
64
67
|
cache_saving_backend: Literal["custom"],
|
|
65
68
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
69
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
66
70
|
cache_dump_fn: Callable[[Any, Path], Any],
|
|
67
71
|
cache_load_fn: Callable[[Path], Any],
|
|
68
72
|
cache_enable: bool = True,
|
|
69
|
-
cache_store_mode: StoreMode
|
|
73
|
+
cache_store_mode: StoreMode,
|
|
74
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
def disk_cache_decorator(
|
|
79
|
+
fn: None = None,
|
|
80
|
+
*,
|
|
81
|
+
cache_dpath: Union[str, Path, None] = None,
|
|
82
|
+
cache_force: bool = False,
|
|
83
|
+
cache_verbose: int = 0,
|
|
84
|
+
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
85
|
+
cache_saving_backend: SavingBackend,
|
|
86
|
+
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
87
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
88
|
+
cache_dump_fn: None = None,
|
|
89
|
+
cache_load_fn: None = None,
|
|
90
|
+
cache_enable: bool = True,
|
|
91
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
70
92
|
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
71
93
|
|
|
72
94
|
|
|
@@ -80,10 +102,11 @@ def disk_cache_decorator(
|
|
|
80
102
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
81
103
|
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
82
104
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
105
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
83
106
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
84
107
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
85
108
|
cache_enable: bool = True,
|
|
86
|
-
cache_store_mode: StoreMode =
|
|
109
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
87
110
|
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
88
111
|
|
|
89
112
|
|
|
@@ -97,10 +120,11 @@ def disk_cache_decorator(
|
|
|
97
120
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
98
121
|
cache_saving_backend: Literal["custom"],
|
|
99
122
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
123
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
100
124
|
cache_dump_fn: Callable[[Any, Path], Any],
|
|
101
125
|
cache_load_fn: Callable[[Path], Any],
|
|
102
126
|
cache_enable: bool = True,
|
|
103
|
-
cache_store_mode: StoreMode =
|
|
127
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
104
128
|
) -> Callable[P, T]: ...
|
|
105
129
|
|
|
106
130
|
|
|
@@ -114,10 +138,11 @@ def disk_cache_decorator(
|
|
|
114
138
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
115
139
|
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
116
140
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
141
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
117
142
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
118
143
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
119
144
|
cache_enable: bool = True,
|
|
120
|
-
cache_store_mode: StoreMode =
|
|
145
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
121
146
|
) -> Callable[P, T]: ...
|
|
122
147
|
|
|
123
148
|
|
|
@@ -130,10 +155,11 @@ def disk_cache_decorator(
|
|
|
130
155
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
131
156
|
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
132
157
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
158
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
133
159
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
134
160
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
135
161
|
cache_enable: bool = True,
|
|
136
|
-
cache_store_mode: StoreMode =
|
|
162
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
137
163
|
) -> Callable:
|
|
138
164
|
"""Decorator to store function output in a cache file.
|
|
139
165
|
|
|
@@ -169,6 +195,7 @@ def disk_cache_decorator(
|
|
|
169
195
|
cache_checksum_fn=cache_checksum_fn,
|
|
170
196
|
cache_saving_backend=cache_saving_backend,
|
|
171
197
|
cache_fname_fmt=cache_fname_fmt,
|
|
198
|
+
cache_fname_fmt_args=cache_fname_fmt_args,
|
|
172
199
|
cache_dump_fn=cache_dump_fn,
|
|
173
200
|
cache_load_fn=cache_load_fn,
|
|
174
201
|
cache_enable=cache_enable,
|
|
@@ -190,10 +217,30 @@ def disk_cache_call(
|
|
|
190
217
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
191
218
|
cache_saving_backend: Literal["custom"],
|
|
192
219
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
193
|
-
|
|
194
|
-
|
|
220
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
221
|
+
cache_dump_fn: Callable[[Any, Path], Any],
|
|
222
|
+
cache_load_fn: Callable[[Path], Any],
|
|
223
|
+
cache_enable: bool = True,
|
|
224
|
+
cache_store_mode: StoreMode,
|
|
225
|
+
**kwargs,
|
|
226
|
+
) -> T: ...
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@overload
|
|
230
|
+
def disk_cache_call(
|
|
231
|
+
fn: Callable[..., T],
|
|
232
|
+
*args,
|
|
233
|
+
cache_dpath: Union[str, Path, None] = None,
|
|
234
|
+
cache_force: bool = False,
|
|
235
|
+
cache_verbose: int = 0,
|
|
236
|
+
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
237
|
+
cache_saving_backend: SavingBackend,
|
|
238
|
+
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
239
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
240
|
+
cache_dump_fn: None = None,
|
|
241
|
+
cache_load_fn: None = None,
|
|
195
242
|
cache_enable: bool = True,
|
|
196
|
-
cache_store_mode: StoreMode =
|
|
243
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
197
244
|
**kwargs,
|
|
198
245
|
) -> T: ...
|
|
199
246
|
|
|
@@ -208,10 +255,11 @@ def disk_cache_call(
|
|
|
208
255
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
209
256
|
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
210
257
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
258
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
211
259
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
212
260
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
213
261
|
cache_enable: bool = True,
|
|
214
|
-
cache_store_mode: StoreMode =
|
|
262
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
215
263
|
**kwargs,
|
|
216
264
|
) -> T: ...
|
|
217
265
|
|
|
@@ -225,10 +273,11 @@ def disk_cache_call(
|
|
|
225
273
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
226
274
|
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
227
275
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
276
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
228
277
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
229
278
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
230
279
|
cache_enable: bool = True,
|
|
231
|
-
cache_store_mode: StoreMode =
|
|
280
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
232
281
|
**kwargs,
|
|
233
282
|
) -> T:
|
|
234
283
|
r"""Call function and store output in a cache file.
|
|
@@ -266,6 +315,7 @@ def disk_cache_call(
|
|
|
266
315
|
cache_checksum_fn=cache_checksum_fn,
|
|
267
316
|
cache_saving_backend=cache_saving_backend,
|
|
268
317
|
cache_fname_fmt=cache_fname_fmt,
|
|
318
|
+
cache_fname_fmt_args=cache_fname_fmt_args,
|
|
269
319
|
cache_dump_fn=cache_dump_fn,
|
|
270
320
|
cache_load_fn=cache_load_fn,
|
|
271
321
|
cache_enable=cache_enable,
|
|
@@ -282,10 +332,11 @@ def _disk_cache_impl(
|
|
|
282
332
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
283
333
|
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
284
334
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
335
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
285
336
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
286
337
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
287
338
|
cache_enable: bool = True,
|
|
288
|
-
cache_store_mode: StoreMode =
|
|
339
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
289
340
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
290
341
|
# for backward compatibility
|
|
291
342
|
if cache_fname_fmt is None:
|
|
@@ -368,15 +419,30 @@ def _disk_cache_impl(
|
|
|
368
419
|
@wraps(fn)
|
|
369
420
|
def _disk_cache_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
370
421
|
checksum_args = fn, args, kwargs
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
fn_name=fn_name
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
422
|
+
|
|
423
|
+
kwds = {}
|
|
424
|
+
|
|
425
|
+
if cache_fname_fmt_args is None or "fn_name" in cache_fname_fmt_args:
|
|
426
|
+
kwds["fn_name"] = fn_name
|
|
427
|
+
|
|
428
|
+
if cache_fname_fmt_args is None or "suffix" in cache_fname_fmt_args:
|
|
429
|
+
kwds["suffix"] = suffix
|
|
430
|
+
|
|
431
|
+
if cache_fname_fmt_args is None or "csum" in cache_fname_fmt_args:
|
|
432
|
+
csum = cache_checksum_fn(checksum_args)
|
|
433
|
+
kwds["checksum"] = csum
|
|
434
|
+
kwds["csum"] = csum
|
|
435
|
+
else:
|
|
436
|
+
csum = None
|
|
437
|
+
|
|
438
|
+
inputs_kwds = {
|
|
439
|
+
argname: argval
|
|
440
|
+
for argname, argval in zip(argnames, args)
|
|
441
|
+
if cache_fname_fmt_args is None or argname in cache_fname_fmt_args
|
|
442
|
+
}
|
|
443
|
+
kwds.update(inputs_kwds)
|
|
444
|
+
|
|
445
|
+
cache_fname = cache_fname_fmt(**kwds)
|
|
380
446
|
cache_fpath = cache_fn_dpath.joinpath(cache_fname)
|
|
381
447
|
|
|
382
448
|
if not cache_enable:
|
|
@@ -5,7 +5,7 @@ import logging
|
|
|
5
5
|
import re
|
|
6
6
|
import sys
|
|
7
7
|
from dataclasses import asdict, dataclass
|
|
8
|
-
from typing import Any,
|
|
8
|
+
from typing import Any, List, Mapping, Tuple, TypedDict, Union, overload
|
|
9
9
|
|
|
10
10
|
from typing_extensions import NotRequired, Self, TypeAlias
|
|
11
11
|
|
|
@@ -40,7 +40,7 @@ VersionTuple: TypeAlias = Union[
|
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
VersionDictLike: TypeAlias = Mapping[str, Union[int, PreRelease, BuildMetadata]]
|
|
43
|
-
VersionTupleLike: TypeAlias =
|
|
43
|
+
VersionTupleLike: TypeAlias = Tuple[Union[int, PreRelease, BuildMetadata], ...]
|
|
44
44
|
VersionLike: TypeAlias = Union["Version", str, VersionDictLike, VersionTupleLike]
|
|
45
45
|
|
|
46
46
|
|
|
@@ -50,7 +50,7 @@ class Version:
|
|
|
50
50
|
|
|
51
51
|
Version format is: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]
|
|
52
52
|
|
|
53
|
-
Based on https://semver.org
|
|
53
|
+
Based on https://semver.org/ version 2.0.0.
|
|
54
54
|
"""
|
|
55
55
|
|
|
56
56
|
major: int
|
|
@@ -266,11 +266,8 @@ class Version:
|
|
|
266
266
|
version_tuple = tuple(self.to_dict(exclude_none).values())
|
|
267
267
|
return version_tuple # type: ignore
|
|
268
268
|
|
|
269
|
-
def
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
def __eq__(self, other: Any) -> bool:
|
|
273
|
-
if isinstance(other, (dict, tuple, str)):
|
|
269
|
+
def equals(self, other: VersionLike, *, ignore_buildmetadata: bool = False) -> bool:
|
|
270
|
+
if isinstance(other, (Mapping, tuple, str)):
|
|
274
271
|
other = Version(other)
|
|
275
272
|
# note: use self.__class__ to avoid error cause by 'pytest -v test' collect
|
|
276
273
|
elif not isinstance(other, (Version, self.__class__)):
|
|
@@ -281,67 +278,80 @@ class Version:
|
|
|
281
278
|
and self.minor == other.minor
|
|
282
279
|
and self.patch == other.patch
|
|
283
280
|
and self.prerelease == other.prerelease
|
|
284
|
-
and self.buildmetadata == other.buildmetadata
|
|
281
|
+
and (ignore_buildmetadata or self.buildmetadata == other.buildmetadata)
|
|
285
282
|
)
|
|
286
283
|
|
|
287
|
-
def
|
|
288
|
-
|
|
289
|
-
other = Version(other)
|
|
290
|
-
# note: use self.__class__ to avoid error cause by 'pytest -v test' collect
|
|
291
|
-
elif not isinstance(other, (Version, self.__class__)):
|
|
292
|
-
msg = f"Invalid argument type {type(other)}. (expected an instance of one of {(dict, tuple, str, Version)})"
|
|
293
|
-
raise TypeError(msg)
|
|
294
|
-
|
|
295
|
-
self_tuple = self.to_tuple(exclude_none=False)
|
|
296
|
-
other_tuple = other.to_tuple(exclude_none=False)
|
|
297
|
-
|
|
298
|
-
for self_v, other_v in zip(self_tuple, other_tuple):
|
|
299
|
-
if self_v == other_v:
|
|
300
|
-
continue
|
|
301
|
-
if self_v is None and other_v is not None:
|
|
302
|
-
return False
|
|
303
|
-
if self_v is not None and other_v is None:
|
|
304
|
-
return True
|
|
284
|
+
def __str__(self) -> str:
|
|
285
|
+
return self.to_str()
|
|
305
286
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
elif not isinstance(self_v, list):
|
|
309
|
-
raise TypeError(f"Invalid argument type {type(self_v)}.")
|
|
310
|
-
|
|
311
|
-
if isinstance(other_v, (int, str, NoneType)):
|
|
312
|
-
other_v = [other_v]
|
|
313
|
-
elif not isinstance(other_v, list):
|
|
314
|
-
raise TypeError(f"Invalid argument type {type(other_v)}.")
|
|
315
|
-
|
|
316
|
-
minlen = min(len(self_v), len(other_v))
|
|
317
|
-
if len(self_v) != len(other_v) and self_v[:minlen] == other_v[:minlen]:
|
|
318
|
-
return len(self_v) < len(other_v)
|
|
319
|
-
|
|
320
|
-
for self_vi, other_vi in zip(self_v, other_v):
|
|
321
|
-
if self_vi == other_vi:
|
|
322
|
-
continue
|
|
323
|
-
if isinstance(self_vi, int) and isinstance(other_vi, int):
|
|
324
|
-
return self_vi < other_vi
|
|
325
|
-
if isinstance(self_vi, int) and isinstance(other_vi, str):
|
|
326
|
-
return True
|
|
327
|
-
if isinstance(self_vi, str) and isinstance(other_vi, int):
|
|
328
|
-
return False
|
|
329
|
-
if isinstance(self_vi, str) and isinstance(other_vi, str):
|
|
330
|
-
return self_vi < other_vi
|
|
331
|
-
|
|
332
|
-
msg = f"Invalid attribute type {self_vi=} and {other_vi=}."
|
|
333
|
-
raise TypeError(msg)
|
|
287
|
+
def __eq__(self, other: Any) -> bool:
|
|
288
|
+
return self.equals(other)
|
|
334
289
|
|
|
335
|
-
|
|
290
|
+
def __lt__(self, other: VersionLike) -> bool:
|
|
291
|
+
return _compare_lt(self, other)
|
|
336
292
|
|
|
337
293
|
def __le__(self, other: VersionLike) -> bool:
|
|
338
294
|
return (self == other) or (self < other)
|
|
339
295
|
|
|
340
296
|
def __gt__(self, other: VersionLike) -> bool:
|
|
341
|
-
return (
|
|
297
|
+
return _compare_lt(other, self)
|
|
342
298
|
|
|
343
299
|
def __ge__(self, other: VersionLike) -> bool:
|
|
344
|
-
return
|
|
300
|
+
return (self == other) or (self > other)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _compare_lt(
|
|
304
|
+
x: Union[Version, Mapping, tuple, str], y: Union[Version, Mapping, tuple, str]
|
|
305
|
+
) -> bool:
|
|
306
|
+
if isinstance(x, (Mapping, tuple, str)):
|
|
307
|
+
x = Version(x)
|
|
308
|
+
if isinstance(y, (Mapping, tuple, str)):
|
|
309
|
+
y = Version(y)
|
|
310
|
+
|
|
311
|
+
self_tuple = x.to_tuple(exclude_none=False)
|
|
312
|
+
other_tuple = y.to_tuple(exclude_none=False)
|
|
313
|
+
|
|
314
|
+
self_tuple = self_tuple[:4]
|
|
315
|
+
other_tuple = other_tuple[:4]
|
|
316
|
+
|
|
317
|
+
for self_v, other_v in zip(self_tuple, other_tuple):
|
|
318
|
+
if self_v == other_v:
|
|
319
|
+
continue
|
|
320
|
+
if self_v is None and other_v is not None:
|
|
321
|
+
return False
|
|
322
|
+
if self_v is not None and other_v is None:
|
|
323
|
+
return True
|
|
324
|
+
|
|
325
|
+
if isinstance(self_v, (int, str, NoneType)):
|
|
326
|
+
self_v = [self_v]
|
|
327
|
+
elif not isinstance(self_v, list):
|
|
328
|
+
raise TypeError(f"Invalid argument type {type(self_v)}.")
|
|
329
|
+
|
|
330
|
+
if isinstance(other_v, (int, str, NoneType)):
|
|
331
|
+
other_v = [other_v]
|
|
332
|
+
elif not isinstance(other_v, list):
|
|
333
|
+
raise TypeError(f"Invalid argument type {type(other_v)}.")
|
|
334
|
+
|
|
335
|
+
minlen = min(len(self_v), len(other_v))
|
|
336
|
+
if len(self_v) != len(other_v) and self_v[:minlen] == other_v[:minlen]:
|
|
337
|
+
return len(self_v) < len(other_v)
|
|
338
|
+
|
|
339
|
+
for self_vi, other_vi in zip(self_v, other_v):
|
|
340
|
+
if self_vi == other_vi:
|
|
341
|
+
continue
|
|
342
|
+
if isinstance(self_vi, int) and isinstance(other_vi, int):
|
|
343
|
+
return self_vi < other_vi
|
|
344
|
+
if isinstance(self_vi, int) and isinstance(other_vi, str):
|
|
345
|
+
return True
|
|
346
|
+
if isinstance(self_vi, str) and isinstance(other_vi, int):
|
|
347
|
+
return False
|
|
348
|
+
if isinstance(self_vi, str) and isinstance(other_vi, str):
|
|
349
|
+
return self_vi < other_vi
|
|
350
|
+
|
|
351
|
+
msg = f"Invalid attribute type {self_vi=} and {other_vi=}."
|
|
352
|
+
raise TypeError(msg)
|
|
353
|
+
|
|
354
|
+
return False
|
|
345
355
|
|
|
346
356
|
|
|
347
357
|
def _parse_version_str(version_str: str) -> VersionDict:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.9
|
|
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>
|
|
@@ -52,7 +52,6 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
License-File: LICENSE
|
|
53
53
|
Requires-Dist: typing-extensions>=4.10.0
|
|
54
54
|
Provides-Extra: dev
|
|
55
|
-
Dynamic: license-file
|
|
56
55
|
|
|
57
56
|
# pythonwrench
|
|
58
57
|
|
|
@@ -57,6 +57,23 @@ class TestChecksum(TestCase):
|
|
|
57
57
|
assert s1 == s2
|
|
58
58
|
assert checksum_any(s1) == checksum_any(s2)
|
|
59
59
|
|
|
60
|
+
def test_enums(self) -> None:
|
|
61
|
+
from enum import Enum
|
|
62
|
+
|
|
63
|
+
class Color(Enum):
|
|
64
|
+
RED = 1
|
|
65
|
+
GREEN = 2
|
|
66
|
+
BLUE = 3
|
|
67
|
+
ROUGE = RED
|
|
68
|
+
|
|
69
|
+
c1 = Color.RED
|
|
70
|
+
c2 = Color.GREEN
|
|
71
|
+
assert c1 != c2
|
|
72
|
+
assert checksum_any(c1) != checksum_any(c2)
|
|
73
|
+
|
|
74
|
+
assert Color.RED == Color.ROUGE
|
|
75
|
+
assert checksum_any(Color.RED) == checksum_any(Color.ROUGE)
|
|
76
|
+
|
|
60
77
|
|
|
61
78
|
if __name__ == "__main__":
|
|
62
79
|
unittest.main()
|
|
@@ -63,6 +63,15 @@ class TestVersion(TestCase):
|
|
|
63
63
|
# Check if versions can be parsed
|
|
64
64
|
Version(pw.__version__)
|
|
65
65
|
|
|
66
|
+
# buildmetadata can contains "-" symbol
|
|
67
|
+
v14 = Version("1.0.0+build-info")
|
|
68
|
+
assert v14.to_dict() == {
|
|
69
|
+
"major": 1,
|
|
70
|
+
"minor": 0,
|
|
71
|
+
"patch": 0,
|
|
72
|
+
"buildmetadata": "build-info",
|
|
73
|
+
}
|
|
74
|
+
|
|
66
75
|
def test_parse_invalid(self) -> None:
|
|
67
76
|
with self.assertRaises(ValueError):
|
|
68
77
|
Version() # type: ignore
|
|
@@ -133,6 +142,27 @@ class TestVersion(TestCase):
|
|
|
133
142
|
for version_str in tests:
|
|
134
143
|
_ = Version(version_str)
|
|
135
144
|
|
|
145
|
+
def test_priority(self) -> None:
|
|
146
|
+
v1 = Version("1.0.0")
|
|
147
|
+
v2 = Version("1.0.0-alpha")
|
|
148
|
+
v3 = Version("1.0.0+build")
|
|
149
|
+
v4 = Version("1.0.0-alpha+build")
|
|
150
|
+
|
|
151
|
+
# IMPORTANT: prerelease should have lower precedence than the associated normal version
|
|
152
|
+
assert v1 > v2
|
|
153
|
+
assert not (v1 < v2)
|
|
154
|
+
assert v1 != v2
|
|
155
|
+
|
|
156
|
+
# IMPORTANT: build metadata should not affect version precedence
|
|
157
|
+
assert not (v1 > v3)
|
|
158
|
+
assert not (v1 < v3)
|
|
159
|
+
assert v1 != v3
|
|
160
|
+
|
|
161
|
+
# check both
|
|
162
|
+
assert v1 > v4
|
|
163
|
+
assert not (v1 < v4)
|
|
164
|
+
assert v1 != v4
|
|
165
|
+
|
|
136
166
|
|
|
137
167
|
if __name__ == "__main__":
|
|
138
168
|
unittest.main()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|