pythonwrench 0.4.2__tar.gz → 0.4.3__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.2/src/pythonwrench.egg-info → pythonwrench-0.4.3}/PKG-INFO +55 -51
  2. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/README.md +46 -47
  3. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/pyproject.toml +6 -2
  4. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/__init__.py +4 -3
  5. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/cast.py +51 -27
  6. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/__init__.py +1 -0
  7. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/reducers.py +36 -3
  8. pythonwrench-0.4.3/src/pythonwrench/concurrent.py +51 -0
  9. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/dataclasses.py +3 -1
  10. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/entries.py +33 -2
  11. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/functools.py +2 -2
  12. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/os.py +73 -32
  13. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/pickle.py +4 -4
  14. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/re.py +18 -0
  15. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/typing/__init__.py +4 -0
  16. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/typing/checks.py +18 -2
  17. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/typing/classes.py +41 -1
  18. {pythonwrench-0.4.2 → pythonwrench-0.4.3/src/pythonwrench.egg-info}/PKG-INFO +55 -51
  19. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/SOURCES.txt +1 -1
  20. pythonwrench-0.4.2/docs/requirements.txt +0 -3
  21. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/LICENSE +0 -0
  22. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/setup.cfg +0 -0
  23. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/setup.py +0 -0
  24. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/__main__.py +0 -0
  25. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/_core.py +0 -0
  26. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/abc.py +0 -0
  27. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/argparse.py +0 -0
  28. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/checksum.py +0 -0
  29. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/collections.py +0 -0
  30. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/prop.py +0 -0
  31. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/csv.py +0 -0
  32. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/datetime.py +0 -0
  33. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/difflib.py +0 -0
  34. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/disk_cache.py +0 -0
  35. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/enum.py +0 -0
  36. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/hashlib.py +0 -0
  37. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/importlib.py +0 -0
  38. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/inspect.py +0 -0
  39. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/json.py +0 -0
  40. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/jsonl.py +0 -0
  41. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/logging.py +0 -0
  42. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/math.py +0 -0
  43. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/random.py +0 -0
  44. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/semver.py +0 -0
  45. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/warnings.py +0 -0
  46. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
  47. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/entry_points.txt +0 -0
  48. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/requires.txt +0 -0
  49. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/top_level.txt +0 -0
  50. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_abc.py +0 -0
  51. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_argparse.py +0 -0
  52. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_cast.py +0 -0
  53. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_checksum.py +0 -0
  54. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_collections.py +0 -0
  55. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_csv.py +0 -0
  56. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_dataclasses.py +0 -0
  57. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_difflib.py +0 -0
  58. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_disk_cache.py +0 -0
  59. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_entries.py +0 -0
  60. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_enum.py +0 -0
  61. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_functools.py +0 -0
  62. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_hashlib.py +0 -0
  63. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_importlib.py +0 -0
  64. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_inspect.py +0 -0
  65. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_json.py +0 -0
  66. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_jsonl.py +0 -0
  67. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_logging.py +0 -0
  68. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_math.py +0 -0
  69. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_os.py +0 -0
  70. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_random.py +0 -0
  71. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_readme.py +0 -0
  72. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_semver.py +0 -0
  73. {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_typing.py +0 -0
@@ -1,7 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pythonwrench
3
- Version: 0.4.2
4
- Summary: Set of tools for Python that could be in the standard library.
3
+ Version: 0.4.3
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>
7
7
  License: MIT License
@@ -41,13 +41,18 @@ Classifier: Programming Language :: Python :: 3.10
41
41
  Classifier: Programming Language :: Python :: 3.11
42
42
  Classifier: Programming Language :: Python :: 3.12
43
43
  Classifier: Programming Language :: Python :: 3.13
44
+ Classifier: Programming Language :: Python :: 3.14
44
45
  Classifier: Topic :: Scientific/Engineering
45
46
  Classifier: Operating System :: OS Independent
46
- Requires-Python: >=3.8
47
+ Classifier: Operating System :: MacOS :: MacOS X
48
+ Classifier: Operating System :: Microsoft :: Windows
49
+ Classifier: Operating System :: POSIX
50
+ Requires-Python: <3.15,>=3.8
47
51
  Description-Content-Type: text/markdown
48
52
  License-File: LICENSE
49
53
  Requires-Dist: typing-extensions>=4.10.0
50
54
  Provides-Extra: dev
55
+ Dynamic: license-file
51
56
 
52
57
  # pythonwrench
53
58
 
@@ -63,27 +68,67 @@ Provides-Extra: dev
63
68
  <img src='https://readthedocs.org/projects/pythonwrench/badge/?version=stable&style=for-the-badge' alt='Documentation Status' />
64
69
  </a>
65
70
 
66
- Set of tools for Python that could be in the standard library.
71
+ Python library with tools for typing, manipulating collections, and more!
67
72
 
68
73
  </center>
69
74
 
70
75
 
71
76
  ## Installation
72
77
 
73
- With pip:
78
+ With uv:
74
79
  ```bash
75
- pip install pythonwrench
80
+ uv add pythonwrench
76
81
  ```
77
82
 
78
- With uv:
83
+ With pip:
79
84
  ```bash
80
- uv add pythonwrench
85
+ pip install pythonwrench
81
86
  ```
82
87
 
83
- This library has been tested on all Python versions **3.8 - 3.13**, requires only `typing_extensions>=4.10.0`, and runs on **Linux, Mac and Windows** systems.
88
+ This library has been tested on all Python versions **3.8 - 3.14**, requires only `typing_extensions>=4.10.0`, and runs on **Linux, Mac and Windows** systems.
84
89
 
85
90
  ## Examples
86
91
 
92
+ ### Typing
93
+
94
+ Check generic types with ìsinstance_generic` :
95
+
96
+ ```python
97
+ >>> import pythonwrench as pw
98
+ >>>
99
+ >>> # Behaves like builtin isinstance() :
100
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict)
101
+ ... True
102
+ >>> # But works with generic types !
103
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, int])
104
+ ... True
105
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, str])
106
+ ... False
107
+ ```
108
+
109
+ ... or check specific methods with protocols classes beginning with `Supports`
110
+ ```python
111
+ >>> import pythonwrench as pw
112
+ >>>
113
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsIterLen)
114
+ ... True
115
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsGetitemLen)
116
+ ... True
117
+ ```
118
+
119
+ Finally, you can also force argument type checking with `check_args_types` function :
120
+
121
+ ```python
122
+ >>> import pythonwrench as pw
123
+
124
+ >>> @pw.check_args_types
125
+ >>> def f(a: int, b: str) -> str:
126
+ >>> return a * b
127
+
128
+ >>> f(1, "a") # pass check
129
+ >>> f(1, 2) # raises TypeError from decorator
130
+ ```
131
+
87
132
  ### Collections
88
133
 
89
134
  Provides functions to facilitate iterables processing, like `unzip` :
@@ -131,47 +176,6 @@ Easely converts common python structures like list of dicts to dict of lists :
131
176
  ... {"a.x": 1, "a.y": 2, "b.x": 3, "b.y": 4}
132
177
  ```
