stouputils 1.9.2__tar.gz → 1.9.4__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 (139) hide show
  1. {stouputils-1.9.2 → stouputils-1.9.4}/PKG-INFO +2 -2
  2. {stouputils-1.9.2 → stouputils-1.9.4}/README.md +1 -1
  3. {stouputils-1.9.2 → stouputils-1.9.4}/pyproject.toml +1 -1
  4. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/__main__.py +8 -1
  5. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/archive.py +19 -1
  6. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/archive.pyi +1 -1
  7. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/backup.py +17 -15
  8. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/collections.py +6 -5
  9. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/collections.pyi +4 -3
  10. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/ctx.py +49 -0
  11. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/ctx.pyi +29 -0
  12. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/decorators.py +146 -42
  13. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/decorators.pyi +52 -18
  14. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/parallel.py +6 -11
  15. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/parallel.pyi +5 -4
  16. {stouputils-1.9.2 → stouputils-1.9.4}/.gitignore +0 -0
  17. {stouputils-1.9.2 → stouputils-1.9.4}/LICENSE +0 -0
  18. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/__init__.py +0 -0
  19. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/__init__.pyi +0 -0
  20. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/_deprecated.py +0 -0
  21. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/_deprecated.pyi +0 -0
  22. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/all_doctests.py +0 -0
  23. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/all_doctests.pyi +0 -0
  24. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/__init__.py +0 -0
  25. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/__init__.pyi +0 -0
  26. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/automatic_docs.py +0 -0
  27. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/automatic_docs.pyi +0 -0
  28. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/__init__.py +0 -0
  29. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/__init__.pyi +0 -0
  30. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/config.py +0 -0
  31. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/config.pyi +0 -0
  32. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/image.py +0 -0
  33. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/image.pyi +0 -0
  34. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/video.py +0 -0
  35. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/applications/upscaler/video.pyi +0 -0
  36. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/backup.pyi +0 -0
  37. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/__init__.py +0 -0
  38. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/__init__.pyi +0 -0
  39. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/cd_utils.py +0 -0
  40. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/cd_utils.pyi +0 -0
  41. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/github.py +0 -0
  42. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/github.pyi +0 -0
  43. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/pypi.py +0 -0
  44. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/pypi.pyi +0 -0
  45. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/pyproject.py +0 -0
  46. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/pyproject.pyi +0 -0
  47. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/stubs.py +0 -0
  48. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/continuous_delivery/stubs.pyi +0 -0
  49. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/config/get.py +0 -0
  50. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/config/set.py +0 -0
  51. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/__init__.py +0 -0
  52. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/auto_contrast.py +0 -0
  53. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/axis_flip.py +0 -0
  54. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/bias_field_correction.py +0 -0
  55. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/binary_threshold.py +0 -0
  56. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/blur.py +0 -0
  57. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/brightness.py +0 -0
  58. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/canny.py +0 -0
  59. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/clahe.py +0 -0
  60. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/common.py +0 -0
  61. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/contrast.py +0 -0
  62. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/curvature_flow_filter.py +0 -0
  63. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/denoise.py +0 -0
  64. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/histogram_equalization.py +0 -0
  65. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/invert.py +0 -0
  66. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/laplacian.py +0 -0
  67. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/median_blur.py +0 -0
  68. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/noise.py +0 -0
  69. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/normalize.py +0 -0
  70. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/random_erase.py +0 -0
  71. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/resize.py +0 -0
  72. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/rotation.py +0 -0
  73. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/salt_pepper.py +0 -0
  74. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/sharpening.py +0 -0
  75. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/shearing.py +0 -0
  76. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/threshold.py +0 -0
  77. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/translation.py +0 -0
  78. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image/zoom.py +0 -0
  79. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image_augmentation.py +0 -0
  80. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/image_preprocess.py +0 -0
  81. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/prosthesis_detection.py +0 -0
  82. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/data_processing/technique.py +0 -0
  83. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/dataset/__init__.py +0 -0
  84. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/dataset/dataset.py +0 -0
  85. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/dataset/dataset_loader.py +0 -0
  86. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/dataset/grouping_strategy.py +0 -0
  87. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/dataset/image_loader.py +0 -0
  88. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/dataset/xy_tuple.py +0 -0
  89. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/metric_dictionnary.py +0 -0
  90. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/metric_utils.py +0 -0
  91. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/mlflow_utils.py +0 -0
  92. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/abstract_model.py +0 -0
  93. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/all.py +0 -0
  94. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/base_keras.py +0 -0
  95. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/all.py +0 -0
  96. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/convnext.py +0 -0
  97. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/densenet.py +0 -0
  98. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/efficientnet.py +0 -0
  99. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/mobilenet.py +0 -0
  100. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/resnet.py +0 -0
  101. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/squeezenet.py +0 -0
  102. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/vgg.py +0 -0
  103. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras/xception.py +0 -0
  104. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/callbacks/__init__.py +0 -0
  105. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +0 -0
  106. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +0 -0
  107. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +0 -0
  108. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +0 -0
  109. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +0 -0
  110. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/losses/__init__.py +0 -0
  111. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +0 -0
  112. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/keras_utils/visualizations.py +0 -0
  113. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/model_interface.py +0 -0
  114. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/models/sandbox.py +0 -0
  115. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/range_tuple.py +0 -0
  116. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/scripts/augment_dataset.py +0 -0
  117. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/scripts/exhaustive_process.py +0 -0
  118. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/scripts/preprocess_dataset.py +0 -0
  119. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/scripts/routine.py +0 -0
  120. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/data_science/utils.py +0 -0
  121. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/image.py +0 -0
  122. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/image.pyi +0 -0
  123. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/__init__.py +0 -0
  124. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/__init__.pyi +0 -0
  125. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/common.py +0 -0
  126. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/common.pyi +0 -0
  127. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/downloader.py +0 -0
  128. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/downloader.pyi +0 -0
  129. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/linux.py +0 -0
  130. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/linux.pyi +0 -0
  131. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/main.py +0 -0
  132. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/main.pyi +0 -0
  133. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/windows.py +0 -0
  134. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/installer/windows.pyi +0 -0
  135. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/io.py +0 -0
  136. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/io.pyi +0 -0
  137. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/print.py +0 -0
  138. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/print.pyi +0 -0
  139. {stouputils-1.9.2 → stouputils-1.9.4}/stouputils/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stouputils
