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.
Files changed (73) hide show
  1. {pythonwrench-0.4.7/src/pythonwrench.egg-info → pythonwrench-0.4.9}/PKG-INFO +2 -3
  2. pythonwrench-0.4.9/docs/requirements.txt +4 -0
  3. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/__init__.py +6 -1
  4. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/cast.py +1 -1
  5. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/checksum.py +6 -0
  6. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/disk_cache.py +86 -20
  7. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/semver.py +68 -58
  8. {pythonwrench-0.4.7 → pythonwrench-0.4.9/src/pythonwrench.egg-info}/PKG-INFO +2 -3
  9. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/SOURCES.txt +1 -0
  10. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_checksum.py +17 -0
  11. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_semver.py +30 -0
  12. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/LICENSE +0 -0
  13. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/README.md +0 -0
  14. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/pyproject.toml +0 -0
  15. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/setup.cfg +0 -0
  16. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/setup.py +0 -0
  17. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/__main__.py +0 -0
  18. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/_core.py +0 -0
  19. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/abc.py +0 -0
  20. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/argparse.py +0 -0
  21. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/__init__.py +0 -0
  22. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/collections.py +0 -0
  23. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/prop.py +0 -0
  24. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/collections/reducers.py +0 -0
  25. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/concurrent.py +0 -0
  26. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/csv.py +0 -0
  27. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/dataclasses.py +0 -0
  28. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/datetime.py +0 -0
  29. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/difflib.py +0 -0
  30. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/entries.py +0 -0
  31. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/enum.py +0 -0
  32. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/functools.py +0 -0
  33. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/hashlib.py +0 -0
  34. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/importlib.py +0 -0
  35. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/inspect.py +0 -0
  36. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/json.py +0 -0
  37. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/jsonl.py +0 -0
  38. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/logging.py +0 -0
  39. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/math.py +0 -0
  40. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/os.py +0 -0
  41. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/pickle.py +0 -0
  42. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/random.py +0 -0
  43. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/re.py +0 -0
  44. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/typing/__init__.py +0 -0
  45. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/typing/checks.py +0 -0
  46. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/typing/classes.py +0 -0
  47. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench/warnings.py +0 -0
  48. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
  49. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/entry_points.txt +0 -0
  50. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/requires.txt +0 -0
  51. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/src/pythonwrench.egg-info/top_level.txt +0 -0
  52. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_abc.py +0 -0
  53. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_argparse.py +0 -0
  54. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_cast.py +0 -0
  55. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_collections.py +0 -0
  56. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_csv.py +0 -0
  57. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_dataclasses.py +0 -0
  58. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_difflib.py +0 -0
  59. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_disk_cache.py +0 -0
  60. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_entries.py +0 -0
  61. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_enum.py +0 -0
  62. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_functools.py +0 -0
  63. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_hashlib.py +0 -0
  64. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_importlib.py +0 -0
  65. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_inspect.py +0 -0
  66. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_json.py +0 -0
  67. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_jsonl.py +0 -0
  68. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_logging.py +0 -0
  69. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_math.py +0 -0
  70. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_os.py +0 -0
  71. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_random.py +0 -0
  72. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_readme.py +0 -0
  73. {pythonwrench-0.4.7 → pythonwrench-0.4.9}/tests/test_typing.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: pythonwrench
3
- Version: 0.4.7
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
 
@@ -0,0 +1,4 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ sphinx-immaterial>=0.11.14
4
+ sphinx<9.0.0
@@ -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.7"
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 = "outputs_metadata",
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 = "outputs_metadata",
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 = "outputs_metadata",
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 = "outputs_metadata",
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 = "outputs_metadata",
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
- cache_dump_fn: Optional[Callable[[Any, Path], Any]] = None,
194
- cache_load_fn: Optional[Callable[[Path], Any]] = None,
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 = "outputs_metadata",
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 = "outputs_metadata",
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 = "outputs_metadata",
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 = "outputs_metadata",
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
- csum = cache_checksum_fn(checksum_args)
372
- inputs = dict(zip(argnames, args))
373
- inputs.update(kwargs)
374
- cache_fname = cache_fname_fmt(
375
- fn_name=fn_name,
376
- csum=csum,
377
- suffix=suffix,
378
- **inputs,
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, Iterable, List, Mapping, Tuple, TypedDict, Union, overload
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 = Iterable[Union[int, PreRelease, BuildMetadata]]
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 __str__(self) -> str:
270
- return self.to_str()
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 __lt__(self, other: VersionLike) -> bool:
288
- if isinstance(other, (dict, tuple, str)):
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
- if isinstance(self_v, (int, str, NoneType)):
307
- self_v = [self_v]
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
- return False
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 (self != other) and not (self < other)
297
+ return _compare_lt(other, self)
342
298
 
343
299
  def __ge__(self, other: VersionLike) -> bool:
344
- return not (self < other)
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.4
1
+ Metadata-Version: 2.1
2
2
  Name: pythonwrench
3
- Version: 0.4.7
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
 
@@ -2,6 +2,7 @@ LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
4
  setup.py
5
+ docs/requirements.txt
5
6
  src/pythonwrench/__init__.py
6
7
  src/pythonwrench/__main__.py
7
8
  src/pythonwrench/_core.py
@@ -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