133
178
 
134
- ### Typing
135
-
136
- Check generic types with ìsinstance_generic` :
137
-
138
- ```python
139
- >>> import pythonwrench as pw
140
- >>>
141
- >>> # Behaves like builtin isinstance() :
142
- >>> pw.isinstance_generic({"a": 1, "b": 2}, dict)
143
- ... True
144
- >>> # But works with generic types !
145
- >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, int])
146
- ... True
147
- >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, str])
148
- ... False
149
- ```
150
-
151
- ... or check specific methods with protocols classes beginning with `Supports`
152
- ```python
153
- >>> import pythonwrench as pw
154
- >>>
155
- >>> # Combines Iterable and Sized !
156
- >>> isinstance({"a": 1, "b": 2}, pw.SupportsGetitemLen)
157
- ... True
158
- >>> isinstance({"a": 1, "b": 2}, pw.SupportsIterLen)
159
- ... True
160
- ```
161
-
162
- Finally, you can also force argument type checking with `check_args_types` function :
163
-
164
- ```python
165
- >>> import pythonwrench as pw
166
-
167
- >>> @pw.check_args_types
168
- >>> def f(a: int, b: str) -> str:
169
- >>> return a * b
170
-
171
- >>> f(1, "a") # pass check
172
- >>> f(1, 2) # raises TypeError from decorator
173
- ```
174
-
175
179
  ### Disk caching (memoize)
176
180
 
177
181
  ```python
