stouputils 1.9.1__tar.gz → 1.9.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stouputils-1.9.1 → stouputils-1.9.3}/PKG-INFO +2 -2
- {stouputils-1.9.1 → stouputils-1.9.3}/README.md +1 -1
- {stouputils-1.9.1 → stouputils-1.9.3}/pyproject.toml +1 -1
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/ctx.py +49 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/ctx.pyi +29 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/decorators.py +146 -42
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/decorators.pyi +52 -18
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/parallel.py +41 -23
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/parallel.pyi +14 -8
- {stouputils-1.9.1 → stouputils-1.9.3}/.gitignore +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/LICENSE +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/__init__.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/__main__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/_deprecated.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/_deprecated.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/all_doctests.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/all_doctests.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/__init__.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/automatic_docs.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/automatic_docs.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/__init__.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/config.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/config.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/image.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/image.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/video.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/applications/upscaler/video.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/archive.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/archive.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/backup.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/backup.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/collections.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/collections.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/__init__.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/cd_utils.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/cd_utils.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/github.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/github.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/pypi.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/pypi.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/pyproject.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/pyproject.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/stubs.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/continuous_delivery/stubs.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/config/get.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/config/set.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/auto_contrast.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/axis_flip.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/bias_field_correction.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/binary_threshold.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/blur.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/brightness.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/canny.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/clahe.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/common.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/contrast.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/curvature_flow_filter.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/denoise.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/histogram_equalization.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/invert.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/laplacian.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/median_blur.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/noise.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/normalize.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/random_erase.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/resize.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/rotation.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/salt_pepper.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/sharpening.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/shearing.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/threshold.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/translation.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/zoom.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image_augmentation.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image_preprocess.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/prosthesis_detection.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/technique.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/dataset/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/dataset/dataset.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/dataset/dataset_loader.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/dataset/grouping_strategy.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/dataset/image_loader.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/dataset/xy_tuple.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/metric_dictionnary.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/metric_utils.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/mlflow_utils.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/abstract_model.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/all.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/base_keras.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/all.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/convnext.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/densenet.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/efficientnet.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/mobilenet.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/resnet.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/squeezenet.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/vgg.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras/xception.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/callbacks/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/losses/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/visualizations.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/model_interface.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/sandbox.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/range_tuple.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/scripts/augment_dataset.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/scripts/exhaustive_process.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/scripts/preprocess_dataset.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/scripts/routine.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/utils.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/image.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/image.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/__init__.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/__init__.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/common.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/common.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/downloader.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/downloader.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/linux.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/linux.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/main.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/main.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/windows.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/installer/windows.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/io.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/io.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/print.py +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/print.pyi +0 -0
- {stouputils-1.9.1 → stouputils-1.9.3}/stouputils/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stouputils
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.3
|
|
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,
|
|
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,
|
|
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.
|
|
8
|
+
version = "1.9.3"
|
|
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"
|
|
@@ -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
|
-
-
|
|
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
|
-
#
|
|
171
|
-
def
|
|
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
|
-
|
|
175
|
+
seconds: float = 60.0,
|
|
176
|
+
message: str = ""
|
|
175
177
|
) -> Callable[..., Any]:
|
|
176
|
-
""" Decorator that
|
|
178
|
+
""" Decorator that raises a TimeoutError if the function runs longer than the specified timeout.
|
|
177
179
|
|
|
178
|
-
|
|
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
|
|
182
|
-
|
|
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]:
|
|
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
|
-
>>> @
|
|
187
|
-
... def
|
|
188
|
-
...
|
|
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
|
-
>>> @
|
|
191
|
-
... def
|
|
192
|
-
...
|
|
193
|
-
>>>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
#
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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 @
|
|
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
|
|
68
|
-
''' Decorator that
|
|
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
|
-
\
|
|
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
|
|
74
|
-
\t\
|
|
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]:
|
|
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>>> @
|
|
79
|
-
\t\t... def
|
|
80
|
-
\t\t...
|
|
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>>> @
|
|
83
|
-
\t\t... def
|
|
84
|
-
\t\t...
|
|
85
|
-
\t\t>>>
|
|
86
|
-
\t\
|
|
87
|
-
\t\t
|
|
88
|
-
\t\
|
|
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
|
|
|
@@ -17,7 +17,7 @@ import time
|
|
|
17
17
|
from collections.abc import Callable
|
|
18
18
|
from typing import Any, TypeVar, cast
|
|
19
19
|
|
|
20
|
-
from .
|
|
20
|
+
from .ctx import SetMPStartMethod
|
|
21
21
|
from .print import BAR_FORMAT, MAGENTA
|
|
22
22
|
|
|
23
23
|
|
|
@@ -34,9 +34,8 @@ T = TypeVar("T")
|
|
|
34
34
|
R = TypeVar("R")
|
|
35
35
|
|
|
36
36
|
# Functions
|
|
37
|
-
@handle_error(error_log=LogLevels.ERROR_TRACEBACK)
|
|
38
37
|
def multiprocessing(
|
|
39
|
-
func: Callable[
|
|
38
|
+
func: Callable[..., R] | list[Callable[..., R]],
|
|
40
39
|
args: list[T],
|
|
41
40
|
use_starmap: bool = False,
|
|
42
41
|
chunksize: int = 1,
|
|
@@ -131,14 +130,8 @@ def multiprocessing(
|
|
|
131
130
|
if "SemLock created in a fork context is being shared with a process in a spawn context" in str(e):
|
|
132
131
|
|
|
133
132
|
# Try with alternate start method
|
|
134
|
-
|
|
135
|
-
new_method: str = "spawn" if old_method in (None, "fork") else "fork"
|
|
136
|
-
mp.set_start_method(new_method, force=True)
|
|
137
|
-
|
|
138
|
-
try:
|
|
133
|
+
with SetMPStartMethod("spawn" if mp.get_start_method() != "spawn" else "fork"):
|
|
139
134
|
return process()
|
|
140
|
-
finally:
|
|
141
|
-
mp.set_start_method(old_method, force=True)
|
|
142
135
|
else: # Re-raise if it's not the SemLock error
|
|
143
136
|
raise
|
|
144
137
|
|
|
@@ -150,9 +143,8 @@ def multiprocessing(
|
|
|
150
143
|
return [func(arg) for arg in args]
|
|
151
144
|
|
|
152
145
|
|
|
153
|
-
@handle_error(error_log=LogLevels.ERROR_TRACEBACK)
|
|
154
146
|
def multithreading(
|
|
155
|
-
func: Callable[
|
|
147
|
+
func: Callable[..., R] | list[Callable[..., R]],
|
|
156
148
|
args: list[T],
|
|
157
149
|
use_starmap: bool = False,
|
|
158
150
|
desc: str = "",
|
|
@@ -243,10 +235,10 @@ def multithreading(
|
|
|
243
235
|
return [func(arg) for arg in args]
|
|
244
236
|
|
|
245
237
|
|
|
246
|
-
@handle_error(error_log=LogLevels.ERROR_TRACEBACK)
|
|
247
238
|
def run_in_subprocess(
|
|
248
239
|
func: Callable[..., R],
|
|
249
240
|
*args: Any,
|
|
241
|
+
timeout: float | None = None,
|
|
250
242
|
**kwargs: Any
|
|
251
243
|
) -> R:
|
|
252
244
|
""" Execute a function in a subprocess with positional and keyword arguments.
|
|
@@ -256,16 +248,19 @@ def run_in_subprocess(
|
|
|
256
248
|
be created, run the function with the provided arguments, and return the result.
|
|
257
249
|
|
|
258
250
|
Args:
|
|
259
|
-
func
|
|
251
|
+
func (Callable): The function to execute in a subprocess.
|
|
260
252
|
(SHOULD BE A TOP-LEVEL FUNCTION TO BE PICKLABLE)
|
|
261
|
-
*args
|
|
262
|
-
|
|
253
|
+
*args (Any): Positional arguments to pass to the function.
|
|
254
|
+
timeout (float | None): Maximum time in seconds to wait for the subprocess.
|
|
255
|
+
If None, wait indefinitely. If the subprocess exceeds this time, it will be terminated.
|
|
256
|
+
**kwargs (Any): Keyword arguments to pass to the function.
|
|
263
257
|
|
|
264
258
|
Returns:
|
|
265
259
|
R: The return value of the function.
|
|
266
260
|
|
|
267
261
|
Raises:
|
|
268
|
-
RuntimeError: If the subprocess exits with a non-zero exit code.
|
|
262
|
+
RuntimeError: If the subprocess exits with a non-zero exit code or times out.
|
|
263
|
+
TimeoutError: If the subprocess exceeds the specified timeout.
|
|
269
264
|
|
|
270
265
|
Examples:
|
|
271
266
|
.. code-block:: python
|
|
@@ -285,6 +280,9 @@ def run_in_subprocess(
|
|
|
285
280
|
. return f"{greeting}, {name}!"
|
|
286
281
|
> run_in_subprocess(greet, "World", greeting="Hi")
|
|
287
282
|
'Hi, World!'
|
|
283
|
+
|
|
284
|
+
> # With timeout to prevent hanging
|
|
285
|
+
> run_in_subprocess(some_gpu_func, data, timeout=300.0)
|
|
288
286
|
"""
|
|
289
287
|
import multiprocessing as mp
|
|
290
288
|
from multiprocessing import Queue
|
|
@@ -298,20 +296,40 @@ def run_in_subprocess(
|
|
|
298
296
|
args=(result_queue, func, args, kwargs)
|
|
299
297
|
)
|
|
300
298
|
process.start()
|
|
301
|
-
|
|
299
|
+
|
|
300
|
+
# Join with timeout to prevent indefinite hanging
|
|
301
|
+
process.join(timeout=timeout)
|
|
302
|
+
|
|
303
|
+
# Check if process is still alive (timed out)
|
|
304
|
+
if process.is_alive():
|
|
305
|
+
process.terminate()
|
|
306
|
+
time.sleep(0.5) # Give it a moment to terminate gracefully
|
|
307
|
+
if process.is_alive():
|
|
308
|
+
process.kill()
|
|
309
|
+
process.join()
|
|
310
|
+
raise TimeoutError(f"Subprocess exceeded timeout of {timeout} seconds and was terminated")
|
|
302
311
|
|
|
303
312
|
# Check exit code
|
|
304
313
|
if process.exitcode != 0:
|
|
305
|
-
|
|
314
|
+
# Try to get any exception from the queue (non-blocking)
|
|
315
|
+
error_msg = f"Subprocess failed with exit code {process.exitcode}"
|
|
316
|
+
try:
|
|
317
|
+
if not result_queue.empty():
|
|
318
|
+
result_or_exception = result_queue.get_nowait()
|
|
319
|
+
if isinstance(result_or_exception, Exception):
|
|
320
|
+
raise result_or_exception
|
|
321
|
+
except Exception:
|
|
322
|
+
pass
|
|
323
|
+
raise RuntimeError(error_msg)
|
|
306
324
|
|
|
307
325
|
# Retrieve the result
|
|
308
|
-
|
|
309
|
-
result_or_exception = result_queue.
|
|
326
|
+
try:
|
|
327
|
+
result_or_exception = result_queue.get_nowait()
|
|
310
328
|
if isinstance(result_or_exception, Exception):
|
|
311
329
|
raise result_or_exception
|
|
312
330
|
return result_or_exception
|
|
313
|
-
|
|
314
|
-
raise RuntimeError("Subprocess did not return any result")
|
|
331
|
+
except Exception as e:
|
|
332
|
+
raise RuntimeError("Subprocess did not return any result") from e
|
|
315
333
|
|
|
316
334
|
|
|
317
335
|
# "Private" function for subprocess wrapper (must be at module level for pickling on Windows)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from .ctx import SetMPStartMethod as SetMPStartMethod
|
|
2
2
|
from .print import BAR_FORMAT as BAR_FORMAT, MAGENTA as MAGENTA
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
from typing import Any, TypeVar
|
|
@@ -10,7 +10,7 @@ CPU_COUNT: int
|
|
|
10
10
|
T = TypeVar('T')
|
|
11
11
|
R = TypeVar('R')
|
|
12
12
|
|
|
13
|
-
def multiprocessing(func: Callable[
|
|
13
|
+
def multiprocessing(func: Callable[..., R] | list[Callable[..., R]], args: list[T], use_starmap: bool = False, chunksize: int = 1, desc: str = '', max_workers: int = ..., delay_first_calls: float = 0, color: str = ..., bar_format: str = ..., ascii: bool = False) -> list[R]:
|
|
14
14
|
''' Method to execute a function in parallel using multiprocessing
|
|
15
15
|
|
|
16
16
|
\t- For CPU-bound operations where the GIL (Global Interpreter Lock) is a bottleneck.
|
|
@@ -64,7 +64,7 @@ def multiprocessing(func: Callable[[T], R] | list[Callable[[T], R]], args: list[
|
|
|
64
64
|
\t\t\t. )
|
|
65
65
|
\t\t\t[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
66
66
|
\t'''
|
|
67
|
-
def multithreading(func: Callable[
|
|
67
|
+
def multithreading(func: Callable[..., R] | list[Callable[..., R]], args: list[T], use_starmap: bool = False, desc: str = '', max_workers: int = ..., delay_first_calls: float = 0, color: str = ..., bar_format: str = ..., ascii: bool = False) -> list[R]:
|
|
68
68
|
''' Method to execute a function in parallel using multithreading, you should use it:
|
|
69
69
|
|
|
70
70
|
\t- For I/O-bound operations where the GIL is not a bottleneck, such as network requests or disk operations.
|
|
@@ -116,7 +116,7 @@ def multithreading(func: Callable[[T], R] | list[Callable[[T], R]], args: list[T
|
|
|
116
116
|
\t\t\t. )
|
|
117
117
|
\t\t\t[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
118
118
|
\t'''
|
|
119
|
-
def run_in_subprocess(func: Callable[..., R], *args: Any, **kwargs: Any) -> R:
|
|
119
|
+
def run_in_subprocess(func: Callable[..., R], *args: Any, timeout: float | None = None, **kwargs: Any) -> R:
|
|
120
120
|
''' Execute a function in a subprocess with positional and keyword arguments.
|
|
121
121
|
|
|
122
122
|
\tThis is useful when you need to run a function in isolation to avoid memory leaks,
|
|
@@ -124,16 +124,19 @@ def run_in_subprocess(func: Callable[..., R], *args: Any, **kwargs: Any) -> R:
|
|
|
124
124
|
\tbe created, run the function with the provided arguments, and return the result.
|
|
125
125
|
|
|
126
126
|
\tArgs:
|
|
127
|
-
\t\tfunc
|
|
127
|
+
\t\tfunc (Callable): The function to execute in a subprocess.
|
|
128
128
|
\t\t\t(SHOULD BE A TOP-LEVEL FUNCTION TO BE PICKLABLE)
|
|
129
|
-
\t\t*args
|
|
130
|
-
\t\
|
|
129
|
+
\t\t*args (Any): Positional arguments to pass to the function.
|
|
130
|
+
\t\ttimeout (float | None): Maximum time in seconds to wait for the subprocess.
|
|
131
|
+
\t\t\tIf None, wait indefinitely. If the subprocess exceeds this time, it will be terminated.
|
|
132
|
+
\t\t**kwargs (Any): Keyword arguments to pass to the function.
|
|
131
133
|
|
|
132
134
|
\tReturns:
|
|
133
135
|
\t\tR: The return value of the function.
|
|
134
136
|
|
|
135
137
|
\tRaises:
|
|
136
|
-
\t\tRuntimeError: If the subprocess exits with a non-zero exit code.
|
|
138
|
+
\t\tRuntimeError: If the subprocess exits with a non-zero exit code or times out.
|
|
139
|
+
\t\tTimeoutError: If the subprocess exceeds the specified timeout.
|
|
137
140
|
|
|
138
141
|
\tExamples:
|
|
139
142
|
\t\t.. code-block:: python
|
|
@@ -153,6 +156,9 @@ def run_in_subprocess(func: Callable[..., R], *args: Any, **kwargs: Any) -> R:
|
|
|
153
156
|
\t\t\t. return f"{greeting}, {name}!"
|
|
154
157
|
\t\t\t> run_in_subprocess(greet, "World", greeting="Hi")
|
|
155
158
|
\t\t\t\'Hi, World!\'
|
|
159
|
+
|
|
160
|
+
\t\t\t> # With timeout to prevent hanging
|
|
161
|
+
\t\t\t> run_in_subprocess(some_gpu_func, data, timeout=300.0)
|
|
156
162
|
\t'''
|
|
157
163
|
def _subprocess_wrapper(result_queue: Any, func: Callable[..., R], args: tuple[Any, ...], kwargs: dict[str, Any]) -> None:
|
|
158
164
|
""" Wrapper function to execute the target function and store the result in the queue.
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/__init__.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/auto_contrast.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/axis_flip.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/brightness.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/canny.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/clahe.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/common.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/contrast.py
RENAMED
|
File without changes
|
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/denoise.py
RENAMED
|
File without changes
|
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/invert.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/laplacian.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/median_blur.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/noise.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/normalize.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/random_erase.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/resize.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/rotation.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/salt_pepper.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/sharpening.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/shearing.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/threshold.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image/translation.py
RENAMED
|
File without changes
|
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image_augmentation.py
RENAMED
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/data_processing/image_preprocess.py
RENAMED
|
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
|
|
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
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/losses/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{stouputils-1.9.1 → stouputils-1.9.3}/stouputils/data_science/models/keras_utils/visualizations.py
RENAMED
|
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
|
|
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
|