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.
- {pythonwrench-0.4.2/src/pythonwrench.egg-info → pythonwrench-0.4.3}/PKG-INFO +55 -51
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/README.md +46 -47
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/pyproject.toml +6 -2
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/__init__.py +4 -3
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/cast.py +51 -27
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/__init__.py +1 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/reducers.py +36 -3
- pythonwrench-0.4.3/src/pythonwrench/concurrent.py +51 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/dataclasses.py +3 -1
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/entries.py +33 -2
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/functools.py +2 -2
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/os.py +73 -32
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/pickle.py +4 -4
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/re.py +18 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/typing/__init__.py +4 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/typing/checks.py +18 -2
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/typing/classes.py +41 -1
- {pythonwrench-0.4.2 → pythonwrench-0.4.3/src/pythonwrench.egg-info}/PKG-INFO +55 -51
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/SOURCES.txt +1 -1
- pythonwrench-0.4.2/docs/requirements.txt +0 -3
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/LICENSE +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/setup.cfg +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/setup.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/_core.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/argparse.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/checksum.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/collections.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/disk_cache.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/jsonl.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/logging.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/semver.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench/warnings.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/entry_points.txt +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/requires.txt +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/src/pythonwrench.egg-info/top_level.txt +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_abc.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_argparse.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_cast.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_checksum.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_collections.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_csv.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_difflib.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_disk_cache.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_entries.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_enum.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_functools.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_importlib.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_inspect.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_json.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_logging.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_math.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_os.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_random.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_readme.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_semver.py +0 -0
- {pythonwrench-0.4.2 → pythonwrench-0.4.3}/tests/test_typing.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
4
|
-
Summary:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
78
|
+
With uv:
|
|
74
79
|
```bash
|
|
75
|
-
|
|
80
|
+
uv add pythonwrench
|
|
76
81
|
```
|
|
77
82
|
|
|
78
|
-
With
|
|
83
|
+
With pip:
|
|
79
84
|
```bash
|
|
80
|
-
|
|
85
|
+
pip install pythonwrench
|
|
81
86
|
```
|
|
82
87
|
|
|
83
|
-
This library has been tested on all Python versions **3.8 - 3.
|
|
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
|
-
|
|
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
|
|
22
|
+
With uv:
|
|
23
23
|
```bash
|
|
24
|
-
|
|
24
|
+
uv add pythonwrench
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
With
|
|
27
|
+
With pip:
|
|
28
28
|
```bash
|
|
29
|
-
|
|
29
|
+
pip install pythonwrench
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This library has been tested on all Python versions **3.8 - 3.
|
|
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 = "
|
|
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
|
-
"""
|
|
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.
|
|
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,
|
|
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:
|
|
145
|
+
def as_builtin(x: date, **kwargs) -> str: ...
|
|
139
146
|
|
|
140
147
|
|
|
141
148
|
@overload
|
|
142
|
-
def as_builtin(x:
|
|
149
|
+
def as_builtin(x: Enum, **kwargs) -> str: ...
|
|
143
150
|
|
|
144
151
|
|
|
145
152
|
@overload
|
|
146
|
-
def as_builtin(x:
|
|
153
|
+
def as_builtin(x: Path, **kwargs) -> str: ...
|
|
147
154
|
|
|
148
155
|
|
|
149
156
|
@overload
|
|
150
|
-
def as_builtin(x:
|
|
157
|
+
def as_builtin(x: Pattern, **kwargs) -> str: ...
|
|
151
158
|
|
|
152
159
|
|
|
153
160
|
@overload
|
|
154
|
-
def as_builtin(x:
|
|
161
|
+
def as_builtin(x: Namespace, **kwargs) -> Dict[str, Any]: ...
|
|
155
162
|
|
|
156
163
|
|
|
157
164
|
@overload
|
|
158
|
-
def as_builtin(x:
|
|
165
|
+
def as_builtin(x: Mapping[K, V], **kwargs) -> Dict[K, V]: ...
|
|
159
166
|
|
|
160
167
|
|
|
161
168
|
@overload
|
|
162
|
-
def as_builtin(x:
|
|
169
|
+
def as_builtin(x: DataclassInstance, **kwargs) -> Dict[str, Any]: ...
|
|
163
170
|
|
|
164
171
|
|
|
165
172
|
@overload
|
|
166
|
-
def as_builtin(x:
|
|
173
|
+
def as_builtin(x: NamedTupleInstance, **kwargs) -> Dict[str, Any]: ...
|
|
167
174
|
|
|
168
175
|
|
|
169
176
|
@overload
|
|
170
|
-
def as_builtin(x:
|
|
177
|
+
def as_builtin(x: T_BuiltinScalar, **kwargs) -> T_BuiltinScalar: ...
|
|
171
178
|
|
|
172
179
|
|
|
173
|
-
|
|
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)
|
|
@@ -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
|
|
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
|
-
|
|
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 = {}
|