@@ -12,27 +12,67 @@
12
12
  <img src='https://readthedocs.org/projects/pythonwrench/badge/?version=stable&style=for-the-badge' alt='Documentation Status' />
13
13
  </a>
14
14
 
15
- Set of tools for Python that could be in the standard library.
15
+ Python library with tools for typing, manipulating collections, and more!
16
16
 
17
17
  </center>
18
18
 
19
19
 
20
20
  ## Installation
21
21
 
22
- With pip:
22
+ With uv:
23
23
  ```bash
24
- pip install pythonwrench
24
+ uv add pythonwrench
25
25
  ```
26
26
 
27
- With uv:
27
+ With pip:
28
28
  ```bash
29
- uv add pythonwrench
29
+ pip install pythonwrench
30
30
  ```
31
31
 
32
- This library has been tested on all Python versions **3.8 - 3.13**, requires only `typing_extensions>=4.10.0`, and runs on **Linux, Mac and Windows** systems.
32
+ This library has been tested on all Python versions **3.8 - 3.14**, requires only `typing_extensions>=4.10.0`, and runs on **Linux, Mac and Windows** systems.
33
33
 
34
34
  ## Examples
35
35
 
36
+ ### Typing
37
+
38
+ Check generic types with ìsinstance_generic` :
39
+
40
+ ```python
41
+ >>> import pythonwrench as pw
42
+ >>>
43
+ >>> # Behaves like builtin isinstance() :
44
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict)
45
+ ... True
46
+ >>> # But works with generic types !
47
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, int])
48
+ ... True
49
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, str])
50
+ ... False
51
+ ```
52
+
53
+ ... or check specific methods with protocols classes beginning with `Supports`
54
+ ```python
55
+ >>> import pythonwrench as pw
56
+ >>>
57
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsIterLen)
58
+ ... True
59
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsGetitemLen)
60
+ ... True
61
+ ```
62
+
63
+ Finally, you can also force argument type checking with `check_args_types` function :
64
+
65
+ ```python
66
+ >>> import pythonwrench as pw
67
+
68
+ >>> @pw.check_args_types
69
+ >>> def f(a: int, b: str) -> str:
70
+ >>> return a * b
71
+
72
+ >>> f(1, "a") # pass check
73
+ >>> f(1, 2) # raises TypeError from decorator
74
+ ```
75
+
36
76
  ### Collections
37
77
 
38
78
  Provides functions to facilitate iterables processing, like `unzip` :
@@ -80,47 +120,6 @@ Easely converts common python structures like list of dicts to dict of lists :
80
120
  ... {"a.x": 1, "a.y": 2, "b.x": 3, "b.y": 4}
81
121
  ```
82
122
 
83
- ### Typing
84
-
85
- Check generic types with ìsinstance_generic` :
86
-
87
- ```python
88
- >>> import pythonwrench as pw
89
- >>>
90
- >>> # Behaves like builtin isinstance() :
91
- >>> pw.isinstance_generic({"a": 1, "b": 2}, dict)
92
- ... True
93
- >>> # But works with generic types !
94
- >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, int])
95
- ... True
96
- >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, str])
97
- ... False
98
- ```
99
-
100
- ... or check specific methods with protocols classes beginning with `Supports`
101
- ```python
102
- >>> import pythonwrench as pw
103
- >>>
104
- >>> # Combines Iterable and Sized !
105
- >>> isinstance({"a": 1, "b": 2}, pw.SupportsGetitemLen)
106
- ... True
107
- >>> isinstance({"a": 1, "b": 2}, pw.SupportsIterLen)
108
- ... True
109
- ```
110
-
111
- Finally, you can also force argument type checking with `check_args_types` function :
112
-
113
- ```python
114
- >>> import pythonwrench as pw
115
-
116
- >>> @pw.check_args_types
117
- >>> def f(a: int, b: str) -> str:
118
- >>> return a * b
119
-
120
- >>> f(1, "a") # pass check
121
- >>> f(1, 2) # raises TypeError from decorator
122
- ```
123
-
124
123
  ### Disk caching (memoize)
125
124
 
126
125
  ```python
