pythonwrench 0.1.0__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 (63) hide show
  1. pythonwrench-0.1.0/LICENSE +21 -0
  2. pythonwrench-0.1.0/PKG-INFO +198 -0
  3. pythonwrench-0.1.0/README.md +148 -0
  4. pythonwrench-0.1.0/pyproject.toml +87 -0
  5. pythonwrench-0.1.0/setup.cfg +4 -0
  6. pythonwrench-0.1.0/setup.py +7 -0
  7. pythonwrench-0.1.0/src/pythonwrench/__init__.py +174 -0
  8. pythonwrench-0.1.0/src/pythonwrench/__main__.py +7 -0
  9. pythonwrench-0.1.0/src/pythonwrench/_core.py +42 -0
  10. pythonwrench-0.1.0/src/pythonwrench/abc.py +28 -0
  11. pythonwrench-0.1.0/src/pythonwrench/argparse.py +108 -0
  12. pythonwrench-0.1.0/src/pythonwrench/checksum.py +353 -0
  13. pythonwrench-0.1.0/src/pythonwrench/collections/__init__.py +40 -0
  14. pythonwrench-0.1.0/src/pythonwrench/collections/collections.py +725 -0
  15. pythonwrench-0.1.0/src/pythonwrench/collections/prop.py +99 -0
  16. pythonwrench-0.1.0/src/pythonwrench/collections/reducers.py +244 -0
  17. pythonwrench-0.1.0/src/pythonwrench/csv.py +239 -0
  18. pythonwrench-0.1.0/src/pythonwrench/dataclasses.py +23 -0
  19. pythonwrench-0.1.0/src/pythonwrench/datetime.py +15 -0
  20. pythonwrench-0.1.0/src/pythonwrench/difflib.py +34 -0
  21. pythonwrench-0.1.0/src/pythonwrench/entries.py +184 -0
  22. pythonwrench-0.1.0/src/pythonwrench/enum.py +44 -0
  23. pythonwrench-0.1.0/src/pythonwrench/functools.py +300 -0
  24. pythonwrench-0.1.0/src/pythonwrench/hashlib.py +72 -0
  25. pythonwrench-0.1.0/src/pythonwrench/importlib.py +153 -0
  26. pythonwrench-0.1.0/src/pythonwrench/inspect.py +74 -0
  27. pythonwrench-0.1.0/src/pythonwrench/io.py +31 -0
  28. pythonwrench-0.1.0/src/pythonwrench/json.py +42 -0
  29. pythonwrench-0.1.0/src/pythonwrench/logging.py +244 -0
  30. pythonwrench-0.1.0/src/pythonwrench/math.py +55 -0
  31. pythonwrench-0.1.0/src/pythonwrench/os.py +183 -0
  32. pythonwrench-0.1.0/src/pythonwrench/pickle.py +33 -0
  33. pythonwrench-0.1.0/src/pythonwrench/random.py +13 -0
  34. pythonwrench-0.1.0/src/pythonwrench/re.py +100 -0
  35. pythonwrench-0.1.0/src/pythonwrench/semver.py +356 -0
  36. pythonwrench-0.1.0/src/pythonwrench/typing/__init__.py +38 -0
  37. pythonwrench-0.1.0/src/pythonwrench/typing/checks.py +300 -0
  38. pythonwrench-0.1.0/src/pythonwrench/typing/classes.py +108 -0
  39. pythonwrench-0.1.0/src/pythonwrench/warnings.py +70 -0
  40. pythonwrench-0.1.0/src/pythonwrench.egg-info/PKG-INFO +198 -0
  41. pythonwrench-0.1.0/src/pythonwrench.egg-info/SOURCES.txt +61 -0
  42. pythonwrench-0.1.0/src/pythonwrench.egg-info/dependency_links.txt +1 -0
  43. pythonwrench-0.1.0/src/pythonwrench.egg-info/entry_points.txt +4 -0
  44. pythonwrench-0.1.0/src/pythonwrench.egg-info/requires.txt +1 -0
  45. pythonwrench-0.1.0/src/pythonwrench.egg-info/top_level.txt +1 -0
  46. pythonwrench-0.1.0/tests/test_abc.py +37 -0
  47. pythonwrench-0.1.0/tests/test_argparse.py +39 -0
  48. pythonwrench-0.1.0/tests/test_checksum.py +52 -0
  49. pythonwrench-0.1.0/tests/test_collections.py +282 -0
  50. pythonwrench-0.1.0/tests/test_csv.py +25 -0
  51. pythonwrench-0.1.0/tests/test_dataclasses.py +27 -0
  52. pythonwrench-0.1.0/tests/test_difflib.py +28 -0
  53. pythonwrench-0.1.0/tests/test_entries.py +41 -0
  54. pythonwrench-0.1.0/tests/test_functools.py +26 -0
  55. pythonwrench-0.1.0/tests/test_hashlib.py +28 -0
  56. pythonwrench-0.1.0/tests/test_importlib.py +27 -0
  57. pythonwrench-0.1.0/tests/test_inspect.py +48 -0
  58. pythonwrench-0.1.0/tests/test_logging.py +29 -0
  59. pythonwrench-0.1.0/tests/test_math.py +24 -0
  60. pythonwrench-0.1.0/tests/test_os.py +25 -0
  61. pythonwrench-0.1.0/tests/test_readme.py +77 -0
  62. pythonwrench-0.1.0/tests/test_semver.py +135 -0
  63. pythonwrench-0.1.0/tests/test_typing_guards.py +265 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Labbeti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: pythonwrench