3
- Version: 1.9.2
3
+ Version: 1.9.4
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
@@ -149,7 +149,7 @@ stouputils all_<TAB> # Completes to: all_doctests
149
149
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.backup.html">backup.py</a> <span class="comment"># 💾 Utilities for backup management (delta backup, consolidate)</span>
150
150
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.collections.html">collections.py</a> <span class="comment"># 🧰 Utilities for collection manipulation (unique_list, sort_dict_keys, upsert_in_dataframe, array_to_disk)</span>
151
151
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.ctx.html">ctx.py</a> <span class="comment"># 🔇 Context managers (Muffle, LogToFile, MeasureTime, DoNothing)</span>
152
- ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.decorators.html">decorators.py</a> <span class="comment"># 🎯 Decorators (measure_time, handle_error, simple_cache, retry, abstract, deprecated, silent)</span>
152
+ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.decorators.html">decorators.py</a> <span class="comment"># 🎯 Decorators (measure_time, handle_error, timeout, retry, simple_cache, abstract, deprecated, silent)</span>
153
153
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.image.html">image.py</a> <span class="comment"># 🖼️ Little utilities for image processing (image_resize, auto_crop, numpy_to_gif, numpy_to_obj)</span>
154
154
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.io.html">io.py</a> <span class="comment"># 💾 Utilities for file management (super_json, super_csv, super_copy, super_open, clean_path)</span>
155
155
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.parallel.html">parallel.py</a> <span class="comment"># 🔀 Utility functions for parallel processing (multiprocessing, multithreading)</span>
@@ -107,7 +107,7 @@ stouputils all_<TAB> # Completes to: all_doctests
107
107
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.backup.html">backup.py</a> <span class="comment"># 💾 Utilities for backup management (delta backup, consolidate)</span>
108
108
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.collections.html">collections.py</a> <span class="comment"># 🧰 Utilities for collection manipulation (unique_list, sort_dict_keys, upsert_in_dataframe, array_to_disk)</span>
109
109
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.ctx.html">ctx.py</a> <span class="comment"># 🔇 Context managers (Muffle, LogToFile, MeasureTime, DoNothing)</span>
110
- ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.decorators.html">decorators.py</a> <span class="comment"># 🎯 Decorators (measure_time, handle_error, simple_cache, retry, abstract, deprecated, silent)</span>
110
+ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.decorators.html">decorators.py</a> <span class="comment"># 🎯 Decorators (measure_time, handle_error, timeout, retry, simple_cache, abstract, deprecated, silent)</span>
111
111
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.image.html">image.py</a> <span class="comment"># 🖼️ Little utilities for image processing (image_resize, auto_crop, numpy_to_gif, numpy_to_obj)</span>
112
112
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.io.html">io.py</a> <span class="comment"># 💾 Utilities for file management (super_json, super_csv, super_copy, super_open, clean_path)</span>
113
113
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.parallel.html">parallel.py</a> <span class="comment"># 🔀 Utility functions for parallel processing (multiprocessing, multithreading)</span>
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
5
5
 
6
6
  [project]
7
7
  name = "stouputils"
8
- version = "1.9.2"
8
+ version = "1.9.4"
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"
@@ -8,13 +8,14 @@ import sys
8
8
  import argcomplete