@@ -3,9 +3,9 @@ name = "pythonwrench"
3
3
  authors = [
4
4
  {name = "Étienne Labbé (Labbeti)", email = "labbeti.pub@gmail.com"},
5
5
  ]
6
- description = "Set of tools for Python that could be in the standard library."
6
+ description = "Python library with tools for typing, manipulating collections, and more!"
7
7
  readme = "README.md"
8
- requires-python = ">=3.8"
8
+ requires-python = ">=3.8,<3.15"
9
9
  keywords = ["python", "tools", "utilities"]
10
10
  license = {file = "LICENSE"}
11
11
  # license-files = ["LICENSE"] # unsupported by python 3.8, but will be required in 2026
@@ -19,8 +19,12 @@ classifiers = [
19
19
  "Programming Language :: Python :: 3.11",
20
20
  "Programming Language :: Python :: 3.12",
21
21
  "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
22
23
  "Topic :: Scientific/Engineering",
23
24
  "Operating System :: OS Independent",
25
+ "Operating System :: MacOS :: MacOS X",
26
+ "Operating System :: Microsoft :: Windows",
27
+ "Operating System :: POSIX",
24
28
  ]
25
29
  maintainers = [
26
30
  {name = "Étienne Labbé (Labbeti)", email = "labbeti.pub@gmail.com"},
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
- """Set of tools for Python that could be in the standard library."""
4
+ """Python library with tools for typing, manipulating collections, and more!"""
5
5
 
6
6
  __name__ = "pythonwrench"
7
7
  __author__ = "Étienne Labbé (Labbeti)"
@@ -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.2"
12
+ __version__ = "0.4.3"
13
13
 
14
14
 
15
15
  # Re-import for language servers
@@ -52,7 +52,7 @@ from .argparse import (
52
52
  str_to_optional_str,
53
53
  str_to_type,
54
54
  )
55
- from .cast import as_builtin
55
+ from .cast import as_builtin, register_as_builtin_fn
56
56
  from .checksum import checksum_any, register_checksum_fn
57
57
  from .collections import (
58
58
  all_eq,
@@ -151,6 +151,7 @@ from .re import (
151
151
  PatternLike,
152
152
  PatternListLike,
153
153
  compile_patterns,
154
+ filter_with_patterns,
154
155
  find_patterns,
155
156
  get_key_fn,
156
157
  match_patterns,
@@ -4,6 +4,7 @@
4
4
  from argparse import Namespace
5
5
  from collections import Counter
6
6
  from dataclasses import asdict
7
+ from datetime import date
7
8
  from enum import Enum
8
9
  from functools import partial
9
10
  from pathlib import Path
@@ -81,105 +82,128 @@ def register_as_builtin_fn(
81
82
 
82
83
 
83
84
  _AS_BUILTIN_REGISTRY.register(
84
- identity, custom_predicate=partial(is_builtin_scalar, strict=True)
85
+ identity,
86
+ custom_predicate=partial(is_builtin_scalar, strict=True),
85
87
  )
86
88
 
87
89
 
88
90
  @register_as_builtin_fn(Counter)
89
- def _counter_to_builtin(x: Counter) -> Dict[Any, int]:
91
+ def _counter_to_builtin(x: Counter, **kwargs) -> Dict[Any, int]:
90
92
  return dict(x)
91
93
 
92
94
 
95
+ @register_as_builtin_fn(date)
96
+ def _date_to_builtin(x: date, **kwargs) -> str:
97
+ return str(x)
98
+
99
+
93
100
  @register_as_builtin_fn(Path)
94
- def _path_to_builtin(x: Path) -> str:
101
+ def _path_to_builtin(x: Path, **kwargs) -> str:
95
102
  return str(x)
96
103
 
97
104
 
98
105
  @register_as_builtin_fn(Enum)
99
- def _enum_to_builtin(x: Enum) -> str:
106
+ def _enum_to_builtin(x: Enum, **kwargs) -> str:
100
107
  return x.name
101
108
 
102
109
 
103
110
  @register_as_builtin_fn(Pattern)
104
- def _pattern_to_builtin(x: Pattern) -> str:
111
+ def _pattern_to_builtin(x: Pattern, **kwargs) -> str:
105
112
  return x.pattern
106
113
 
107
114
 
108
115
  @register_as_builtin_fn(Namespace)
109
- def _namespace_to_builtin(x: Namespace) -> Any:
110
- return as_builtin(x.__dict__)
116
+ def _namespace_to_builtin(x: Namespace, **kwargs) -> Any:
117
+ return as_builtin(x.__dict__, **kwargs)
111
118
 
112
119
 
113
120
  @register_as_builtin_fn(DataclassInstance)
114
- def _dataclass_to_builtin(x: DataclassInstance) -> Any:
115
- return as_builtin(asdict(x))
121
+ def _dataclass_to_builtin(x: DataclassInstance, **kwargs) -> Any:
122
+ return as_builtin(asdict(x), **kwargs)
116
123
 
117
124
 
118
125
  @register_as_builtin_fn(NamedTupleInstance)
119
- def _namedtuple_to_builtin(x: NamedTupleInstance) -> Any:
120
- return as_builtin(x._asdict())
126
+ def _namedtuple_to_builtin(x: NamedTupleInstance, **kwargs) -> Any:
127
+ return as_builtin(x._asdict(), **kwargs)
121
128
 
122
129
 
123
130
  @register_as_builtin_fn(Mapping, priority=-100)
124
- def _mapping_to_builtin(x: Mapping) -> Any:
125
- return {as_builtin(k): as_builtin(v) for k, v in x.items()}
131
+ def _mapping_to_builtin(x: Mapping, **kwargs) -> Any:
132
+ return {as_builtin(k, **kwargs): as_builtin(v, **kwargs) for k, v in x.items()}
126
133
 
127
134
 
128
135
  @register_as_builtin_fn(Iterable, priority=-200)
129
- def _iterable_to_builtin(x: Iterable) -> Any:
130
- return [as_builtin(xi) for xi in x]
136
+ def _iterable_to_builtin(x: Iterable, **kwargs) -> Any:
137
+ return [as_builtin(xi, **kwargs) for xi in x]
131
138
 
132
139
 
133
140
  @overload
134
- def as_builtin(x: Counter) -> Dict[Any, int]: ...
141
+ def as_builtin(x: Counter, **kwargs) -> Dict[Any, int]: ...
135
142
 
136
143
 
137
144
  @overload
138
- def as_builtin(x: Enum) -> str: ...
145
+ def as_builtin(x: date, **kwargs) -> str: ...
139
146
 
140
147
 
141
148
  @overload
142
- def as_builtin(x: Path) -> str: ...
149
+ def as_builtin(x: Enum, **kwargs) -> str: ...
143
150
 
144
151
 
145
152
  @overload
146
- def as_builtin(x: Pattern) -> str: ...
153
+ def as_builtin(x: Path, **kwargs) -> str: ...
147
154
 
148
155
 
149
156
  @overload
150
- def as_builtin(x: Namespace) -> Dict[str, Any]: ...
157
+ def as_builtin(x: Pattern, **kwargs) -> str: ...
151
158
 
152
159
 
153
160
  @overload
154
- def as_builtin(x: Mapping[K, V]) -> Dict[K, V]: ...
161
+ def as_builtin(x: Namespace, **kwargs) -> Dict[str, Any]: ...
155
162
 
156
163
 
157
164
  @overload
158
- def as_builtin(x: DataclassInstance) -> Dict[str, Any]: ...
165
+ def as_builtin(x: Mapping[K, V], **kwargs) -> Dict[K, V]: ...
159
166
 
160
167
 
161
168
  @overload
162
- def as_builtin(x: NamedTupleInstance) -> Dict[str, Any]: ...
169
+ def as_builtin(x: DataclassInstance, **kwargs) -> Dict[str, Any]: ...
163
170
 
164
171
 
165
172
  @overload
166
- def as_builtin(x: T_BuiltinScalar) -> T_BuiltinScalar: ...
173
+ def as_builtin(x: NamedTupleInstance, **kwargs) -> Dict[str, Any]: ...
167
174
 
168
175
 
169
176
  @overload
170
- def as_builtin(x: Any) -> Any: ...
177
+ def as_builtin(x: T_BuiltinScalar, **kwargs) -> T_BuiltinScalar: ...
171
178
 
172
179
 
173
- def as_builtin(x: Any) -> Any:
180
+ @overload
181
+ def as_builtin(x: Any, **kwargs) -> Any: ...
182
+
183
+
184
+ def as_builtin(x: Any, **kwargs) -> Any:
174
185
  """Convert an object to a sanitized python builtin equivalent.
175
186
 
176
187
  This function can be used to sanitize data before saving to a JSON, YAML or CSV file.
177
188
 
178
189
  Additional objects to convert can be added dynamically with `pythonwrench.register_as_builtin_fn` function decorator.
179
190
 
191
+ Here is the list of default objects converted to built-in:
192
+ - tuple -> list
193
+ - collections.Counter -> dict
194
+ - datetime.date -> str
195
+ - argparse.Namespace -> dict
196
+ - re.Pattern -> str
197
+ - pathlib.Path -> str
198
+ - enum.Enum -> str
199
+ - Mapping -> dict
200
+ - Iterable -> list
201
+ - Dataclass -> dict
202
+ - NamedTuple -> dict
203
+
180
204
  Note: By default, tuple objects are converted to list.
181
205
 
182
206
  Args:
183
207
  x: Object to convert to built-in equivalent.
184
208
  """
185
- return _AS_BUILTIN_REGISTRY.apply(x)
209
+ return _AS_BUILTIN_REGISTRY.apply(x, **kwargs)
@@ -34,6 +34,7 @@ from .reducers import (
34
34
  prod,
35
35
  reduce_add,
36
36
  reduce_and,
37
+ reduce_matmul,
37
38
  reduce_mul,
38
39
  reduce_or,
39
40
  sum,
@@ -19,6 +19,7 @@ from pythonwrench.typing.checks import isinstance_generic
19
19
  from pythonwrench.typing.classes import (
20
20
  SupportsAdd,
21
21
  SupportsAnd,
22
+ SupportsMatmul,
22
23
  SupportsMul,
23
24
  SupportsOr,
24
25
  )
@@ -28,6 +29,7 @@ T_SupportsAdd = TypeVar("T_SupportsAdd", bound=SupportsAdd)
28
29
  T_SupportsAnd = TypeVar("T_SupportsAnd", bound=SupportsAnd)
29
30
  T_SupportsMul = TypeVar("T_SupportsMul", bound=SupportsMul)
30
31
  T_SupportsOr = TypeVar("T_SupportsOr", bound=SupportsOr)
32
+ T_SupportsMatmul = TypeVar("T_SupportsMatmul", bound=SupportsMatmul)
31
33
 
32
34
 
33
35
  @overload
@@ -90,6 +92,36 @@ def reduce_and(*args, start=None):
90
92
  return _reduce(*args, start=start, op_fn=operator.and_, type_=SupportsAnd)
91
93
 
92
94
 
95
+ @overload
96
+ def reduce_matmul(
97
+ args: Iterable[T_SupportsMatmul],
98
+ /,
99
+ *,
100
+ start: T_SupportsMatmul,
101
+ ) -> T_SupportsMatmul: ...
102
+
103
+
104
+ @overload
105
+ def reduce_matmul(
106
+ *args: T_SupportsMatmul,
107
+ start: T_SupportsMatmul,
108
+ ) -> T_SupportsMatmul: ...
109
+
110
+
111
+ @overload
112
+ def reduce_matmul(
113
+ arg0: T_SupportsMatmul,
114
+ /,
115
+ *args: T_SupportsMatmul,
116
+ start: Optional[T_SupportsMatmul] = None,
117
+ ) -> T_SupportsMatmul: ...
118
+
119
+
120
+ def reduce_matmul(*args, start=None):
121
+ """Reduce elements using "mul" operator (*)."""
122
+ return _reduce(*args, start=start, op_fn=operator.matmul, type_=SupportsMatmul)
123
+
124
+
93
125
  @overload
94
126
  def reduce_mul(
95
127
  args: Iterable[T_SupportsMul],
@@ -168,14 +200,15 @@ def _reduce(
168
200
 
169
201
  if isinstance(start, type_):
170
202
  accumulator = start
171
- elif start is None:
203
+ elif start is None or start is ...:
172
204
  try:
173
205
  accumulator = next(it)
174
206
  except StopIteration:
175
- msg = f"Invalid combinaison of arguments {args=} and {start=}. (expected at least 1 non-empty argument or start that supports or operator.)"
207
+ msg = f"Invalid combinaison of arguments {args=} and {start=}. (expected at least 1 non-empty argument or start object that supports operator.)"
176
208
  raise ValueError(msg)
177
209
  else:
178
- raise TypeError(f"Invalid argument type {type(start)}.")
210
+ msg = f"Invalid argument type {type(start)}."
211
+ raise TypeError(msg)
179
212
 
180
213
  for arg in it:
181
214
  accumulator = op_fn(accumulator, arg)
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import logging
5
+ from concurrent.futures import Future, ThreadPoolExecutor
6
+ from typing import Callable, Generic, List, Optional, TypeVar
7
+
8
+ from typing_extensions import ParamSpec
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ P = ParamSpec("P")
14
+ T = TypeVar("T")
15
+
16
+
17
+ class ThreadPoolExecutorHelper(Generic[P, T]):
18
+ def __init__(self, fn: Callable[P, T], **default_kwargs) -> None:
19
+ super().__init__()
20
+ self.fn = fn
21
+ self.default_kwargs = default_kwargs
22
+ self.executor: Optional[ThreadPoolExecutor] = None
23
+ self.futures: list[Future[T]] = []
24
+
25
+ def submit(self, *args: P.args, **kwargs: P.kwargs) -> Future[T]:
26
+ if self.executor is None:
27
+ self.executor = ThreadPoolExecutor()
28
+
29
+ kwargs = self.default_kwargs | kwargs # type: ignore
30
+ future = self.executor.submit(self.fn, *args, **kwargs)
31
+ self.futures.append(future)
32
+ return future
33
+
34
+ def wait_all(self, shutdown: bool = True, verbose: bool = True) -> List[T]:
35
+ futures = self.futures
36
+ if verbose:
37
+ try:
38
+ import tqdm # type: ignore
39
+
40
+ futures = tqdm.tqdm(futures, disable=not verbose)
41
+ except ImportError:
42
+ logger.warning(
43
+ "Cannot display verbose bar because tqdm is not installed."
44
+ )
45
+
46
+ results = [future.result() for future in futures]
47
+ self.futures.clear()
48
+ if shutdown and self.executor is not None:
49
+ self.executor.shutdown()
50
+ self.executor = None
51
+ return results
@@ -2,11 +2,13 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  from dataclasses import MISSING
5
- from typing import Any, Dict
5
+ from typing import Any, Dict, TypeVar
6
6
 
7
7
  from pythonwrench.typing.checks import is_dataclass_instance # noqa: F401
8
8
  from pythonwrench.typing.classes import DataclassInstance
9
9
 
10
+ T = TypeVar("T")
11
+
10
12
 
11
13
  def get_defaults_values(obj: DataclassInstance) -> Dict[str, Any]:
12
14
  defaults = {}