3
+ Version: 0.1.0
4
+ Summary: Set of tools for Python that could be in the standard library.
5
+ Author-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
6
+ Maintainer-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2025 Labbeti
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: Homepage, https://pypi.org/project/pythonwrench/
30
+ Project-URL: Documentation, https://pythonwrench.readthedocs.io/
31
+ Project-URL: Repository, https://github.com/Labbeti/pythonwrench.git
32
+ Project-URL: Changelog, https://github.com/Labbeti/pythonwrench/blob/main/CHANGELOG.md
33
+ Project-URL: Tracker, https://github.com/Labbeti/pythonwrench/issues
34
+ Keywords: python,tools,utilities
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Intended Audience :: Science/Research
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Scientific/Engineering
44
+ Classifier: Operating System :: OS Independent
45
+ Requires-Python: >=3.9
46
+ Description-Content-Type: text/markdown
47
+ License-File: LICENSE
48
+ Requires-Dist: typing-extensions>=4.10.0
49
+ Dynamic: license-file
50
+
51
+ # pythonwrench
52
+
53
+ <center>
54
+
55
+ <a href="https://www.python.org/">
56
+ <img alt="Python" src="https://img.shields.io/badge/-Python 3.9+-blue?style=for-the-badge&logo=python&logoColor=white">
57
+ </a>
58
+ <a href="https://github.com/Labbeti/pythonwrench/actions">
59
+ <img alt="Build" src="https://img.shields.io/github/actions/workflow/status/Labbeti/pythonwrench/test.yaml?branch=main&style=for-the-badge&logo=github">
60
+ </a>
61
+ <a href='https://pythonwrench.readthedocs.io/en/stable/?badge=stable'>
62
+ <img src='https://readthedocs.org/projects/pythonwrench/badge/?version=stable&style=for-the-badge' alt='Documentation Status' />
63
+ </a>
64
+
65
+ Set of tools for Python that could be in the standard library.
66
+
67
+ </center>
68
+
69
+
70
+ ## Installation
71
+
72
+ With pip:
73
+ ```bash
74
+ pip install pythonwrench
75
+ ```
76
+
77
+ With uv:
78
+ ```bash
79
+ uv add pythonwrench
80
+ ```
81
+
82
+ This library works on all Python versions **>=3.9**, requires only `typing_extensions>=4.10.0`, and runs on **Linux, Mac and Windows** systems.
83
+
84
+ ## Examples
85
+
86
+ ### Collections
87
+
88
+ Provides functions to facilitate iterables processing, like `unzip` :
89
+
90
+ ```python
91
+ >>> import pythonwrench as pw
92
+ >>>
93
+ >>> list_of_tuples = [(1, "a"), (2, "b"), (3, "c"), (4, "d")]
94
+ >>> pw.unzip(list_of_tuples)
95
+ ... [1, 2, 3, 4], ["a", "b", "c", "d"]
96
+ >>> pw.flatten(list_of_tuples)
97
+ ... [1, "a", 2, "b", 3, "c", 4, "d"]
98
+ ```
99
+
100
+ ... or mathematical functions like `prod` or `argmax` :
101
+
102
+ ```python
103
+ >>> import pythonwrench as pw
104
+ >>>
105
+ >>> values = [3, 1, 6, 4]
106
+ >>> pw.prod(values)
107
+ ... 72
108
+ >>> pw.argmax(values)
109
+ ... 2
110
+ >>> pw.is_sorted(values)
111
+ ... False
112
+ ```
113
+
114
+ Easely converts common python structures like list of dicts to dict of lists :
115
+
116
+ ```python
117
+ >>> import pythonwrench as pw
118
+ >>>
119
+ >>> list_of_dicts = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
120
+ >>> pw.list_dict_to_dict_list(list_of_dicts)
121
+ ... {"a": [1, 3], "b": [2, 4]}
122
+ ```
123
+
124
+ ... or dict of dicts :
125
+ ```python
126
+ >>> import pythonwrench as pw
127
+ >>>
128
+ >>> dict_of_dicts = {"a": {"x": 1, "y": 2}, "b": {"x": 3, "y": 4}}
129
+ >>> pw.flat_dict_of_dict(dict_of_dicts)
130
+ ... {"a.x": 1, "a.y": 2, "b.x": 3, "b.y": 4}
131
+ ```
132
+
133
+ ### Typing
134
+
135
+ ```python
136
+ >>> import pythonwrench as pw
137
+ >>>
138
+ >>> # Behaves like builtin isinstance() :
139
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict)
140
+ ... True
141
+ >>> # But works with generic types !
142
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, int])
143
+ ... True
144
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, str])
145
+ ... False
146
+ ```
147
+
148
+ ```python
149
+ >>> import pythonwrench as pw
150
+ >>>
151
+ >>> # Combines Iterable and Sized !
152
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsGetitemLen)
153
+ ... True
154
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsIterLen)
155
+ ... True
156
+ ```
157
+
158
+ ### Disk caching (memoize)
159
+
160
+ ```python
161
+ >>> import pythonwrench as pw
162
+ >>>
163
+ >>> @pw.disk_cache_decorator
164
+ >>> def heavy_processing():
165
+ >>> # Lot of stuff here
166
+ >>> ...
167
+ >>>
168
+ >>> data1 = heavy_processing() # first call function is called and the result is stored on disk
169
+ >>> data2 = heavy_processing() # second call result is loaded from disk directly
170
+ ```
171
+
172
+ ### Semantic versionning parsing
173
+
174
+ ```python
175
+ >>> import pythonwrench as pw
176
+ >>> version = pw.Version("1.12.2")
177
+ >>> version.to_tuple()
178
+ ... (1, 12, 2)
179
+ >>> version = pw.Version("0.5.1-beta+linux")
180
+ >>> version.to_tuple()
181
+ ... (0, 5, 1, "beta", "linux")
182
+ ```
183
+
184
+ ### Serialization
185
+
186
+ ```python
187
+ >>> import pythonwrench as pw
188
+ >>>
189
+ >>> list_of_dicts = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
190
+ >>> pw.dump_csv(list_of_dicts, "data.csv")
191
+ >>> pw.dump_json(list_of_dicts, "data.json")
192
+ >>> pw.load_json("data.json") == list_of_dicts
193
+ ... True
194
+ ```
195
+
196
+ ## Contact
197
+ Maintainer:
198
+ - [Étienne Labbé](https://labbeti.github.io/) "Labbeti": labbeti.pub@gmail.com
@@ -0,0 +1,148 @@
1
+ # pythonwrench
2
+
3
+ <center>
4
+
5
+ <a href="https://www.python.org/">
6
+ <img alt="Python" src="https://img.shields.io/badge/-Python 3.9+-blue?style=for-the-badge&logo=python&logoColor=white">
7
+ </a>
8
+ <a href="https://github.com/Labbeti/pythonwrench/actions">
9
+ <img alt="Build" src="https://img.shields.io/github/actions/workflow/status/Labbeti/pythonwrench/test.yaml?branch=main&style=for-the-badge&logo=github">
10
+ </a>
11
+ <a href='https://pythonwrench.readthedocs.io/en/stable/?badge=stable'>
12
+ <img src='https://readthedocs.org/projects/pythonwrench/badge/?version=stable&style=for-the-badge' alt='Documentation Status' />
13
+ </a>
14
+
15
+ Set of tools for Python that could be in the standard library.
16
+
17
+ </center>
18
+
19
+
20
+ ## Installation
21
+
22
+ With pip:
23
+ ```bash
24
+ pip install pythonwrench
25
+ ```
26
+
27
+ With uv:
28
+ ```bash
29
+ uv add pythonwrench
30
+ ```
31
+
32
+ This library works on all Python versions **>=3.9**, requires only `typing_extensions>=4.10.0`, and runs on **Linux, Mac and Windows** systems.
33
+
34
+ ## Examples
35
+
36
+ ### Collections
37
+
38
+ Provides functions to facilitate iterables processing, like `unzip` :
39
+
40
+ ```python
41
+ >>> import pythonwrench as pw
42
+ >>>
43
+ >>> list_of_tuples = [(1, "a"), (2, "b"), (3, "c"), (4, "d")]
44
+ >>> pw.unzip(list_of_tuples)
45
+ ... [1, 2, 3, 4], ["a", "b", "c", "d"]
46
+ >>> pw.flatten(list_of_tuples)
47
+ ... [1, "a", 2, "b", 3, "c", 4, "d"]
48
+ ```
49
+
50
+ ... or mathematical functions like `prod` or `argmax` :
51
+
52
+ ```python
53
+ >>> import pythonwrench as pw
54
+ >>>
55
+ >>> values = [3, 1, 6, 4]
56
+ >>> pw.prod(values)
57
+ ... 72
58
+ >>> pw.argmax(values)
59
+ ... 2
60
+ >>> pw.is_sorted(values)
61
+ ... False
62
+ ```
63
+
64
+ Easely converts common python structures like list of dicts to dict of lists :
65
+
66
+ ```python
67
+ >>> import pythonwrench as pw
68
+ >>>
69
+ >>> list_of_dicts = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
70
+ >>> pw.list_dict_to_dict_list(list_of_dicts)
71
+ ... {"a": [1, 3], "b": [2, 4]}
72
+ ```
73
+
74
+ ... or dict of dicts :
75
+ ```python
76
+ >>> import pythonwrench as pw
77
+ >>>
78
+ >>> dict_of_dicts = {"a": {"x": 1, "y": 2}, "b": {"x": 3, "y": 4}}
79
+ >>> pw.flat_dict_of_dict(dict_of_dicts)
80
+ ... {"a.x": 1, "a.y": 2, "b.x": 3, "b.y": 4}
81
+ ```
82
+
83
+ ### Typing
84
+
85
+ ```python
86
+ >>> import pythonwrench as pw
87
+ >>>
88
+ >>> # Behaves like builtin isinstance() :
89
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict)
90
+ ... True
91
+ >>> # But works with generic types !
92
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, int])
93
+ ... True
94
+ >>> pw.isinstance_generic({"a": 1, "b": 2}, dict[str, str])
95
+ ... False
96
+ ```
97
+
98
+ ```python
99
+ >>> import pythonwrench as pw
100
+ >>>
101
+ >>> # Combines Iterable and Sized !
102
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsGetitemLen)
103
+ ... True
104
+ >>> isinstance({"a": 1, "b": 2}, pw.SupportsIterLen)
105
+ ... True
106
+ ```
107
+
108
+ ### Disk caching (memoize)
109
+
110
+ ```python
111
+ >>> import pythonwrench as pw
112
+ >>>
113
+ >>> @pw.disk_cache_decorator
114
+ >>> def heavy_processing():
115
+ >>> # Lot of stuff here
116
+ >>> ...
117
+ >>>
118
+ >>> data1 = heavy_processing() # first call function is called and the result is stored on disk
119
+ >>> data2 = heavy_processing() # second call result is loaded from disk directly
120
+ ```
121
+
122
+ ### Semantic versionning parsing
123
+
124
+ ```python
125
+ >>> import pythonwrench as pw
126
+ >>> version = pw.Version("1.12.2")
127
+ >>> version.to_tuple()
128
+ ... (1, 12, 2)
129
+ >>> version = pw.Version("0.5.1-beta+linux")
130
+ >>> version.to_tuple()
131
+ ... (0, 5, 1, "beta", "linux")
132
+ ```
133
+
134
+ ### Serialization
135
+
136
+ ```python
137
+ >>> import pythonwrench as pw
138
+ >>>
139
+ >>> list_of_dicts = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
140
+ >>> pw.dump_csv(list_of_dicts, "data.csv")
141
+ >>> pw.dump_json(list_of_dicts, "data.json")
142
+ >>> pw.load_json("data.json") == list_of_dicts
143
+ ... True
144
+ ```
145
+
146
+ ## Contact
147
+ Maintainer:
148
+ - [Étienne Labbé](https://labbeti.github.io/) "Labbeti": labbeti.pub@gmail.com
@@ -0,0 +1,87 @@
1
+ [project]
2
+ name = "pythonwrench"
3
+ authors = [
4
+ {name = "Étienne Labbé (Labbeti)", email = "labbeti.pub@gmail.com"},
5
+ ]
6
+ description = "Set of tools for Python that could be in the standard library."
7
+ readme = "README.md"
8
+ requires-python = ">=3.9"
9
+ keywords = ["python", "tools", "utilities"]
10
+ license = {file = "LICENSE"}
11
+ classifiers = [
12
+ "Intended Audience :: Developers",
13
+ "Intended Audience :: Science/Research",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.9",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Scientific/Engineering",
21
+ "Operating System :: OS Independent",
22
+ ]
23
+ maintainers = [
24
+ {name = "Étienne Labbé (Labbeti)", email = "labbeti.pub@gmail.com"},
25
+ ]
26
+ dynamic = ["version"]
27
+ dependencies = [
28
+ "typing-extensions>=4.10.0",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://pypi.org/project/pythonwrench/"
33
+ Documentation = "https://pythonwrench.readthedocs.io/"
34
+ Repository = "https://github.com/Labbeti/pythonwrench.git"
35
+ Changelog = "https://github.com/Labbeti/pythonwrench/blob/main/CHANGELOG.md"
36
+ Tracker = "https://github.com/Labbeti/pythonwrench/issues"
37
+
38
+ [project.scripts]
39
+ pythonwrench-info = "pythonwrench.entries:print_install_info"
40
+ pythonwrench-tree = "pythonwrench.entries:main_tree"
41
+ pythonwrench-safe-rmdir = "pythonwrench.entries:main_safe_rmdir"
42
+
43
+ [build-system]
44
+ requires = ["setuptools >= 61.0"]
45
+ build-backend = "setuptools.build_meta"
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"] # list of folders that contain the packages (["."] by default)
49
+ # package names should match these glob patterns (["*"] by default)
50
+ # note: star after package names is required to work
51
+ include = ["pythonwrench*"]
52
+
53
+ [tool.setuptools.dynamic]
54
+ version = {attr = "pythonwrench.__version__"}
55
+
56
+ [tool.pyright]
57
+ include = ["src"]
58
+ exclude = ["**/__pycache__"]
59
+ pythonVersion = "3.9"
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+
64
+ [tool.coverage.run]
65
+ source = ["src"]
66
+
67
+ [tool.ruff.lint.extend-per-file-ignores]
68
+ "__init__.py" = ["F401", "F403"]
69
+
70
+ [tool.uv.sources]
71
+ pythonwrench = { workspace = true }
72
+
73
+ [dependency-groups]
74
+ dev = [
75
+ "coverage[toml]>=7.9.1",
76
+ "flake8>=7.2.0",
77
+ "ipykernel>=6.29.5",
78
+ "ipython>=8.18.1",
79
+ "pre-commit>=4.2.0",
80
+ "pytest>=8.4.1",
81
+ "pythonwrench",
82
+ "ruff>=0.12.0",
83
+ "setuptools>=80.9.0",
84
+ "sphinx>=7.4.7",
85
+ "sphinx-press-theme>=0.8.0",
86
+ "twine>=6.1.0",
87
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from setuptools import setup
5
+
6
+ if __name__ == "__main__":
7
+ setup()
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """Set of tools for Python that could be in the standard library."""
5
+
6
+ __name__ = "pythonwrench"
7
+ __author__ = "Étienne Labbé (Labbeti)"
8
+ __author_email__ = "labbeti.pub@gmail.com"
9
+ __license__ = "MIT"
10
+ __maintainer__ = "Étienne Labbé (Labbeti)"
11
+ __status__ = "Development"
12
+ __version__ = "0.1.0"
13
+
14
+
15
+ # Re-import for language servers
16
+ from . import abc as abc
17
+ from . import argparse as argparse
18
+ from . import collections as collections
19
+ from . import csv as csv
20
+ from . import dataclasses as dataclasses
21
+ from . import datetime as datetime
22
+ from . import difflib as difflib
23
+ from . import entries as entries
24
+ from . import enum as enum
25
+ from . import functools as functools
26
+ from . import hashlib as hashlib
27
+ from . import importlib as importlib
28
+ from . import inspect as inspect
29
+ from . import io as io
30
+ from . import json as json
31
+ from . import logging as logging
32
+ from . import math as math
33
+ from . import os as os
34
+ from . import pickle as pickle
35
+ from . import random as random
36
+ from . import re as re
37
+ from . import semver as semver
38
+ from . import warnings as warnings
39
+
40
+ # Global library imports
41
+ from .abc import Singleton
42
+ from .argparse import (
43
+ str_to_bool,
44
+ str_to_optional_bool,
45
+ str_to_optional_float,
46
+ str_to_optional_int,
47
+ str_to_optional_str,
48
+ )
49
+ from .checksum import checksum_any, register_checksum_fn
50
+ from .collections import (
51
+ all_eq,
52
+ all_ne,
53
+ contained,
54
+ dict_list_to_list_dict,
55
+ dump_dict,
56
+ filter_iterable,
57
+ find,
58
+ flat_dict_of_dict,
59
+ flat_list_of_list,
60
+ flatten,
61
+ intersect,
62
+ intersect_lists,
63
+ is_full,
64
+ is_sorted,
65
+ is_unique,
66
+ list_dict_to_dict_list,
67
+ prod,
68
+ recursive_generator,
69
+ reduce_add,
70
+ reduce_and,
71
+ reduce_mul,
72
+ reduce_or,
73
+ shuffled,
74
+ sorted_dict,
75
+ sum,
76
+ unflat_dict_of_dict,
77
+ unflat_list_of_list,
78
+ union,
79
+ union_dicts,
80
+ union_lists,
81
+ unzip,
82
+ )
83
+ from .csv import dump_csv, load_csv
84
+ from .dataclasses import get_defaults_values
85
+ from .datetime import get_now, get_now_iso8601
86
+ from .difflib import find_closest_in_list, sequence_matcher_ratio
87
+ from .enum import StrEnum
88
+ from .functools import (
89
+ Compose,
90
+ compose,
91
+ disk_cache_call,
92
+ disk_cache_decorator,
93
+ filter_and_call,
94
+ function_alias,
95
+ get_argnames,
96
+ identity,
97
+ )
98
+ from .hashlib import hash_file
99
+ from .importlib import (
100
+ is_available_package,
101
+ is_editable_package,
102
+ reload_editable_packages,
103
+ reload_submodules,
104
+ search_submodules,
105
+ )
106
+ from .inspect import get_current_fn_name, get_fullname
107
+ from .json import dump_json, load_json
108
+ from .logging import (
109
+ VERBOSE_DEBUG,
110
+ VERBOSE_ERROR,
111
+ VERBOSE_INFO,
112
+ VERBOSE_WARNING,
113
+ MkdirFileHandler,
114
+ get_current_file_logger,
115
+ get_ipython_name,
116
+ get_null_logger,
117
+ log_once,
118
+ running_on_interpreter,
119
+ running_on_notebook,
120
+ running_on_terminal,
121
+ setup_logging_level,
122
+ setup_logging_verbose,
123
+ )
124
+ from .math import argmax, argmin, argsort, clamp, clip
125
+ from .os import get_num_cpus_available, safe_rmdir, tree_iter
126
+ from .pickle import dump_pickle, load_pickle
127
+ from .random import randstr
128
+ from .re import (
129
+ PatternLike,
130
+ PatternListLike,
131
+ compile_patterns,
132
+ find_patterns,
133
+ get_key_fn,
134
+ match_patterns,
135
+ sort_with_patterns,
136
+ )
137
+ from .semver import Version
138
+ from .typing import (
139
+ BuiltinCollection,
140
+ BuiltinNumber,
141
+ BuiltinScalar,
142
+ DataclassInstance,
143
+ EllipsisType,
144
+ NamedTupleInstance,
145
+ NoneType,
146
+ SupportsAdd,
147
+ SupportsAnd,
148
+ SupportsBool,
149
+ SupportsGetitemIterLen,
150
+ SupportsGetitemLen,
151
+ SupportsIterLen,
152
+ SupportsMul,
153
+ SupportsOr,
154
+ T_BuiltinNumber,
155
+ T_BuiltinScalar,
156
+ is_builtin_number,
157
+ is_builtin_obj,
158
+ is_builtin_scalar,
159
+ is_dataclass_instance,
160
+ is_iterable_bool,
161
+ is_iterable_bytes_or_list,
162
+ is_iterable_float,
163
+ is_iterable_int,
164
+ is_iterable_integral,
165
+ is_iterable_str,
166
+ is_namedtuple_instance,
167
+ is_sequence_str,
168
+ is_typed_dict,
169
+ isinstance_generic,
170
+ )
171
+ from .warnings import deprecated_alias, deprecated_function, warn_once
172
+
173
+ version = __version__
174
+ version_info = Version(__version__)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from .entries import print_install_info
5
+
6
+ if __name__ == "__main__":
7
+ print_install_info()
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from functools import wraps
5
+ from typing import Any, Callable, Optional, TypeVar
6
+
7
+ from typing_extensions import ParamSpec
8
+
9
+ P = ParamSpec("P")
10
+ T = TypeVar("T")
11
+ U = TypeVar("U")
12
+
13
+
14
+ def return_none(*args, **kwargs) -> None:
15
+ """Return None function placeholder."""
16
+ return None
17
+
18
+
19
+ def _decorator_factory(
20
+ inner_fn: Optional[Callable[P, U]],
21
+ *,
22
+ pre_fn: Callable[..., Any] = return_none,
23
+ post_fn: Callable[..., Any] = return_none,
24
+ ) -> Callable[..., Callable[P, U]]:
25
+ """Deprecated decorator for function aliases."""
26
+
27
+ def wrapper_factory(fn: Callable[P, U]) -> Callable[P, U]:
28
+ if inner_fn is None:
29
+ _inner_fn = fn
30
+ else:
31
+ _inner_fn = inner_fn
32
+
33
+ @wraps(_inner_fn)
34
+ def wrapped(*args: P.args, **kwargs: P.kwargs) -> U:
35
+ pre_fn(fn, *args, **kwargs)
36
+ result = _inner_fn(*args, **kwargs)
37
+ post_fn(fn, *args, **kwargs)
38
+ return result
39
+
40
+ return wrapped
41
+
42
+ return wrapper_factory