9
9
 
10
10
  from .all_doctests import launch_tests
11
+ from .archive import archive_cli
11
12
  from .backup import backup_cli
12
13
  from .decorators import handle_error
13
14
  from .print import CYAN, GREEN, RESET, show_version
14
15
 
15
16
  # Argument Parser Setup for Auto-Completion
16
17
  parser = argparse.ArgumentParser(prog="stouputils", add_help=False)
17
- parser.add_argument("command", nargs="?", choices=["--version", "-v", "all_doctests", "backup"])
18
+ parser.add_argument("command", nargs="?", choices=["--version", "-v", "all_doctests", "archive", "backup"])
18
19
  parser.add_argument("args", nargs="*")
19
20
  argcomplete.autocomplete(parser)
20
21
 
@@ -33,6 +34,11 @@ def main() -> None:
33
34
  sys.exit(1)
34
35
  return
35
36
 
37
+ # Handle "archive" command
38
+ if second_arg == "archive":
39
+ sys.argv.pop(1) # Remove "archive" from argv so archive_cli gets clean arguments
40
+ return archive_cli()
41
+
36
42
  # Handle "backup" command
37
43
  if second_arg == "backup":
38
44
  sys.argv.pop(1) # Remove "backup" from argv so backup_cli gets clean arguments
@@ -58,6 +64,7 @@ def main() -> None:
58
64
  print(f"\n{CYAN}Available commands:{RESET}")
59
65
  print(f" {GREEN}--version, -v{RESET} Show version information")
60
66
  print(f" {GREEN}all_doctests{RESET} [dir] Run all doctests in the specified directory")
67
+ print(f" {GREEN}archive{RESET} --help Archive utilities (make, repair)")
61
68
  print(f" {GREEN}backup{RESET} --help Backup utilities (delta, consolidate, limit)")
62
69
  print(f"{CYAN}{separator}{RESET}")
63
70
  return
@@ -17,7 +17,7 @@ from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
17
17
 
18
18
  from .decorators import LogLevels, handle_error
19
19
  from .io import clean_path, super_copy
20
- from .print import debug, error, info
20
+ from .print import CYAN, GREEN, RESET, debug, error, info
21
21
 
22
22
 
23
23
  # Function that repair a corrupted zip file (ignoring some of the errors)
@@ -267,6 +267,24 @@ def archive_cli() -> None:
267
267
  import argparse
268
268
  import sys
269
269
 
270
+ # Check for help or no command
271
+ if len(sys.argv) == 1 or (len(sys.argv) == 2 and sys.argv[1] in ("--help", "-h", "help")):
272
+ separator: str = "─" * 60
273
+ print(f"{CYAN}{separator}{RESET}")
274
+ print(f"{CYAN}stouputils {GREEN}archive {CYAN}utilities{RESET}")
275
+ print(f"{CYAN}{separator}{RESET}")
276
+ print(f"\n{CYAN}Usage:{RESET} stouputils archive <command> [options]")
277
+ print(f"\n{CYAN}Available commands:{RESET}")
278
+ print(f" {GREEN}make{RESET} <source> <destination> [--ignore PATTERNS] [--create-dir]")
279
+ print(" Create a zip archive from source directory")
280
+ print(f" {CYAN}--ignore{RESET} Glob patterns to ignore (comma-separated)")
281
+ print(f" {CYAN}--create-dir{RESET} Create destination directory if needed")
282
+ print(f"\n {GREEN}repair{RESET} <input_file> [output_file]")
283
+ print(" Repair a corrupted zip file")
284
+ print(" If output_file is omitted, adds '_repaired' suffix")
285
+ print(f"{CYAN}{separator}{RESET}")
286
+ return
287
+
270
288
  parser = argparse.ArgumentParser(description="Archive utilities")
271
289
  subparsers = parser.add_subparsers(dest="command", help="Available commands")
272
290
 
@@ -1,6 +1,6 @@
1
1
  from .decorators import LogLevels as LogLevels, handle_error as handle_error
2
2
  from .io import clean_path as clean_path, super_copy as super_copy
3
- from .print import debug as debug, error as error, info as info
3
+ from .print import CYAN as CYAN, GREEN as GREEN, RESET as RESET, debug as debug, error as error, info as info
4
4
 
5
5
  def repair_zip_file(file_path: str, destination: str) -> bool:
