stouputils 1.2.22__tar.gz → 1.2.23__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.
- {stouputils-1.2.22 → stouputils-1.2.23}/.gitignore +1 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/PKG-INFO +1 -1
- {stouputils-1.2.22 → stouputils-1.2.23}/pyproject.toml +1 -1
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/continuous_delivery/github.py +11 -11
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/decorators.py +137 -51
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/print.py +1 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/LICENSE +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/README.md +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/__init__.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/all_doctests.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/applications/__init__.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/applications/automatic_docs.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/archive.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/backup.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/collections.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/continuous_delivery/__init__.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/continuous_delivery/cd_utils.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/continuous_delivery/pypi.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/continuous_delivery/pyproject.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/ctx.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/dont_look/zip_file_override.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/image.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/io.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/parallel.py +0 -0
- {stouputils-1.2.22 → stouputils-1.2.23}/stouputils/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stouputils
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.23
|
|
4
4
|
Summary: Stouputils is a collection of utility modules designed to simplify and enhance the development process. It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers, and many more.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Stoupy51/stouputils
|
|
6
6
|
Project-URL: Issues, https://github.com/Stoupy51/stouputils/issues
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "stouputils"
|
|
8
|
-
version = "1.2.
|
|
8
|
+
version = "1.2.23"
|
|
9
9
|
description = "Stouputils is a collection of utility modules designed to simplify and enhance the development process. It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers, and many more."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -234,27 +234,27 @@ def generate_changelog(commits: list[dict[str, Any]], owner: str, project_name:
|
|
|
234
234
|
|
|
235
235
|
# If the message contains a colon, split the message into a type and a description
|
|
236
236
|
if ":" in message:
|
|
237
|
-
|
|
237
|
+
commit_type, desc = message.split(":", 1)
|
|
238
238
|
|
|
239
|
-
# Clean the type
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
# Clean the type, ex: 'feat(hand)/refactor(feet)' -> 'feat'
|
|
240
|
+
commit_type = commit_type.split('(')[0].split('/')[0]
|
|
241
|
+
commit_type = "".join(c for c in commit_type.lower().strip() if c in "abcdefghijklmnopqrstuvwxyz")
|
|
242
|
+
commit_type = COMMIT_TYPES.get(commit_type, commit_type.title())
|
|
243
243
|
|
|
244
244
|
# Add the commit to the commit groups
|
|
245
|
-
if
|
|
246
|
-
commit_groups[
|
|
247
|
-
commit_groups[
|
|
245
|
+
if commit_type not in commit_groups:
|
|
246
|
+
commit_groups[commit_type] = []
|
|
247
|
+
commit_groups[commit_type].append((desc.strip(), sha))
|
|
248
248
|
|
|
249
249
|
# Initialize the changelog
|
|
250
250
|
changelog: str = "## Changelog\n\n"
|
|
251
251
|
|
|
252
252
|
# Iterate over the commit groups
|
|
253
|
-
for
|
|
254
|
-
changelog += f"### {
|
|
253
|
+
for commit_type in sorted(commit_groups.keys()):
|
|
254
|
+
changelog += f"### {commit_type}\n"
|
|
255
255
|
|
|
256
256
|
# Reverse the list to display the most recent commits in last
|
|
257
|
-
for desc, sha in commit_groups[
|
|
257
|
+
for desc, sha in commit_groups[commit_type][::-1]:
|
|
258
258
|
changelog += f"- {desc} ([{sha[:7]}](https://github.com/{owner}/{project_name}/commit/{sha}))\n"
|
|
259
259
|
changelog += "\n"
|
|
260
260
|
|
|
@@ -28,7 +28,8 @@ from .print import debug, warning, error
|
|
|
28
28
|
|
|
29
29
|
# Decorator that make a function silent (disable stdout)
|
|
30
30
|
def silent(
|
|
31
|
-
func: Callable[..., Any],
|
|
31
|
+
func: Callable[..., Any] | None = None,
|
|
32
|
+
*,
|
|
32
33
|
mute_stderr: bool = False
|
|
33
34
|
) -> Callable[..., Any]:
|
|
34
35
|
""" Decorator that makes a function silent (disable stdout, and stderr if specified).
|
|
@@ -36,8 +37,8 @@ def silent(
|
|
|
36
37
|
Alternative to stouputils.ctx.Muffle.
|
|
37
38
|
|
|
38
39
|
Args:
|
|
39
|
-
func (Callable[..., Any]): Function to make silent
|
|
40
|
-
mute_stderr (bool):
|
|
40
|
+
func (Callable[..., Any] | None): Function to make silent
|
|
41
|
+
mute_stderr (bool): Whether to mute stderr or not
|
|
41
42
|
|
|
42
43
|
Examples:
|
|
43
44
|
>>> @silent
|
|
@@ -45,30 +46,41 @@ def silent(
|
|
|
45
46
|
... print("Hello, world!")
|
|
46
47
|
>>> test()
|
|
47
48
|
|
|
49
|
+
>>> @silent(mute_stderr=True)
|
|
50
|
+
... def test2():
|
|
51
|
+
... print("Hello, world!")
|
|
52
|
+
>>> test2()
|
|
53
|
+
|
|
48
54
|
>>> silent(print)("Hello, world!")
|
|
49
55
|
"""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# Disable stdout and stderr
|
|
54
|
-
_original_stdout: Any = sys.stdout
|
|
55
|
-
_original_stderr: Any = None
|
|
56
|
-
sys.stdout = open(os.devnull, "w", encoding="utf-8")
|
|
57
|
-
if mute_stderr:
|
|
58
|
-
_original_stderr = sys.stderr
|
|
59
|
-
sys.stderr = open(os.devnull, "w", encoding="utf-8")
|
|
60
|
-
|
|
61
|
-
# Call the function
|
|
62
|
-
result: Any = func(*args, **kwargs)
|
|
56
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
57
|
+
@wraps(func)
|
|
58
|
+
def wrapper(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
# Disable stdout and stderr
|
|
61
|
+
_original_stdout: Any = sys.stdout
|
|
62
|
+
_original_stderr: Any = None
|
|
63
|
+
sys.stdout = open(os.devnull, "w", encoding="utf-8")
|
|
64
|
+
if mute_stderr:
|
|
65
|
+
_original_stderr = sys.stderr
|
|
66
|
+
sys.stderr = open(os.devnull, "w", encoding="utf-8")
|
|
67
|
+
|
|
68
|
+
# Call the function
|
|
69
|
+
result: Any = func(*args, **kwargs)
|
|
70
|
+
|
|
71
|
+
# Re-Enable stdout and stderr
|
|
72
|
+
sys.stdout.close()
|
|
73
|
+
sys.stdout = _original_stdout
|
|
74
|
+
if mute_stderr:
|
|
75
|
+
sys.stderr.close()
|
|
76
|
+
sys.stderr = _original_stderr
|
|
77
|
+
return result
|
|
78
|
+
return wrapper
|
|
79
|
+
|
|
80
|
+
# Handle both @silent and @silent(mute_stderr=...)
|
|
81
|
+
if func is None:
|
|
82
|
+
return decorator
|
|
83
|
+
return decorator(func)
|
|
72
84
|
|
|
73
85
|
|
|
74
86
|
|
|
@@ -156,6 +168,8 @@ force_raise_exception: bool = False
|
|
|
156
168
|
""" If true, the error_log parameter will be set to RAISE_EXCEPTION for every next handle_error calls, useful for doctests """
|
|
157
169
|
|
|
158
170
|
def handle_error(
|
|
171
|
+
func: Callable[..., Any] | None = None,
|
|
172
|
+
*,
|
|
159
173
|
exceptions: tuple[type[BaseException], ...] | type[BaseException] = (Exception,),
|
|
160
174
|
message: str = "",
|
|
161
175
|
error_log: LogLevels = LogLevels.WARNING_TRACEBACK
|
|
@@ -163,6 +177,7 @@ def handle_error(
|
|
|
163
177
|
""" Decorator that handle an error with different log levels.
|
|
164
178
|
|
|
165
179
|
Args:
|
|
180
|
+
func (Callable[..., Any] | None): Function to decorate
|
|
166
181
|
exceptions (tuple[type[BaseException]], ...): Exceptions to handle
|
|
167
182
|
message (str): Message to display with the error. (e.g. "Error during something")
|
|
168
183
|
error_log (LogLevels): Log level for the errors
|
|
@@ -170,15 +185,17 @@ def handle_error(
|
|
|
170
185
|
LogLevels.WARNING: Show as warning
|
|
171
186
|
LogLevels.WARNING_TRACEBACK: Show as warning with traceback
|
|
172
187
|
LogLevels.ERROR_TRACEBACK: Show as error with traceback
|
|
173
|
-
LogLevels.RAISE_EXCEPTION: Raise exception
|
|
188
|
+
LogLevels.RAISE_EXCEPTION: Raise exception
|
|
174
189
|
|
|
175
190
|
Examples:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
191
|
+
>>> @handle_error
|
|
192
|
+
... def might_fail():
|
|
193
|
+
... raise ValueError("Let's fail")
|
|
194
|
+
|
|
195
|
+
>>> @handle_error(error_log=LogLevels.WARNING)
|
|
196
|
+
... def test():
|
|
197
|
+
... raise ValueError("Let's fail")
|
|
198
|
+
>>> # test() # [WARNING HH:MM:SS] Error during test: (ValueError) Let's fail
|
|
182
199
|
"""
|
|
183
200
|
# Update error_log if needed
|
|
184
201
|
if force_raise_exception:
|
|
@@ -204,32 +221,44 @@ def handle_error(
|
|
|
204
221
|
elif error_log == LogLevels.RAISE_EXCEPTION:
|
|
205
222
|
raise e
|
|
206
223
|
return wrapper
|
|
207
|
-
|
|
224
|
+
|
|
225
|
+
# Handle both @handle_error and @handle_error(exceptions=..., message=..., error_log=...)
|
|
226
|
+
if func is None:
|
|
227
|
+
return decorator
|
|
228
|
+
return decorator(func)
|
|
208
229
|
|
|
209
230
|
|
|
210
231
|
|
|
211
232
|
# Easy cache function with parameter caching method
|
|
212
|
-
def simple_cache(
|
|
233
|
+
def simple_cache(
|
|
234
|
+
func: Callable[..., Any] | None = None,
|
|
235
|
+
*,
|
|
236
|
+
method: Literal["str", "pickle"] = "str"
|
|
237
|
+
) -> Callable[..., Any]:
|
|
213
238
|
""" Decorator that caches the result of a function based on its arguments.
|
|
214
239
|
|
|
215
240
|
The str method is often faster than the pickle method (by a little).
|
|
216
241
|
|
|
217
242
|
Args:
|
|
218
|
-
|
|
243
|
+
func (Callable[..., Any] | None): Function to cache
|
|
244
|
+
method (Literal["str", "pickle"]): The method to use for caching.
|
|
219
245
|
Returns:
|
|
220
|
-
Callable[...,
|
|
246
|
+
Callable[..., Any]: A decorator that caches the result of a function.
|
|
221
247
|
Examples:
|
|
248
|
+
>>> @simple_cache
|
|
249
|
+
... def test1(a: int, b: int) -> int:
|
|
250
|
+
... return a + b
|
|
251
|
+
|
|
222
252
|
>>> @simple_cache(method="str")
|
|
223
|
-
... def
|
|
253
|
+
... def test2(a: int, b: int) -> int:
|
|
224
254
|
... return a + b
|
|
225
|
-
>>>
|
|
255
|
+
>>> test2(1, 2)
|
|
226
256
|
3
|
|
227
|
-
>>>
|
|
257
|
+
>>> test2(1, 2)
|
|
228
258
|
3
|
|
229
|
-
>>>
|
|
259
|
+
>>> test2(3, 4)
|
|
230
260
|
7
|
|
231
261
|
"""
|
|
232
|
-
|
|
233
262
|
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
234
263
|
# Create the cache dict
|
|
235
264
|
cache_dict: dict[bytes, Any] = {}
|
|
@@ -259,19 +288,24 @@ def simple_cache(method: Literal["str", "pickle"] = "str") -> Callable[..., Call
|
|
|
259
288
|
# Return the wrapper
|
|
260
289
|
return wrapper
|
|
261
290
|
|
|
262
|
-
#
|
|
263
|
-
|
|
291
|
+
# Handle both @simple_cache and @simple_cache(method=...)
|
|
292
|
+
if func is None:
|
|
293
|
+
return decorator
|
|
294
|
+
return decorator(func)
|
|
264
295
|
|
|
265
296
|
|
|
266
297
|
def deprecated(
|
|
298
|
+
func: Callable[..., Any] | None = None,
|
|
299
|
+
*,
|
|
267
300
|
message: str = "",
|
|
268
301
|
error_log: LogLevels = LogLevels.WARNING
|
|
269
302
|
) -> Callable[..., Any]:
|
|
270
303
|
""" Decorator that marks a function as deprecated.
|
|
271
304
|
|
|
272
305
|
Args:
|
|
273
|
-
|
|
274
|
-
|
|
306
|
+
func (Callable[..., Any] | None): Function to mark as deprecated
|
|
307
|
+
message (str): Additional message to display with the deprecation warning
|
|
308
|
+
error_log (LogLevels): Log level for the deprecation warning
|
|
275
309
|
LogLevels.NONE: None
|
|
276
310
|
LogLevels.WARNING: Show as warning
|
|
277
311
|
LogLevels.WARNING_TRACEBACK: Show as warning with traceback
|
|
@@ -281,12 +315,13 @@ def deprecated(
|
|
|
281
315
|
Callable[..., Any]: Decorator that marks a function as deprecated
|
|
282
316
|
|
|
283
317
|
Examples:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
318
|
+
>>> @deprecated
|
|
319
|
+
... def old_function():
|
|
320
|
+
... pass
|
|
321
|
+
|
|
322
|
+
>>> @deprecated(message="Use 'new_function()' instead", error_log=LogLevels.WARNING)
|
|
323
|
+
... def another_old_function():
|
|
324
|
+
... pass
|
|
290
325
|
"""
|
|
291
326
|
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
292
327
|
@wraps(func)
|
|
@@ -309,5 +344,56 @@ def deprecated(
|
|
|
309
344
|
# Call the original function
|
|
310
345
|
return func(*args, **kwargs)
|
|
311
346
|
return wrapper
|
|
312
|
-
|
|
347
|
+
|
|
348
|
+
# Handle both @deprecated and @deprecated(message=..., error_log=...)
|
|
349
|
+
if func is None:
|
|
350
|
+
return decorator
|
|
351
|
+
return decorator(func)
|
|
352
|
+
|
|
353
|
+
def abstract(
|
|
354
|
+
func: Callable[..., Any] | None = None,
|
|
355
|
+
*,
|
|
356
|
+
error_log: LogLevels = LogLevels.RAISE_EXCEPTION
|
|
357
|
+
) -> Callable[..., Any]:
|
|
358
|
+
""" Decorator that marks a function as abstract.
|
|
359
|
+
|
|
360
|
+
Contrary to the abstractmethod decorator from the abc module that raises a TypeError
|
|
361
|
+
when you try to instantiate a class that has abstract methods, this decorator raises
|
|
362
|
+
a NotImplementedError ONLY when the decorated function is called, indicating that the function
|
|
363
|
+
must be implemented by a subclass.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
func (Callable[..., Any] | None): The function to mark as abstract
|
|
367
|
+
error_log (LogLevels): Log level for the error handling
|
|
368
|
+
LogLevels.NONE: None
|
|
369
|
+
LogLevels.WARNING: Show as warning
|
|
370
|
+
LogLevels.WARNING_TRACEBACK: Show as warning with traceback
|
|
371
|
+
LogLevels.ERROR_TRACEBACK: Show as error with traceback
|
|
372
|
+
LogLevels.RAISE_EXCEPTION: Raise exception
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Callable[..., Any]: Decorator that raises NotImplementedError when called
|
|
376
|
+
|
|
377
|
+
Examples:
|
|
378
|
+
>>> class Base:
|
|
379
|
+
... @abstract
|
|
380
|
+
... def method(self):
|
|
381
|
+
... pass
|
|
382
|
+
>>> Base().method()
|
|
383
|
+
Traceback (most recent call last):
|
|
384
|
+
...
|
|
385
|
+
NotImplementedError: Function 'method' is abstract and must be implemented by a subclass
|
|
386
|
+
"""
|
|
387
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
388
|
+
message: str = f"Function '{func.__name__}' is abstract and must be implemented by a subclass"
|
|
389
|
+
@wraps(func)
|
|
390
|
+
@handle_error(exceptions=NotImplementedError, error_log=error_log)
|
|
391
|
+
def wrapper(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
|
|
392
|
+
raise NotImplementedError(message)
|
|
393
|
+
return wrapper
|
|
394
|
+
|
|
395
|
+
# Handle both @abstract and @abstract(error_log=...)
|
|
396
|
+
if func is None:
|
|
397
|
+
return decorator
|
|
398
|
+
return decorator(func)
|
|
313
399
|
|
|
@@ -239,6 +239,7 @@ class TeeMultiOutput(object):
|
|
|
239
239
|
>>> original_stdout = sys.stdout
|
|
240
240
|
>>> sys.stdout = TeeMultiOutput(sys.stdout, f)
|
|
241
241
|
>>> print("Hello World") # Output goes to both console and file
|
|
242
|
+
Hello World
|
|
242
243
|
>>> sys.stdout = original_stdout
|
|
243
244
|
>>> f.close()
|
|
244
245
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|