stouputils 1.2.22__tar.gz → 1.2.24__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 (25) hide show
  1. {stouputils-1.2.22 → stouputils-1.2.24}/.gitignore +1 -0
  2. {stouputils-1.2.22 → stouputils-1.2.24}/PKG-INFO +1 -1
  3. {stouputils-1.2.22 → stouputils-1.2.24}/pyproject.toml +1 -1
  4. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/continuous_delivery/github.py +11 -11
  5. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/decorators.py +140 -51
  6. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/print.py +1 -0
  7. {stouputils-1.2.22 → stouputils-1.2.24}/LICENSE +0 -0
  8. {stouputils-1.2.22 → stouputils-1.2.24}/README.md +0 -0
  9. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/__init__.py +0 -0
  10. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/all_doctests.py +0 -0
  11. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/applications/__init__.py +0 -0
  12. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/applications/automatic_docs.py +0 -0
  13. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/archive.py +0 -0
  14. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/backup.py +0 -0
  15. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/collections.py +0 -0
  16. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/continuous_delivery/__init__.py +0 -0
  17. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/continuous_delivery/cd_utils.py +0 -0
  18. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/continuous_delivery/pypi.py +0 -0
  19. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/continuous_delivery/pyproject.py +0 -0
  20. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/ctx.py +0 -0
  21. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/dont_look/zip_file_override.py +0 -0
  22. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/image.py +0 -0
  23. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/io.py +0 -0
  24. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/parallel.py +0 -0
  25. {stouputils-1.2.22 → stouputils-1.2.24}/stouputils/py.typed +0 -0
@@ -5,6 +5,7 @@
5
5
  # Miscellaneous
6
6
  __temporary__/
7
7
  output.log
8
+ logfile.txt
8
9
 
9
10
  # Documentation
10
11
  docs/source/_static/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stouputils
3
- Version: 1.2.22
3
+ Version: 1.2.24
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.22"
8
+ version = "1.2.24"
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
- type_, desc = message.split(":", 1)
237
+ commit_type, desc = message.split(":", 1)
238
238
 
239
- # Clean the type
240
- type_ = type_.split('(')[0]
241
- type_ = "".join(c for c in type_.lower().strip() if c in "abcdefghijklmnopqrstuvwxyz")
242
- type_ = COMMIT_TYPES.get(type_, type_.title())
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 type_ not in commit_groups:
246
- commit_groups[type_] = []
247
- commit_groups[type_].append((desc.strip(), sha))
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 type_ in sorted(commit_groups.keys()):
254
- changelog += f"### {type_}\n"
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[type_][::-1]:
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): Whether to mute stderr or not
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
- @wraps(func)
51
- def wrapper(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
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
- # Re-Enable stdout and stderr
65
- sys.stdout.close()
66
- sys.stdout = _original_stdout
67
- if mute_stderr:
68
- sys.stderr.close()
69
- sys.stderr = _original_stderr
70
- return result
71
- return wrapper
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 (as if the decorator didn't exist)
188
+ LogLevels.RAISE_EXCEPTION: Raise exception
174
189
 
175
190
  Examples:
176
- .. code-block:: python
177
-
178
- > @handle_error(error_log=LogLevels.WARNING)
179
- > def test():
180
- > raise ValueError("Let's fail")
181
- > test() # [WARNING HH:MM:SS] Error during test: (ValueError) Let's fail
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
- return decorator
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(method: Literal["str", "pickle"] = "str") -> Callable[..., Callable[..., Any]]:
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
- method (Literal["str", "pickle"]): The method to use for caching.
243
+ func (Callable[..., Any] | None): Function to cache
244
+ method (Literal["str", "pickle"]): The method to use for caching.
219
245
  Returns:
220
- Callable[..., Callable[..., Any]]: A decorator that caches the result of a function.
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 test(a: int, b: int) -> int:
253
+ ... def test2(a: int, b: int) -> int:
224
254
  ... return a + b
225
- >>> test(1, 2) # 3
255
+ >>> test2(1, 2)
226
256
  3
227
- >>> test(1, 2) # 3
257
+ >>> test2(1, 2)
228
258
  3
229
- >>> test(3, 4) # 7
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
- # Return the decorator
263
- return decorator
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
- message (str): Additional message to display with the deprecation warning
274
- error_log (LogLevels): Log level for the deprecation warning
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
- .. code-block:: python
285
-
286
- > @deprecated(message="Use 'this_function()' instead", error_log=LogLevels.WARNING)
287
- > def test():
288
- > pass
289
- > test() # [WARNING HH:MM:SS] Function 'test()' is deprecated. Use 'this_function()' instead
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,59 @@ def deprecated(
309
344
  # Call the original function
310
345
  return func(*args, **kwargs)
311
346
  return wrapper
312
- return decorator
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
+ if not func.__doc__:
390
+ func.__doc__ = message
391
+
392
+ @wraps(func)
393
+ @handle_error(exceptions=NotImplementedError, error_log=error_log)
394
+ def wrapper(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
395
+ raise NotImplementedError(message)
396
+ return wrapper
397
+
398
+ # Handle both @abstract and @abstract(error_log=...)
399
+ if func is None:
400
+ return decorator
401
+ return decorator(func)
313
402
 
@@ -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