6
6
  ''' Try to repair a corrupted zip file by ignoring some of the errors
@@ -50,6 +50,23 @@ def backup_cli() -> None:
50
50
  python -m stouputils.backup limit 5 /path/to/backups
51
51
  """
52
52
  import argparse
53
+ import sys
54
+
55
+ # Check for help or no command
56
+ if len(sys.argv) == 1 or (len(sys.argv) == 2 and sys.argv[1] in ("--help", "-h", "help")):
57
+ separator: str = "─" * 60
58
+ print(f"{CYAN}{separator}{RESET}")
59
+ print(f"{CYAN}Backup Utilities{RESET}")
60
+ print(f"{CYAN}{separator}{RESET}")
61
+ print(f"\n{CYAN}Usage:{RESET} stouputils backup <command> [options]")
62
+ print(f"\n{CYAN}Available commands:{RESET}")
63
+ print(f" {GREEN}delta{RESET} Create a new delta backup")
64
+ print(f" {GREEN}consolidate{RESET} Consolidate existing backups into one")
65
+ print(f" {GREEN}limit{RESET} Limit the number of delta backups")
66
+ print(f"\n{CYAN}For detailed help on a specific command:{RESET}")
67
+ print(" stouputils backup <command> --help")
68
+ print(f"{CYAN}{separator}{RESET}")
69
+ return
53
70
 
54
71
  # Setup command line argument parser
55
72
  parser: argparse.ArgumentParser = argparse.ArgumentParser(
@@ -82,21 +99,6 @@ def backup_cli() -> None:
82
99
  # Parse arguments and execute appropriate command
83
100
  args: argparse.Namespace = parser.parse_args()
84
101
 
85
- # Show custom help if no command is provided
86
- if not args.command:
87
- separator: str = "─" * 60
88
- print(f"{CYAN}{separator}{RESET}")
89
- print(f"{CYAN}Backup Utilities{RESET}")
90
- print(f"{CYAN}{separator}{RESET}")
91
- print(f"\n{CYAN}Usage:{RESET} stouputils backup <command> [options]")
92
- print(f"\n{CYAN}Available commands:{RESET}")
93
- print(f" {GREEN}delta{RESET} Create a new delta backup")
94
- print(f" {GREEN}consolidate{RESET} Consolidate existing backups into one")
95
- print(f" {GREEN}limit{RESET} Limit the number of delta backups")
96
- print(f"\n{CYAN}For detailed help on a specific command:{RESET}")
97
- print(" stouputils backup <command> --help")
98
- print(f"{CYAN}{separator}{RESET}")
99
- return
100
102
 
101
103
  if args.command == "delta":
102
104
  create_delta_backup(args.source, args.destination, args.exclude)
@@ -15,6 +15,7 @@ import atexit
15
15
  import os
16
16
  import shutil
17
17
  import tempfile
18
+ from collections.abc import Iterable
18
19
  from typing import TYPE_CHECKING, Any, Literal, TypeVar
19
20
 
20
21
  # Lazy imports for typing
@@ -28,14 +29,14 @@ if TYPE_CHECKING:
28
29
  T = TypeVar("T")
29
30
 
30
31
  # Functions
31
- def unique_list(list_to_clean: list[Any], method: Literal["id", "hash", "str"] = "str") -> list[Any]:
32
+ def unique_list(list_to_clean: Iterable[T], method: Literal["id", "hash", "str"] = "str") -> list[T]:
32
33
  """ Remove duplicates from the list while keeping the order using ids (default) or hash or str
33
34
 
34
35
  Args:
35
- list_to_clean (list[Any]): The list to clean
36
+ list_to_clean (Iterable[T]): The list to clean
36
37
  method (Literal["id", "hash", "str"]): The method to use to identify duplicates
37
38
  Returns:
38
- list[Any]: The cleaned list
39
+ list[T]: The cleaned list
39
40
 
40
41
  Examples:
41
42
  >>> unique_list([1, 2, 3, 2, 1], method="id")
@@ -54,8 +55,8 @@ def unique_list(list_to_clean: list[Any], method: Literal["id", "hash", "str"] =
54
55
  [{1, 2, 3}, {2, 3, 4}]
55
56
  """
56
57
  # Initialize the seen ids set and the result list
57
- seen: set[Any] = set()
58
- result: list[Any] = []
58
+ seen: set[int | str] = set()
59
+ result: list[T] = []
59
60
 
60
61
  # Iterate over each item in the list
61
62
  for item in list_to_clean:
@@ -1,18 +1,19 @@
1
1
  import polars as pl
2
2
  import zarr
3
+ from collections.abc import Iterable
3
4
  from numpy.typing import NDArray as NDArray
4
5
  from typing import Any, Literal, TypeVar
5
6
 
6
7
  T = TypeVar('T')
7
8
 
8
- def unique_list(list_to_clean: list[Any], method: Literal['id', 'hash', 'str'] = 'str') -> list[Any]:
9
+ def unique_list(list_to_clean: Iterable[T], method: Literal['id', 'hash', 'str'] = 'str') -> list[T]:
9
10
  ''' Remove duplicates from the list while keeping the order using ids (default) or hash or str
10
11
 
11
12
  \tArgs:
