pythonwrench 0.4.6__tar.gz → 0.4.8__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.6/src/pythonwrench.egg-info → pythonwrench-0.4.8}/PKG-INFO +1 -1
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/__init__.py +7 -2
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/cast.py +1 -1
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/checksum.py +16 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/disk_cache.py +177 -26
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/entries.py +5 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/functools.py +1 -1
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/pickle.py +32 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/typing/checks.py +12 -6
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/typing/classes.py +35 -3
- {pythonwrench-0.4.6 → pythonwrench-0.4.8/src/pythonwrench.egg-info}/PKG-INFO +1 -1
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_checksum.py +17 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_typing.py +8 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/LICENSE +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/README.md +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/docs/requirements.txt +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/pyproject.toml +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/setup.cfg +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/setup.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/_core.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/argparse.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/collections/__init__.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/collections/collections.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/collections/reducers.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/concurrent.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/dataclasses.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/jsonl.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/logging.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/os.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/re.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/semver.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/typing/__init__.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench/warnings.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench.egg-info/SOURCES.txt +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench.egg-info/entry_points.txt +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench.egg-info/requires.txt +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/src/pythonwrench.egg-info/top_level.txt +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_abc.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_argparse.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_cast.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_collections.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_csv.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_difflib.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_disk_cache.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_entries.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_enum.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_functools.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_importlib.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_inspect.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_json.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_logging.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_math.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_os.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_random.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_readme.py +0 -0
- {pythonwrench-0.4.6 → pythonwrench-0.4.8}/tests/test_semver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
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>
|
|
@@ -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.8"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Re-import for language servers
|
|
@@ -54,7 +54,7 @@ from .argparse import (
|
|
|
54
54
|
str_to_type,
|
|
55
55
|
)
|
|
56
56
|
from .cast import as_builtin, register_as_builtin_fn
|
|
57
|
-
from .checksum import checksum_any, register_checksum_fn
|
|
57
|
+
from .checksum import checksum_any, checksum_object, register_checksum_fn
|
|
58
58
|
from .collections import (
|
|
59
59
|
all_eq,
|
|
60
60
|
all_ne,
|
|
@@ -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
|
|
@@ -22,6 +23,7 @@ from typing import (
|
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
from pythonwrench._core import ClassOrTuple, Predicate, _FunctionRegistry
|
|
26
|
+
from pythonwrench.functools import function_alias
|
|
25
27
|
from pythonwrench.inspect import get_fullname
|
|
26
28
|
from pythonwrench.typing import (
|
|
27
29
|
DataclassInstance,
|
|
@@ -85,10 +87,15 @@ def checksum_any(
|
|
|
85
87
|
"""Compute checksum integer value from an arbitrary object.
|
|
86
88
|
|
|
87
89
|
Supports most builtin types. Checksum can be used to compare objects.
|
|
90
|
+
Not meant for security/cryptography.
|
|
88
91
|
"""
|
|
89
92
|
return _CHECKSUM_REGISTRY.apply(x, isinstance_fn=isinstance_fn, **kwargs)
|
|
90
93
|
|
|
91
94
|
|
|
95
|
+
@function_alias(checksum_any)
|
|
96
|
+
def checksum_object(*args, **kwargs): ...
|
|
97
|
+
|
|
98
|
+
|
|
92
99
|
# Terminate functions
|
|
93
100
|
@register_checksum_fn(bool)
|
|
94
101
|
def checksum_bool(x: bool, **kwargs) -> int:
|
|
@@ -185,6 +192,11 @@ def checksum_dict(x: dict, **kwargs) -> int:
|
|
|
185
192
|
return _checksum_mapping(x, **kwargs)
|
|
186
193
|
|
|
187
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
|
+
|
|
188
200
|
@register_checksum_fn((list, tuple))
|
|
189
201
|
def checksum_list_tuple(x: Union[list, tuple], **kwargs) -> int:
|
|
190
202
|
return _checksum_iterable(x, **kwargs)
|
|
@@ -253,6 +265,10 @@ def checksum_path(x: Path, **kwargs) -> int:
|
|
|
253
265
|
kwargs["accumulator"] = kwargs.get("accumulator", 0) + _cached_checksum_str(
|
|
254
266
|
get_fullname(x)
|
|
255
267
|
)
|
|
268
|
+
resolve_path = kwargs.get("resolve_path", False)
|
|
269
|
+
if isinstance(resolve_path, bool) and resolve_path:
|
|
270
|
+
x = x.expanduser().resolve()
|
|
271
|
+
|
|
256
272
|
return checksum_str(str(x), **kwargs)
|
|
257
273
|
|
|
258
274
|
|
|
@@ -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
|
|
@@ -61,12 +64,49 @@ def disk_cache_decorator(
|
|
|
61
64
|
cache_force: bool = False,
|
|
62
65
|
cache_verbose: int = 0,
|
|
63
66
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
64
|
-
cache_saving_backend:
|
|
67
|
+
cache_saving_backend: Literal["custom"],
|
|
68
|
+
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
69
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
70
|
+
cache_dump_fn: Callable[[Any, Path], Any],
|
|
71
|
+
cache_load_fn: Callable[[Path], Any],
|
|
72
|
+
cache_enable: bool = True,
|
|
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,
|
|
92
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@overload
|
|
96
|
+
def disk_cache_decorator(
|
|
97
|
+
fn: None = None,
|
|
98
|
+
*,
|
|
99
|
+
cache_dpath: Union[str, Path, None] = None,
|
|
100
|
+
cache_force: bool = False,
|
|
101
|
+
cache_verbose: int = 0,
|
|
102
|
+
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
103
|
+
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
65
104
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
105
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
66
106
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
67
107
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
68
108
|
cache_enable: bool = True,
|
|
69
|
-
cache_store_mode: StoreMode =
|
|
109
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
70
110
|
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
|
71
111
|
|
|
72
112
|
|
|
@@ -78,12 +118,31 @@ def disk_cache_decorator(
|
|
|
78
118
|
cache_force: bool = False,
|
|
79
119
|
cache_verbose: int = 0,
|
|
80
120
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
81
|
-
cache_saving_backend:
|
|
121
|
+
cache_saving_backend: Literal["custom"],
|
|
122
|
+
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
123
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
124
|
+
cache_dump_fn: Callable[[Any, Path], Any],
|
|
125
|
+
cache_load_fn: Callable[[Path], Any],
|
|
126
|
+
cache_enable: bool = True,
|
|
127
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
128
|
+
) -> Callable[P, T]: ...
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@overload
|
|
132
|
+
def disk_cache_decorator(
|
|
133
|
+
fn: Callable[P, T],
|
|
134
|
+
*,
|
|
135
|
+
cache_dpath: Union[str, Path, None] = None,
|
|
136
|
+
cache_force: bool = False,
|
|
137
|
+
cache_verbose: int = 0,
|
|
138
|
+
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
139
|
+
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
82
140
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
141
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
83
142
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
84
143
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
85
144
|
cache_enable: bool = True,
|
|
86
|
-
cache_store_mode: StoreMode =
|
|
145
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
87
146
|
) -> Callable[P, T]: ...
|
|
88
147
|
|
|
89
148
|
|
|
@@ -94,12 +153,13 @@ def disk_cache_decorator(
|
|
|
94
153
|
cache_force: bool = False,
|
|
95
154
|
cache_verbose: int = 0,
|
|
96
155
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
97
|
-
cache_saving_backend:
|
|
156
|
+
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
98
157
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
158
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
99
159
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
100
160
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
101
161
|
cache_enable: bool = True,
|
|
102
|
-
cache_store_mode: StoreMode =
|
|
162
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
103
163
|
) -> Callable:
|
|
104
164
|
"""Decorator to store function output in a cache file.
|
|
105
165
|
|
|
@@ -121,7 +181,7 @@ def disk_cache_decorator(
|
|
|
121
181
|
cache_force: Force function call and overwrite cache. defaults to False.
|
|
122
182
|
cache_verbose: Set verbose logging level. Higher means more verbose. defaults to 0.
|
|
123
183
|
cache_checksum_fn: Checksum function to identify input arguments. defaults to ``pythonwrench.checksum_any``.
|
|
124
|
-
cache_saving_backend: Optional saving backend. Can be one of ('csv', 'json', 'pickle'). defaults to '
|
|
184
|
+
cache_saving_backend: Optional saving backend. Can be one of ('csv', 'json', 'pickle', 'custom', 'auto'). defaults to 'auto'.
|
|
125
185
|
cache_fname_fmt: Cache filename format. defaults to "{fn_name}_{csum}{suffix}".
|
|
126
186
|
cache_dump_fn: Dump/save function to store outputs and overwrite saving backend. defaults to None.
|
|
127
187
|
cache_load_fn: Load function to store outputs and overwrite saving backend. defaults to None.
|
|
@@ -135,6 +195,7 @@ def disk_cache_decorator(
|
|
|
135
195
|
cache_checksum_fn=cache_checksum_fn,
|
|
136
196
|
cache_saving_backend=cache_saving_backend,
|
|
137
197
|
cache_fname_fmt=cache_fname_fmt,
|
|
198
|
+
cache_fname_fmt_args=cache_fname_fmt_args,
|
|
138
199
|
cache_dump_fn=cache_dump_fn,
|
|
139
200
|
cache_load_fn=cache_load_fn,
|
|
140
201
|
cache_enable=cache_enable,
|
|
@@ -146,6 +207,7 @@ def disk_cache_decorator(
|
|
|
146
207
|
return impl_fn
|
|
147
208
|
|
|
148
209
|
|
|
210
|
+
@overload
|
|
149
211
|
def disk_cache_call(
|
|
150
212
|
fn: Callable[..., T],
|
|
151
213
|
*args,
|
|
@@ -153,12 +215,69 @@ def disk_cache_call(
|
|
|
153
215
|
cache_force: bool = False,
|
|
154
216
|
cache_verbose: int = 0,
|
|
155
217
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
156
|
-
cache_saving_backend:
|
|
218
|
+
cache_saving_backend: Literal["custom"],
|
|
157
219
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
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,
|
|
242
|
+
cache_enable: bool = True,
|
|
243
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
244
|
+
**kwargs,
|
|
245
|
+
) -> T: ...
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@overload
|
|
249
|
+
def disk_cache_call(
|
|
250
|
+
fn: Callable[..., T],
|
|
251
|
+
*args,
|
|
252
|
+
cache_dpath: Union[str, Path, None] = None,
|
|
253
|
+
cache_force: bool = False,
|
|
254
|
+
cache_verbose: int = 0,
|
|
255
|
+
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
256
|
+
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
257
|
+
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
258
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
158
259
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
159
260
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
160
261
|
cache_enable: bool = True,
|
|
161
|
-
cache_store_mode: StoreMode =
|
|
262
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
263
|
+
**kwargs,
|
|
264
|
+
) -> T: ...
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def disk_cache_call(
|
|
268
|
+
fn: Callable[..., T],
|
|
269
|
+
*args,
|
|
270
|
+
cache_dpath: Union[str, Path, None] = None,
|
|
271
|
+
cache_force: bool = False,
|
|
272
|
+
cache_verbose: int = 0,
|
|
273
|
+
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
274
|
+
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
275
|
+
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
276
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
277
|
+
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
278
|
+
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
279
|
+
cache_enable: bool = True,
|
|
280
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
162
281
|
**kwargs,
|
|
163
282
|
) -> T:
|
|
164
283
|
r"""Call function and store output in a cache file.
|
|
@@ -180,7 +299,7 @@ def disk_cache_call(
|
|
|
180
299
|
cache_force: Force function call and overwrite cache. defaults to False.
|
|
181
300
|
cache_verbose: Set verbose logging level. Higher means more verbose. defaults to 0.
|
|
182
301
|
cache_checksum_fn: Checksum function to identify input arguments. defaults to ``pythonwrench.checksum_any``.
|
|
183
|
-
cache_saving_backend: Optional saving backend. Can be one of ('csv', 'json', 'pickle'). defaults to '
|
|
302
|
+
cache_saving_backend: Optional saving backend. Can be one of ('csv', 'json', 'pickle', 'custom', 'auto'). defaults to 'auto'.
|
|
184
303
|
cache_fname_fmt: Cache filename format. defaults to '{fn_name}_{csum}{suffix}'.
|
|
185
304
|
cache_dump_fn: Dump/save function to store outputs and overwrite saving backend. defaults to None.
|
|
186
305
|
cache_load_fn: Load function to store outputs and overwrite saving backend. defaults to None.
|
|
@@ -196,6 +315,7 @@ def disk_cache_call(
|
|
|
196
315
|
cache_checksum_fn=cache_checksum_fn,
|
|
197
316
|
cache_saving_backend=cache_saving_backend,
|
|
198
317
|
cache_fname_fmt=cache_fname_fmt,
|
|
318
|
+
cache_fname_fmt_args=cache_fname_fmt_args,
|
|
199
319
|
cache_dump_fn=cache_dump_fn,
|
|
200
320
|
cache_load_fn=cache_load_fn,
|
|
201
321
|
cache_enable=cache_enable,
|
|
@@ -210,20 +330,36 @@ def _disk_cache_impl(
|
|
|
210
330
|
cache_force: bool = False,
|
|
211
331
|
cache_verbose: int = 0,
|
|
212
332
|
cache_checksum_fn: ChecksumFn = checksum_any,
|
|
213
|
-
cache_saving_backend:
|
|
333
|
+
cache_saving_backend: Union[SavingBackend, Literal["custom", "auto"]] = "auto",
|
|
214
334
|
cache_fname_fmt: Union[str, Callable[..., str]] = "{fn_name}_{csum}{suffix}",
|
|
335
|
+
cache_fname_fmt_args: Optional[Iterable[str]] = None,
|
|
215
336
|
cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
|
|
216
337
|
cache_load_fn: Optional[Callable[[Path], Any]] = None,
|
|
217
338
|
cache_enable: bool = True,
|
|
218
|
-
cache_store_mode: StoreMode =
|
|
339
|
+
cache_store_mode: StoreMode = _DEFAULT_CACHE_STORE_MODE,
|
|
219
340
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
220
341
|
# for backward compatibility
|
|
221
342
|
if cache_fname_fmt is None:
|
|
343
|
+
expected = "{fn_name}_{csum}{suffix}"
|
|
222
344
|
warnings.warn(
|
|
223
|
-
f"Deprecated argument value {cache_fname_fmt=}. (use
|
|
345
|
+
f"Deprecated argument value {cache_fname_fmt=}. (use {expected} instead)",
|
|
224
346
|
DeprecationWarning,
|
|
225
347
|
)
|
|
226
|
-
cache_fname_fmt =
|
|
348
|
+
cache_fname_fmt = expected
|
|
349
|
+
|
|
350
|
+
if cache_saving_backend is None:
|
|
351
|
+
expected = "auto"
|
|
352
|
+
warnings.warn(
|
|
353
|
+
f"Deprecated argument value {cache_saving_backend=}. (use {expected} instead)",
|
|
354
|
+
DeprecationWarning,
|
|
355
|
+
)
|
|
356
|
+
cache_saving_backend = expected
|
|
357
|
+
|
|
358
|
+
if cache_saving_backend == "auto":
|
|
359
|
+
if cache_dump_fn is not None and cache_load_fn is not None:
|
|
360
|
+
cache_saving_backend = "custom"
|
|
361
|
+
else:
|
|
362
|
+
cache_saving_backend = "pickle"
|
|
227
363
|
|
|
228
364
|
if cache_saving_backend == "pickle":
|
|
229
365
|
from pythonwrench.pickle import dump_pickle, load_pickle
|
|
@@ -250,9 +386,9 @@ def _disk_cache_impl(
|
|
|
250
386
|
cache_dump_fn = dump_csv
|
|
251
387
|
cache_load_fn = load_csv
|
|
252
388
|
|
|
253
|
-
elif cache_saving_backend
|
|
254
|
-
if
|
|
255
|
-
msg = f"If {cache_saving_backend=}, arguments
|
|
389
|
+
elif cache_saving_backend == "custom":
|
|
390
|
+
if cache_dump_fn is None or cache_load_fn is None:
|
|
391
|
+
msg = f"If {cache_saving_backend=}, arguments cache_dump_fn and cache_load_fn cannot be None. (found {cache_dump_fn=} {cache_load_fn=})"
|
|
256
392
|
raise ValueError(msg)
|
|
257
393
|
|
|
258
394
|
suffix = ""
|
|
@@ -283,15 +419,30 @@ def _disk_cache_impl(
|
|
|
283
419
|
@wraps(fn)
|
|
284
420
|
def _disk_cache_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
285
421
|
checksum_args = fn, args, kwargs
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
fn_name=fn_name
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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)
|
|
295
446
|
cache_fpath = cache_fn_dpath.joinpath(cache_fname)
|
|
296
447
|
|
|
297
448
|
if not cache_enable:
|
|
@@ -175,24 +175,29 @@ def main_safe_rmdir() -> None:
|
|
|
175
175
|
)
|
|
176
176
|
parser.add_argument(
|
|
177
177
|
"--rm_root",
|
|
178
|
+
"--rm-root",
|
|
178
179
|
type=str_to_bool,
|
|
179
180
|
default=True,
|
|
180
181
|
help="If True, remove the root directory too if it is empty at the end. defaults to True.",
|
|
181
182
|
)
|
|
182
183
|
parser.add_argument(
|
|
183
184
|
"--error_on_non_empty_dir",
|
|
185
|
+
"--error-on-non-empty-dir",
|
|
184
186
|
type=str_to_bool,
|
|
185
187
|
default=True,
|
|
186
188
|
help="If True, raises a RuntimeError if a subdirectory contains at least 1 file. Otherwise it will ignore non-empty directories. defaults to True.",
|
|
187
189
|
)
|
|
188
190
|
parser.add_argument(
|
|
189
191
|
"--followlinks",
|
|
192
|
+
"--follow_links",
|
|
193
|
+
"--follow-links",
|
|
190
194
|
type=str_to_bool,
|
|
191
195
|
default=False,
|
|
192
196
|
help="Indicates whether or not symbolic links shound be followed. defaults to False.",
|
|
193
197
|
)
|
|
194
198
|
parser.add_argument(
|
|
195
199
|
"--dry_run",
|
|
200
|
+
"--dry-run",
|
|
196
201
|
type=str_to_bool,
|
|
197
202
|
default=False,
|
|
198
203
|
help="If True, does not remove any directory and just output the list of directories which could be deleted. defaults to False.",
|
|
@@ -89,7 +89,7 @@ class Compose(Generic[T, U]):
|
|
|
89
89
|
elif isinstance_generic(fns, Tuple[Callable, ...]):
|
|
90
90
|
pass
|
|
91
91
|
else:
|
|
92
|
-
msg = f"Invalid argument types {type(fns)=}."
|
|
92
|
+
msg = f"Invalid argument types {type(fns)=}. (with {fns=})"
|
|
93
93
|
raise TypeError(msg)
|
|
94
94
|
|
|
95
95
|
super().__init__()
|
|
@@ -66,6 +66,16 @@ def dumps_pickle(
|
|
|
66
66
|
to_builtins: bool = False,
|
|
67
67
|
**pkl_dumps_kwds,
|
|
68
68
|
) -> bytes:
|
|
69
|
+
r"""Dump content to PICKLE format into bytes.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
data: Data to dump to PICKLE.
|
|
73
|
+
to_builtins: If True, converts data to builtin equivalent before saving. defaults to False.
|
|
74
|
+
\*\*pkl_dumps_kwds: Other args passed to `pickle.dumps`.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dumped content as bytes.
|
|
78
|
+
"""
|
|
69
79
|
with BytesIO() as buffer:
|
|
70
80
|
_serialize_pickle(
|
|
71
81
|
data,
|
|
@@ -87,6 +97,16 @@ def save_pickle(
|
|
|
87
97
|
to_builtins: bool = False,
|
|
88
98
|
**pkl_dumps_kwds,
|
|
89
99
|
) -> None:
|
|
100
|
+
r"""Dump content to PICKLE format into file.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
data: Data to dump to PICKLE.
|
|
104
|
+
file: Filepath to save dumped data.
|
|
105
|
+
overwrite: If True, overwrite target filepath. defaults to True.
|
|
106
|
+
make_parents: Build intermediate directories to filepath. defaults to True.
|
|
107
|
+
to_builtins: If True, converts data to builtin equivalent before saving. defaults to False.
|
|
108
|
+
\*\*pkl_dumps_kwds: Other args passed to `pickle.dumps`.
|
|
109
|
+
"""
|
|
90
110
|
if isinstance(file, (str, Path, PathLike)):
|
|
91
111
|
file = _setup_output_fpath(file, overwrite=overwrite, make_parents=make_parents)
|
|
92
112
|
file = open(file, "wb")
|
|
@@ -125,6 +145,12 @@ def _serialize_pickle(
|
|
|
125
145
|
|
|
126
146
|
|
|
127
147
|
def load_pickle(file: Union[str, Path, BinaryIO], /, **pkl_loads_kwds) -> Any:
|
|
148
|
+
r"""Load content from PICKLE file.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
file: Filepath file path.
|
|
152
|
+
\*\*pkl_loads_kwds: Other args passed to `pickle.loads`.
|
|
153
|
+
"""
|
|
128
154
|
if isinstance(file, (str, Path, PathLike)):
|
|
129
155
|
file = open(file, "rb")
|
|
130
156
|
close = True
|
|
@@ -138,6 +164,12 @@ def load_pickle(file: Union[str, Path, BinaryIO], /, **pkl_loads_kwds) -> Any:
|
|
|
138
164
|
|
|
139
165
|
|
|
140
166
|
def loads_pickle(content: bytes, /, **pkl_loads_kwds) -> Any:
|
|
167
|
+
r"""Load content from raw bytes.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
content: Encoded elements bytes.
|
|
171
|
+
\*\*pkl_loads_kwds: Other args passed to `pickle.loads`.
|
|
172
|
+
"""
|
|
141
173
|
with BytesIO(content) as buffer:
|
|
142
174
|
return _parse_pickle(buffer, **pkl_loads_kwds)
|
|
143
175
|
|
|
@@ -99,7 +99,7 @@ def check_args_types(fn: Callable[P, T]) -> Callable[P, T]:
|
|
|
99
99
|
|
|
100
100
|
def isinstance_generic(
|
|
101
101
|
obj: Any,
|
|
102
|
-
class_or_tuple: Union[Type[T], None, Tuple[Type[T], ...]],
|
|
102
|
+
class_or_tuple: Union[Type[T], None, Tuple[Type[T], ...], Any],
|
|
103
103
|
*,
|
|
104
104
|
check_only_first: bool = False,
|
|
105
105
|
) -> TypeIs[T]:
|
|
@@ -123,8 +123,6 @@ def isinstance_generic(
|
|
|
123
123
|
... True
|
|
124
124
|
|
|
125
125
|
"""
|
|
126
|
-
if isinstance(obj, type):
|
|
127
|
-
return False
|
|
128
126
|
if class_or_tuple is Any or class_or_tuple is typing_extensions.Any:
|
|
129
127
|
return True
|
|
130
128
|
if class_or_tuple is None:
|
|
@@ -139,7 +137,7 @@ def isinstance_generic(
|
|
|
139
137
|
|
|
140
138
|
origin = get_origin(class_or_tuple)
|
|
141
139
|
if origin is None:
|
|
142
|
-
return isinstance(obj, class_or_tuple)
|
|
140
|
+
return isinstance(obj, class_or_tuple) # type: ignore
|
|
143
141
|
|
|
144
142
|
# Special case for empty tuple because get_args(Tuple[()]) returns () and not ((),) in python >= 3.11
|
|
145
143
|
# More info at https://github.com/python/cpython/issues/91137
|
|
@@ -147,6 +145,14 @@ def isinstance_generic(
|
|
|
147
145
|
return obj == ()
|
|
148
146
|
|
|
149
147
|
args = get_args(class_or_tuple)
|
|
148
|
+
if origin is Callable:
|
|
149
|
+
if len(args) == 0:
|
|
150
|
+
return callable(obj)
|
|
151
|
+
else:
|
|
152
|
+
# TODO: impl
|
|
153
|
+
msg = "Function `isinstance_generic` currently does not support parametrized Callable."
|
|
154
|
+
raise NotImplementedError(msg)
|
|
155
|
+
|
|
150
156
|
if len(args) == 0:
|
|
151
157
|
return isinstance_generic(obj, origin)
|
|
152
158
|
|
|
@@ -298,7 +304,7 @@ def is_dataclass_instance(x: Any) -> TypeIs[DataclassInstance]:
|
|
|
298
304
|
|
|
299
305
|
Unlike function `dataclasses.is_dataclass`, this function returns False for a dataclass type.
|
|
300
306
|
"""
|
|
301
|
-
return isinstance_generic(x, DataclassInstance)
|
|
307
|
+
return not isinstance(x, type) and isinstance_generic(x, DataclassInstance)
|
|
302
308
|
|
|
303
309
|
|
|
304
310
|
def is_iterable_bool(
|
|
@@ -369,7 +375,7 @@ def is_iterable_str(
|
|
|
369
375
|
|
|
370
376
|
def is_namedtuple_instance(x: Any) -> TypeIs[NamedTupleInstance]:
|
|
371
377
|
"""Returns True if argument is a NamedTuple."""
|
|
372
|
-
return isinstance_generic(x, NamedTupleInstance)
|
|
378
|
+
return not isinstance(x, type) and isinstance_generic(x, NamedTupleInstance)
|
|
373
379
|
|
|
374
380
|
|
|
375
381
|
def is_sequence_str(
|
|
@@ -67,37 +67,50 @@ class NamedTupleInstance(Protocol):
|
|
|
67
67
|
|
|
68
68
|
@runtime_checkable
|
|
69
69
|
class SupportsAdd(Protocol[_T_Other]):
|
|
70
|
+
"""Protocol that support `__add__` (+) method."""
|
|
71
|
+
|
|
70
72
|
def __add__(self, other: _T_Other, /):
|
|
71
73
|
raise NotImplementedError
|
|
72
74
|
|
|
73
75
|
|
|
74
76
|
@runtime_checkable
|
|
75
77
|
class SupportsAnd(Protocol[_T_Other]):
|
|
78
|
+
"""Protocol that support `__and__` (&) method."""
|
|
79
|
+
|
|
76
80
|
def __and__(self, other: _T_Other, /):
|
|
77
81
|
raise NotImplementedError
|
|
78
82
|
|
|
79
83
|
|
|
80
84
|
@runtime_checkable
|
|
81
85
|
class SupportsBool(Protocol):
|
|
86
|
+
"""Protocol that support `__bool__` method."""
|
|
87
|
+
|
|
82
88
|
def __bool__(self) -> bool:
|
|
83
89
|
raise NotImplementedError
|
|
84
90
|
|
|
85
91
|
|
|
86
92
|
@runtime_checkable
|
|
87
93
|
class SupportsDiv(Protocol[_T_Other]):
|
|
94
|
+
"""Protocol that support `__div__` (/) method."""
|
|
95
|
+
|
|
88
96
|
def __div__(self, other: _T_Other, /):
|
|
89
97
|
raise NotImplementedError
|
|
90
98
|
|
|
91
99
|
|
|
92
100
|
@runtime_checkable
|
|
93
101
|
class SupportsGetitem(Protocol[_T_Item, _T_Index]):
|
|
102
|
+
"""Protocol that support `__getitem__` method."""
|
|
103
|
+
|
|
94
104
|
def __getitem__(self, idx: _T_Index, /) -> _T_Item:
|
|
95
105
|
raise NotImplementedError
|
|
96
106
|
|
|
97
107
|
|
|
98
108
|
@runtime_checkable
|
|
99
109
|
class SupportsGetitem2(Protocol[_T_Index2, _T_Item]):
|
|
100
|
-
"""
|
|
110
|
+
"""Protocol that support `__getitem__` method.
|
|
111
|
+
|
|
112
|
+
Same than `SupportsGetitem` except that generic parameters are in reversed order: [T_Index, T_Item].
|
|
113
|
+
"""
|
|
101
114
|
|
|
102
115
|
def __getitem__(self, idx: _T_Index2, /) -> _T_Item:
|
|
103
116
|
raise NotImplementedError
|
|
@@ -105,6 +118,8 @@ class SupportsGetitem2(Protocol[_T_Index2, _T_Item]):
|
|
|
105
118
|
|
|
106
119
|
@runtime_checkable
|
|
107
120
|
class SupportsGetitemLen(Protocol[_T_Item, _T_Index]):
|
|
121
|
+
"""Protocol that support `__getitem__` and `__len__` methods."""
|
|
122
|
+
|
|
108
123
|
def __getitem__(self, idx: _T_Index, /) -> _T_Item:
|
|
109
124
|
raise NotImplementedError
|
|
110
125
|
|
|
@@ -114,7 +129,9 @@ class SupportsGetitemLen(Protocol[_T_Item, _T_Index]):
|
|
|
114
129
|
|
|
115
130
|
@runtime_checkable
|
|
116
131
|
class SupportsGetitemLen2(Protocol[_T_Index2, _T_Item]):
|
|
117
|
-
"""
|
|
132
|
+
"""Protocol that support `__getitem__` and `__len__` methods.
|
|
133
|
+
|
|
134
|
+
Same than `SupportsGetitemLen` except that generic parameters are in reversed order: [T_Index, T_Item]."""
|
|
118
135
|
|
|
119
136
|
def __getitem__(self, idx: _T_Index2, /) -> _T_Item:
|
|
120
137
|
raise NotImplementedError
|
|
@@ -125,6 +142,8 @@ class SupportsGetitemLen2(Protocol[_T_Index2, _T_Item]):
|
|
|
125
142
|
|
|
126
143
|
@runtime_checkable
|
|
127
144
|
class SupportsGetitemIterLen(Protocol[_T_Item, _T_Index]):
|
|
145
|
+
"""Protocol that support `__getitem__`, `__iter__` and `__len__` methods."""
|
|
146
|
+
|
|
128
147
|
def __getitem__(self, idx: _T_Index, /) -> _T_Item:
|
|
129
148
|
raise NotImplementedError
|
|
130
149
|
|
|
@@ -137,7 +156,10 @@ class SupportsGetitemIterLen(Protocol[_T_Item, _T_Index]):
|
|
|
137
156
|
|
|
138
157
|
@runtime_checkable
|
|
139
158
|
class SupportsGetitemIterLen2(Protocol[_T_Index2, _T_Item]):
|
|
140
|
-
"""
|
|
159
|
+
"""Protocol that support `__getitem__`, `__iter__` and `__len__` methods.
|
|
160
|
+
|
|
161
|
+
Same than `SupportsGetitemIterLen` except that generic parameters are in reversed order: [T_Index, T_Item].
|
|
162
|
+
"""
|
|
141
163
|
|
|
142
164
|
def __getitem__(self, idx: _T_Index2, /) -> _T_Item:
|
|
143
165
|
raise NotImplementedError
|
|
@@ -151,6 +173,8 @@ class SupportsGetitemIterLen2(Protocol[_T_Index2, _T_Item]):
|
|
|
151
173
|
|
|
152
174
|
@runtime_checkable
|
|
153
175
|
class SupportsIterLen(Protocol[_T_Item]):
|
|
176
|
+
"""Protocol that support `__iter__` and `__len__` methods."""
|
|
177
|
+
|
|
154
178
|
def __iter__(self) -> Iterator[_T_Item]:
|
|
155
179
|
raise NotImplementedError
|
|
156
180
|
|
|
@@ -160,23 +184,31 @@ class SupportsIterLen(Protocol[_T_Item]):
|
|
|
160
184
|
|
|
161
185
|
@runtime_checkable
|
|
162
186
|
class SupportsLen(Protocol):
|
|
187
|
+
"""Protocol that support `__len__` method."""
|
|
188
|
+
|
|
163
189
|
def __len__(self) -> int:
|
|
164
190
|
raise NotImplementedError
|
|
165
191
|
|
|
166
192
|
|
|
167
193
|
@runtime_checkable
|
|
168
194
|
class SupportsMul(Protocol[_T_Other]):
|
|
195
|
+
"""Protocol that support `__mul__` (*) method."""
|
|
196
|
+
|
|
169
197
|
def __mul__(self, other: _T_Other, /):
|
|
170
198
|
raise NotImplementedError
|
|
171
199
|
|
|
172
200
|
|
|
173
201
|
@runtime_checkable
|
|
174
202
|
class SupportsOr(Protocol[_T_Other]):
|
|
203
|
+
"""Protocol that support `__or__` (|) method."""
|
|
204
|
+
|
|
175
205
|
def __or__(self, other: _T_Other, /):
|
|
176
206
|
raise NotImplementedError
|
|
177
207
|
|
|
178
208
|
|
|
179
209
|
@runtime_checkable
|
|
180
210
|
class SupportsMatmul(Protocol[_T_Other]):
|
|
211
|
+
"""Protocol that support `__matmul__` (@) method."""
|
|
212
|
+
|
|
181
213
|
def __matmul__(self, other: _T_Other, /):
|
|
182
214
|
raise NotImplementedError
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
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>
|
|
@@ -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()
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import unittest
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from numbers import Number
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
from typing import (
|
|
8
9
|
Any,
|
|
9
10
|
Callable,
|
|
@@ -280,6 +281,13 @@ class TestIsInstanceGuard(TestCase):
|
|
|
280
281
|
with self.assertRaises(TypeError):
|
|
281
282
|
assert not isinstance_generic(1, Generator[int, None, None])
|
|
282
283
|
|
|
284
|
+
def test_callable(self) -> None:
|
|
285
|
+
assert isinstance_generic(lambda x: x, Callable)
|
|
286
|
+
assert isinstance_generic(Path, Callable)
|
|
287
|
+
|
|
288
|
+
with self.assertRaises(NotImplementedError):
|
|
289
|
+
assert isinstance_generic(Path, Callable[[str], Path])
|
|
290
|
+
|
|
283
291
|
|
|
284
292
|
class TestCheckArgsType(TestCase):
|
|
285
293
|
def test_example_1(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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|