12
- \t\tlist_to_clean\t(list[Any]):\t\t\t\t\tThe list to clean
13
+ \t\tlist_to_clean\t(Iterable[T]):\t\t\t\t\tThe list to clean
13
14
  \t\tmethod\t\t\t(Literal["id", "hash", "str"]):\tThe method to use to identify duplicates
14
15
  \tReturns:
15
- \t\tlist[Any]: The cleaned list
16
+ \t\tlist[T]: The cleaned list
16
17
 
17
18
  \tExamples:
18
19
  \t\t>>> unique_list([1, 2, 3, 2, 1], method="id")
@@ -5,6 +5,7 @@ This module provides context managers for temporarily silencing output.
5
5
  - MeasureTime: Context manager to measure execution time of a code block
6
6
  - Muffle: Context manager that temporarily silences output (alternative to stouputils.decorators.silent())
7
7
  - DoNothing: Context manager that does nothing (no-op)
8
+ - SetMPStartMethod: Context manager to temporarily set multiprocessing start method
8
9
 
9
10
  .. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/ctx_module.gif
10
11
  :alt: stouputils ctx examples
@@ -284,3 +285,51 @@ class DoNothing:
284
285
  """ Exit async context manager (does nothing) """
285
286
  pass
286
287
 
288
+ # Context manager to temporarily set multiprocessing start method
289
+ class SetMPStartMethod:
290
+ """ Context manager to temporarily set multiprocessing start method.
291
+
292
+ This context manager allows you to temporarily change the multiprocessing start method
293
+ and automatically restores the original method when exiting the context.
294
+
295
+ Args:
296
+ start_method (str): The start method to use: "spawn", "fork", or "forkserver"
297
+
298
+ Examples:
299
+ .. code-block:: python
300
+
301
+ > import multiprocessing as mp
302
+ > import stouputils as stp
303
+ > # Temporarily use spawn method
304
+ > with stp.SetMPStartMethod("spawn"):
305
+ > ... # Your multiprocessing code here
306
+ > ... pass
307
+
308
+ > # Original method is automatically restored
309
+ """
310
+ def __init__(self, start_method: str | None) -> None:
311
+ self.start_method: str | None = start_method
312
+ """ The start method to use """
313
+ self.old_method: str | None = None
314
+ """ The original start method to restore """
315
+
316
+ def __enter__(self) -> SetMPStartMethod:
317
+ """ Enter context manager which sets the start method """
318
+ if self.start_method is None:
319
+ return self
320
+ import multiprocessing as mp
321
+
322
+ self.old_method = mp.get_start_method(allow_none=True)
323
+ if self.old_method != self.start_method:
324
+ mp.set_start_method(self.start_method, force=True)
325
+ return self
326
+
327
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
328
+ """ Exit context manager which restores the original start method """
329
+ if self.start_method is None:
330
+ return
331
+ import multiprocessing as mp
332
+
333
+ if self.old_method != self.start_method:
334
+ mp.set_start_method(self.old_method, force=True)
335
+
@@ -141,3 +141,32 @@ class DoNothing:
141
141
  """ Enter async context manager (does nothing) """
142
142
  async def __aexit__(self, *excinfo: Any) -> None:
143
143
  """ Exit async context manager (does nothing) """
144
+
145
+ class SetMPStartMethod:
146
+ ''' Context manager to temporarily set multiprocessing start method.
147
+
148
+ \tThis context manager allows you to temporarily change the multiprocessing start method
149
+ \tand automatically restores the original method when exiting the context.
150
+
151
+ \tArgs:
152
+ \t\tstart_method (str): The start method to use: "spawn", "fork", or "forkserver"
153
+
154
+ \tExamples:
155
+ \t\t.. code-block:: python
156
+
157
+ \t\t\t> import multiprocessing as mp
158
+ \t\t\t> import stouputils as stp
159
+ \t\t\t> # Temporarily use spawn method
160
+ \t\t\t> with stp.SetMPStartMethod("spawn"):
161
+ \t\t\t> ... # Your multiprocessing code here
162
+ \t\t\t> ... pass
163
+
164
+ \t\t\t> # Original method is automatically restored
165
+ \t'''
166
+ start_method: str | None
167
+ old_method: str | None
168
+ def __init__(self, start_method: str | None) -> None: ...
169
+ def __enter__(self) -> SetMPStartMethod:
170
+ """ Enter context manager which sets the start method """
171
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
172
+ """ Exit context manager which restores the original start method """
@@ -3,8 +3,9 @@ This module provides decorators for various purposes:
3
3
 
4
4
  - measure_time(): Measure the execution time of a function and print it with the given print function
5
5
  - handle_error(): Handle an error with different log levels
6
- - simple_cache(): Easy cache function with parameter caching method
6
+ - timeout(): Raise an exception if the function runs longer than the specified timeout
7
7
  - retry(): Retry a function when specific exceptions are raised, with configurable delay and max attempts
8
+ - simple_cache(): Easy cache function with parameter caching method
8
9
  - abstract(): Mark a function as abstract, using LogLevels for error handling
9
10
  - deprecated(): Mark a function as deprecated, using LogLevels for warning handling
10
11
  - silent(): Make a function silent (disable stdout, and stderr if specified) (alternative to stouputils.ctx.Muffle)
@@ -167,67 +168,105 @@ def handle_error(
167
168
  return decorator
168
169
  return decorator(func)
169
170
 
170
- # Easy cache function with parameter caching method
171
- def simple_cache(
171
+ # Decorator that raises an exception if the function runs too long
172
+ def timeout(
172
173
  func: Callable[..., Any] | None = None,
173
174
  *,
174
- method: Literal["str", "pickle"] = "str"
175
+ seconds: float = 60.0,
176
+ message: str = ""
175
177
  ) -> Callable[..., Any]:
176
- """ Decorator that caches the result of a function based on its arguments.
178
+ """ Decorator that raises a TimeoutError if the function runs longer than the specified timeout.
177
179
 
178
- The str method is often faster than the pickle method (by a little) but not as accurate with complex objects.
180
+ Note: This decorator uses SIGALRM on Unix systems, which only works in the main thread.
181
+ On Windows or in non-main threads, it will fall back to a polling-based approach.
179
182
 
180
183
  Args:
181
- func (Callable[..., Any] | None): Function to cache
182
- method (Literal["str", "pickle"]): The method to use for caching.
184
+ func (Callable[..., Any] | None): Function to apply timeout to
185
+ seconds (float): Timeout duration in seconds (default: 60.0)
186
+ message (str): Custom timeout message (default: "Function '{func_name}' timed out after {seconds} seconds")
187
+
183
188
  Returns:
184
- Callable[..., Any]: A decorator that caches the result of a function.
189
+ Callable[..., Any]: Decorator that enforces timeout on the function
190
+
191
+ Raises:
192
+ TimeoutError: If the function execution exceeds the timeout duration
193
+
185
194
  Examples:
186
- >>> @simple_cache
187
- ... def test1(a: int, b: int) -> int:
188
- ... return a + b
195
+ >>> @timeout(seconds=2.0)
196
+ ... def slow_function():
197
+ ... time.sleep(5)
198
+ >>> slow_function() # Raises TimeoutError after 2 seconds
199
+ Traceback (most recent call last):
200
+ ...
201
+ TimeoutError: Function 'slow_function' timed out after 2.0 seconds
189
202
 
190
- >>> @simple_cache(method="str")
191
- ... def test2(a: int, b: int) -> int:
192
- ... return a + b
193
- >>> test2(1, 2)
194
- 3
195
- >>> test2(1, 2)
196
- 3
197
- >>> test2(3, 4)
198
- 7
203
+ >>> @timeout(seconds=1.0, message="Custom timeout message")
204
+ ... def another_slow_function():
205
+ ... time.sleep(3)
206
+ >>> another_slow_function() # Raises TimeoutError after 1 second
207
+ Traceback (most recent call last):
208
+ ...
209
+ TimeoutError: Custom timeout message
199
210
  """
200
211
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
201
- # Create the cache dict
202
- cache_dict: dict[bytes, Any] = {}
203
-
204
- # Create the wrapper
205
212
  @wraps(func)
206
213
  def wrapper(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
214
+ # Build timeout message
215
+ msg: str = message if message else f"Function '{_get_func_name(func)}' timed out after {seconds} seconds"
207
216
 
208
- # Get the hashed key
209
- if method == "str":
210
- hashed: bytes = str(args).encode() + str(kwargs).encode()
211
- elif method == "pickle":
212
- hashed: bytes = pickle_dumps((args, kwargs))
213
- else:
214
- raise ValueError("Invalid caching method. Supported methods are 'str' and 'pickle'.")
217
+ # Try to use signal-based timeout (Unix only, main thread only)
218
+ try:
219
+ import signal
220
+ def timeout_handler(signum: int, frame: Any) -> None:
221
+ raise TimeoutError(msg)
215
222
 
216
- # If the key is in the cache, return it
217
- if hashed in cache_dict:
218
- return cache_dict[hashed]
223
+ # Set the signal handler and alarm
224
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler) # type: ignore
225
+ signal.setitimer(signal.ITIMER_REAL, seconds) # type: ignore
226
+
227
+ try:
228
+ result = func(*args, **kwargs)
229
+ finally:
230
+ # Cancel the alarm and restore the old handler
231
+ signal.setitimer(signal.ITIMER_REAL, 0) # type: ignore
232
+ signal.signal(signal.SIGALRM, old_handler) # type: ignore
219
233
 
220
- # Else, call the function and add the result to the cache
221
- else:
222
- result: Any = func(*args, **kwargs)
223
- cache_dict[hashed] = result
224
234
  return result
225
235
 
226
- # Return the wrapper
227
- wrapper.__name__ = _get_wrapper_name("stouputils.decorators.simple_cache", func)
236
+ except (ValueError, AttributeError) as e:
237
+ # SIGALRM not available (Windows) or not in main thread
238
+ # Fall back to polling-based timeout (less precise but portable)
239
+ import threading
240
+
241
+ result_container: list[Any] = []
242
+ exception_container: list[BaseException] = []
243
+
244
+ def target() -> None:
245
+ try:
246
+ result_container.append(func(*args, **kwargs))
247
+ except BaseException as e_2:
248
+ exception_container.append(e_2)
249
+
250
+ thread = threading.Thread(target=target, daemon=True)
251
+ thread.start()
252
+ thread.join(timeout=seconds)
253
+
254
+ if thread.is_alive():
255
+ # Thread is still running, timeout occurred
256
+ raise TimeoutError(msg) from e
257
+
258
+ # Check if an exception was raised in the thread
259
+ if exception_container:
260
+ raise exception_container[0] from e
261
+
262
+ # Return the result if available
263
+ if result_container:
264
+ return result_container[0]
265
+
266
+ wrapper.__name__ = _get_wrapper_name("stouputils.decorators.timeout", func)
228
267
  return wrapper
229
268
 
230
- # Handle both @simple_cache and @simple_cache(method=...)
269
+ # Handle both @timeout and @timeout(seconds=..., message=...)
231
270
  if func is None:
232
271
  return decorator
233
272
  return decorator(func)
@@ -307,6 +346,71 @@ def retry(
307
346
  return decorator
308
347
  return decorator(func)
309
348
 
349
+ # Easy cache function with parameter caching method
350
+ def simple_cache(
351
+ func: Callable[..., Any] | None = None,
352
+ *,
353
+ method: Literal["str", "pickle"] = "str"
354
+ ) -> Callable[..., Any]:
355
+ """ Decorator that caches the result of a function based on its arguments.
356
+
357
+ The str method is often faster than the pickle method (by a little) but not as accurate with complex objects.
358
+
359
+ Args:
360
+ func (Callable[..., Any] | None): Function to cache
361
+ method (Literal["str", "pickle"]): The method to use for caching.
362
+ Returns:
363
+ Callable[..., Any]: A decorator that caches the result of a function.
364
+ Examples:
365
+ >>> @simple_cache
366
+ ... def test1(a: int, b: int) -> int:
367
+ ... return a + b
368
+
369
+ >>> @simple_cache(method="str")
370
+ ... def test2(a: int, b: int) -> int:
371
+ ... return a + b
372
+ >>> test2(1, 2)
373
+ 3
374
+ >>> test2(1, 2)
375
+ 3
376
+ >>> test2(3, 4)
377
+ 7
378
+ """
379
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
380
+ # Create the cache dict
381
+ cache_dict: dict[bytes, Any] = {}
382
+
383
+ # Create the wrapper
384
+ @wraps(func)
385
+ def wrapper(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
386
+
387
+ # Get the hashed key
388
+ if method == "str":
389
+ hashed: bytes = str(args).encode() + str(kwargs).encode()
390
+ elif method == "pickle":
391
+ hashed: bytes = pickle_dumps((args, kwargs))
392
+ else:
393
+ raise ValueError("Invalid caching method. Supported methods are 'str' and 'pickle'.")
394
+
395
+ # If the key is in the cache, return it
396
+ if hashed in cache_dict:
397
+ return cache_dict[hashed]
398
+
399
+ # Else, call the function and add the result to the cache
400
+ else:
401
+ result: Any = func(*args, **kwargs)
402
+ cache_dict[hashed] = result
403
+ return result
404
+
405
+ # Return the wrapper
406
+ wrapper.__name__ = _get_wrapper_name("stouputils.decorators.simple_cache", func)
407
+ return wrapper
408
+
409
+ # Handle both @simple_cache and @simple_cache(method=...)
410
+ if func is None:
411
+ return decorator
412
+ return decorator(func)
413
+
310
414
  # Decorator that marks a function as abstract
311
415
  def abstract(
312
416
  func: Callable[..., Any] | None = None,
@@ -64,30 +64,39 @@ def handle_error(func: Callable[..., Any] | None = None, *, exceptions: tuple[ty
64
64
  \t\t... raise ValueError("Let\'s fail")
65
65
  \t\t>>> # test()\t# [WARNING HH:MM:SS] Error during test: (ValueError) Let\'s fail
66
66
  \t'''
67
- def simple_cache(func: Callable[..., Any] | None = None, *, method: Literal['str', 'pickle'] = 'str') -> Callable[..., Any]:
68
- ''' Decorator that caches the result of a function based on its arguments.
67
+ def timeout(func: Callable[..., Any] | None = None, *, seconds: float = 60.0, message: str = '') -> Callable[..., Any]:
68
+ ''' Decorator that raises a TimeoutError if the function runs longer than the specified timeout.
69
69
 
70
- \tThe str method is often faster than the pickle method (by a little) but not as accurate with complex objects.
70
+ \tNote: This decorator uses SIGALRM on Unix systems, which only works in the main thread.
71
+ \tOn Windows or in non-main threads, it will fall back to a polling-based approach.
71
72
 
72
73
  \tArgs:
73
- \t\tfunc (Callable[..., Any] | None): Function to cache
74
- \t\tmethod (Literal["str", "pickle"]): The method to use for caching.
74
+ \t\tfunc\t\t(Callable[..., Any] | None):\tFunction to apply timeout to
75
+ \t\tseconds\t\t(float):\t\t\t\t\t\tTimeout duration in seconds (default: 60.0)
76
+ \t\tmessage\t\t(str):\t\t\t\t\t\t\tCustom timeout message (default: "Function \'{func_name}\' timed out after {seconds} seconds")
77
+
75
78
  \tReturns:
76
- \t\tCallable[..., Any]: A decorator that caches the result of a function.
79
+ \t\tCallable[..., Any]: Decorator that enforces timeout on the function
80
+
81
+ \tRaises:
82
+ \t\tTimeoutError: If the function execution exceeds the timeout duration
83
+
77
84
  \tExamples:
78
- \t\t>>> @simple_cache
79
- \t\t... def test1(a: int, b: int) -> int:
80
- \t\t... return a + b
85
+ \t\t>>> @timeout(seconds=2.0)
86
+ \t\t... def slow_function():
87
+ \t\t... time.sleep(5)
88
+ \t\t>>> slow_function() # Raises TimeoutError after 2 seconds
89
+ \t\tTraceback (most recent call last):
90
+ \t\t\t...
91
+ \t\tTimeoutError: Function \'slow_function\' timed out after 2.0 seconds
81
92
 
82
- \t\t>>> @simple_cache(method="str")
83
- \t\t... def test2(a: int, b: int) -> int:
84
- \t\t... return a + b
85
- \t\t>>> test2(1, 2)
86
- \t\t3
87
- \t\t>>> test2(1, 2)
88
- \t\t3
89
- \t\t>>> test2(3, 4)
90
- \t\t7
93
+ \t\t>>> @timeout(seconds=1.0, message="Custom timeout message")
94
+ \t\t... def another_slow_function():
95
+ \t\t... time.sleep(3)
96
+ \t\t>>> another_slow_function() # Raises TimeoutError after 1 second
97
+ \t\tTraceback (most recent call last):
98
+ \t\t\t...
99
+ \t\tTimeoutError: Custom timeout message
91
100
  \t'''
92
101
  def retry(func: Callable[..., Any] | None = None, *, exceptions: tuple[type[BaseException], ...] | type[BaseException] = ..., max_attempts: int = 10, delay: float = 1.0, backoff: float = 1.0, message: str = '') -> Callable[..., Any]:
93
102
  ''' Decorator that retries a function when specific exceptions are raised.
@@ -118,6 +127,31 @@ def retry(func: Callable[..., Any] | None = None, *, exceptions: tuple[type[Base
118
127
  \t\t... def might_fail():
119
128
  \t\t... pass
120
129
  \t'''
130
+ def simple_cache(func: Callable[..., Any] | None = None, *, method: Literal['str', 'pickle'] = 'str') -> Callable[..., Any]:
131
+ ''' Decorator that caches the result of a function based on its arguments.
132
+
133
+ \tThe str method is often faster than the pickle method (by a little) but not as accurate with complex objects.
134
+
135
+ \tArgs:
136
+ \t\tfunc (Callable[..., Any] | None): Function to cache
137
+ \t\tmethod (Literal["str", "pickle"]): The method to use for caching.
138
+ \tReturns:
139
+ \t\tCallable[..., Any]: A decorator that caches the result of a function.
140
+ \tExamples:
141
+ \t\t>>> @simple_cache
142
+ \t\t... def test1(a: int, b: int) -> int:
143
+ \t\t... return a + b
144
+
145
+ \t\t>>> @simple_cache(method="str")
146
+ \t\t... def test2(a: int, b: int) -> int:
147
+ \t\t... return a + b
148
+ \t\t>>> test2(1, 2)
149
+ \t\t3
150
+ \t\t>>> test2(1, 2)
151
+ \t\t3
152
+ \t\t>>> test2(3, 4)
153
+ \t\t7
154
+ \t'''
121
155
  def abstract(func: Callable[..., Any] | None = None, *, error_log: LogLevels = ...) -> Callable[..., Any]:
122
156
  """ Decorator that marks a function as abstract.
123
157