stouputils 1.15.1__tar.gz → 1.16.1__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 (141) hide show
  1. {stouputils-1.15.1 → stouputils-1.16.1}/PKG-INFO +2 -1
  2. {stouputils-1.15.1 → stouputils-1.16.1}/README.md +1 -0
  3. {stouputils-1.15.1 → stouputils-1.16.1}/pyproject.toml +1 -1
  4. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/__init__.py +4 -14
  5. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/__init__.pyi +1 -0
  6. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/__main__.py +9 -7
  7. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/all_doctests.py +20 -9
  8. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/all_doctests.pyi +3 -2
  9. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/automatic_docs.py +4 -0
  10. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/collections.py +1 -1
  11. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/collections.pyi +1 -1
  12. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/pypi.py +10 -9
  13. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/pypi.pyi +3 -3
  14. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/stubs.py +1 -1
  15. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/stubs.pyi +1 -1
  16. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/scripts/exhaustive_process.py +3 -2
  17. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/image.py +7 -8
  18. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/image.pyi +1 -1
  19. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/parallel.py +15 -30
  20. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/print.py +2 -1
  21. stouputils-1.16.1/stouputils/typing.py +71 -0
  22. stouputils-1.16.1/stouputils/typing.pyi +42 -0
  23. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/_deprecated.py +0 -0
  24. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/_deprecated.pyi +0 -0
  25. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/__init__.py +0 -0
  26. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/__init__.pyi +0 -0
  27. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/automatic_docs.pyi +0 -0
  28. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/__init__.py +0 -0
  29. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/__init__.pyi +0 -0
  30. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/config.py +0 -0
  31. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/config.pyi +0 -0
  32. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/image.py +0 -0
  33. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/image.pyi +0 -0
  34. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/video.py +0 -0
  35. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/applications/upscaler/video.pyi +0 -0
  36. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/archive.py +0 -0
  37. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/archive.pyi +0 -0
  38. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/backup.py +0 -0
  39. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/backup.pyi +0 -0
  40. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/__init__.py +0 -0
  41. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/__init__.pyi +0 -0
  42. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/cd_utils.py +0 -0
  43. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/cd_utils.pyi +0 -0
  44. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/github.py +0 -0
  45. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/github.pyi +0 -0
  46. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/pyproject.py +0 -0
  47. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/continuous_delivery/pyproject.pyi +0 -0
  48. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/ctx.py +0 -0
  49. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/ctx.pyi +0 -0
  50. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/config/get.py +0 -0
  51. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/config/set.py +0 -0
  52. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/__init__.py +0 -0
  53. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/auto_contrast.py +0 -0
  54. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/axis_flip.py +0 -0
  55. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/bias_field_correction.py +0 -0
  56. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/binary_threshold.py +0 -0
  57. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/blur.py +0 -0
  58. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/brightness.py +0 -0
  59. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/canny.py +0 -0
  60. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/clahe.py +0 -0
  61. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/common.py +0 -0
  62. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/contrast.py +0 -0
  63. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/curvature_flow_filter.py +0 -0
  64. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/denoise.py +0 -0
  65. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/histogram_equalization.py +0 -0
  66. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/invert.py +0 -0
  67. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/laplacian.py +0 -0
  68. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/median_blur.py +0 -0
  69. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/noise.py +0 -0
  70. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/normalize.py +0 -0
  71. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/random_erase.py +0 -0
  72. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/resize.py +0 -0
  73. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/rotation.py +0 -0
  74. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/salt_pepper.py +0 -0
  75. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/sharpening.py +0 -0
  76. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/shearing.py +0 -0
  77. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/threshold.py +0 -0
  78. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/translation.py +0 -0
  79. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image/zoom.py +0 -0
  80. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image_augmentation.py +0 -0
  81. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/image_preprocess.py +0 -0
  82. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/prosthesis_detection.py +0 -0
  83. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/data_processing/technique.py +0 -0
  84. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/dataset/__init__.py +0 -0
  85. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/dataset/dataset.py +0 -0
  86. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/dataset/dataset_loader.py +0 -0
  87. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/dataset/grouping_strategy.py +0 -0
  88. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/dataset/image_loader.py +0 -0
  89. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/dataset/xy_tuple.py +0 -0
  90. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/metric_dictionnary.py +0 -0
  91. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/metric_utils.py +0 -0
  92. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/mlflow_utils.py +0 -0
  93. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/abstract_model.py +0 -0
  94. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/all.py +0 -0
  95. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/base_keras.py +0 -0
  96. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/all.py +0 -0
  97. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/convnext.py +0 -0
  98. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/densenet.py +0 -0
  99. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/efficientnet.py +0 -0
  100. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/mobilenet.py +0 -0
  101. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/resnet.py +0 -0
  102. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/squeezenet.py +0 -0
  103. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/vgg.py +0 -0
  104. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras/xception.py +0 -0
  105. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/callbacks/__init__.py +0 -0
  106. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +0 -0
  107. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +0 -0
  108. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +0 -0
  109. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +0 -0
  110. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +0 -0
  111. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/losses/__init__.py +0 -0
  112. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +0 -0
  113. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/keras_utils/visualizations.py +0 -0
  114. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/model_interface.py +0 -0
  115. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/models/sandbox.py +0 -0
  116. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/range_tuple.py +0 -0
  117. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/scripts/augment_dataset.py +0 -0
  118. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/scripts/preprocess_dataset.py +0 -0
  119. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/scripts/routine.py +0 -0
  120. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/data_science/utils.py +0 -0
  121. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/decorators.py +0 -0
  122. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/decorators.pyi +0 -0
  123. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/__init__.py +0 -0
  124. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/__init__.pyi +0 -0
  125. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/common.py +0 -0
  126. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/common.pyi +0 -0
  127. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/downloader.py +0 -0
  128. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/downloader.pyi +0 -0
  129. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/linux.py +0 -0
  130. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/linux.pyi +0 -0
  131. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/main.py +0 -0
  132. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/main.pyi +0 -0
  133. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/windows.py +0 -0
  134. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/installer/windows.pyi +0 -0
  135. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/io.py +0 -0
  136. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/io.pyi +0 -0
  137. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/parallel.pyi +0 -0
  138. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/print.pyi +0 -0
  139. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/py.typed +0 -0
  140. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/version_pkg.py +0 -0
  141. {stouputils-1.15.1 → stouputils-1.16.1}/stouputils/version_pkg.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stouputils
3
- Version: 1.15.1
3
+ Version: 1.16.1
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
  Keywords: utilities,tools,helpers,development,python
6
6
  Author: Stoupy51
@@ -94,6 +94,7 @@ Start now by installing the package: `pip install stouputils`.<br>
94
94
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.parallel.html">parallel.py</a> <span class="comment"># 🔀 Utility functions for parallel processing <span class="paren">(multiprocessing, multithreading, run_in_subprocess)</span></span>
95
95
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.image.html">image.py</a> <span class="comment"># 🖼️ Little utilities for image processing <span class="paren">(image_resize, auto_crop, numpy_to_gif, numpy_to_obj)</span></span>
96
96
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.collections.html">collections.py</a> <span class="comment"># 🧰 Utilities for collection manipulation <span class="paren">(unique_list, sort_dict_keys, upsert_in_dataframe, array_to_disk)</span></span>
97
+ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.typing.html">typing.py</a> <span class="comment"># 📝 Utilities for typing enhancements <span class="paren">(IterAny, JsonDict, JsonList, ..., convert_to_serializable)</span></span>
97
98
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.all_doctests.html">all_doctests.py</a> <span class="comment"># ✅ Run all doctests for all modules in a given directory <span class="paren">(launch_tests, test_module_with_progress)</span></span>
98
99
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.backup.html">backup.py</a> <span class="comment"># 💾 Utilities for backup management <span class="paren">(delta backup, consolidate)</span></span>
99
100
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.archive.html">archive.py</a> <span class="comment"># 📦 Functions for creating and managing archives</span>
@@ -52,6 +52,7 @@ Start now by installing the package: `pip install stouputils`.<br>
52
52
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.parallel.html">parallel.py</a> <span class="comment"># 🔀 Utility functions for parallel processing <span class="paren">(multiprocessing, multithreading, run_in_subprocess)</span></span>
53
53
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.image.html">image.py</a> <span class="comment"># 🖼️ Little utilities for image processing <span class="paren">(image_resize, auto_crop, numpy_to_gif, numpy_to_obj)</span></span>
54
54
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.collections.html">collections.py</a> <span class="comment"># 🧰 Utilities for collection manipulation <span class="paren">(unique_list, sort_dict_keys, upsert_in_dataframe, array_to_disk)</span></span>
55
+ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.typing.html">typing.py</a> <span class="comment"># 📝 Utilities for typing enhancements <span class="paren">(IterAny, JsonDict, JsonList, ..., convert_to_serializable)</span></span>
55
56
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.all_doctests.html">all_doctests.py</a> <span class="comment"># ✅ Run all doctests for all modules in a given directory <span class="paren">(launch_tests, test_module_with_progress)</span></span>
56
57
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.backup.html">backup.py</a> <span class="comment"># 💾 Utilities for backup management <span class="paren">(delta backup, consolidate)</span></span>
57
58
  ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.archive.html">archive.py</a> <span class="comment"># 📦 Functions for creating and managing archives</span>
@@ -8,7 +8,7 @@ module-root = ""
8
8
 
9
9
  [project]
10
10
  name = "stouputils"
11
- version = "1.15.1"
11
+ version = "1.16.1"
12
12
  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."
13
13
  readme = "README.md"
14
14
  keywords = ["utilities", "tools", "helpers", "development", "python"]
@@ -1,18 +1,7 @@
1
- """ A collection of utility modules designed to simplify and enhance the development process.
2
-
3
- This package provides various tools and utilities for common development tasks including:
4
-
5
- Key Features:
6
- - Continuous delivery utilities (GitHub, PyPI)
7
- - Display and logging utilities (print)
8
- - File and I/O management (io)
9
- - Decorators for common patterns
10
- - Context managers
11
- - Archive and backup tools
12
- - Parallel processing helpers
13
- - Collection utilities
14
- - Doctests utilities
1
+ """ Stouputils is a collection of utility modules designed to simplify and enhance the development process.
2
+ It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers.
15
3
 
4
+ Check the documentation for more details: https://stoupy51.github.io/stouputils/
16
5
  """
17
6
  # Version (handle case where the package is not installed)
18
7
  from importlib.metadata import PackageNotFoundError
@@ -31,6 +20,7 @@ from .image import *
31
20
  from .io import *
32
21
  from .parallel import *
33
22
  from .print import *
23
+ from .typing import *
34
24
  from .version_pkg import *
35
25
 
36
26
  try:
@@ -10,6 +10,7 @@ from .image import *
10
10
  from .io import *
11
11
  from .parallel import *
12
12
  from .print import *
13
+ from .typing import *
13
14
  from .version_pkg import *
14
15
 
15
16
  __version__: str
@@ -7,12 +7,7 @@ import sys
7
7
 
8
8
  import argcomplete
9
9
 
10
- from .all_doctests import launch_tests
11
- from .archive import archive_cli
12
- from .backup import backup_cli
13
10
  from .decorators import handle_error
14
- from .print import CYAN, GREEN, RESET
15
- from .version_pkg import show_version_cli
16
11
 
17
12
  # Argument Parser Setup for Auto-Completion
18
13
  parser = argparse.ArgumentParser(prog="stouputils", add_help=False)
@@ -29,22 +24,28 @@ def main() -> None:
29
24
 
30
25
  # Print the version of stouputils and its dependencies
31
26
  if second_arg in ("--version", "-v", "version", "show_version"):
27
+ from .version_pkg import show_version_cli
32
28
  return show_version_cli()
33
29
 
34
30
  # Handle "all_doctests" command
35
31
  if second_arg == "all_doctests":
36
- if launch_tests("." if len(sys.argv) == 2 else sys.argv[2]) > 0:
32
+ root_dir: str = "." if len(sys.argv) == 2 else sys.argv[2]
33
+ pattern: str = sys.argv[3] if len(sys.argv) >= 4 else "*"
34
+ from .all_doctests import launch_tests
35
+ if launch_tests(root_dir, pattern=pattern) > 0:
37
36
  sys.exit(1)
38
37
  return
39
38
 
40
39
  # Handle "archive" command
41
40
  if second_arg == "archive":
42
41
  sys.argv.pop(1) # Remove "archive" from argv so archive_cli gets clean arguments
42
+ from .archive import archive_cli
43
43
  return archive_cli()
44
44
 
45
45
  # Handle "backup" command
46
46
  if second_arg == "backup":
47
47
  sys.argv.pop(1) # Remove "backup" from argv so backup_cli gets clean arguments
48
+ from .backup import backup_cli
48
49
  return backup_cli()
49
50
 
50
51
  # Handle "build" command
@@ -64,6 +65,7 @@ def main() -> None:
64
65
  pkg_version = "unknown"
65
66
 
66
67
  # Print help with nice formatting
68
+ from .print import CYAN, GREEN, RESET
67
69
  separator: str = "─" * 60
68
70
  print(f"""
69
71
  {CYAN}{separator}{RESET}
@@ -73,7 +75,7 @@ def main() -> None:
73
75
 
74
76
  {CYAN}Available commands:{RESET}
75
77
  {GREEN}--version, -v{RESET} [pkg] [-t <depth>] Show version information (optionally for a specific package)
76
- {GREEN}all_doctests{RESET} [dir] Run all doctests in the specified directory
78
+ {GREEN}all_doctests{RESET} [dir] [pattern] Run all doctests in the specified directory (optionally filter by pattern)
77
79
  {GREEN}archive{RESET} --help Archive utilities (make, repair)
78
80
  {GREEN}backup{RESET} --help Backup utilities (delta, consolidate, limit)
79
81
  {GREEN}build{RESET} [--no_stubs] [<minor|major>] Build and publish package to PyPI using 'uv' tool (complete routine)
@@ -9,29 +9,26 @@ This module is used to run all the doctests for all the modules in a given direc
9
9
  """
10
10
 
11
11
  # Imports
12
- import importlib
13
- import os
14
- import pkgutil
15
- import sys
16
- from types import ModuleType
17
12
  from typing import TYPE_CHECKING
18
13
 
19
14
  from . import decorators
20
15
  from .decorators import measure_time
21
16
  from .io import clean_path, relative_path
22
- from .print import error, info, progress, warning
17
+ from .print import error, info, warning
23
18
 
24
19
  if TYPE_CHECKING:
25
20
  from doctest import TestResults
21
+ from types import ModuleType
26
22
 
27
23
 
28
24
  # Main program
29
- def launch_tests(root_dir: str, strict: bool = True) -> int:
25
+ def launch_tests(root_dir: str, strict: bool = True, pattern: str = "*") -> int:
30
26
  """ Main function to launch tests for all modules in the given directory.
31
27
 
32
28
  Args:
33
29
  root_dir (str): Root directory to search for modules
34
30
  strict (bool): Modify the force_raise_exception variable to True in the decorators module
31
+ pattern (str): Pattern to filter module names (fnmatch style, e.g., '*typ*', 'io', etc.)
35
32
 
36
33
  Returns:
37
34
  int: The number of failed tests
@@ -62,11 +59,14 @@ def launch_tests(root_dir: str, strict: bool = True) -> int:
62
59
  strict = old_value
63
60
 
64
61
  # Get the path of the directory to check modules from
62
+ import os
65
63
  working_dir: str = clean_path(os.getcwd())
66
64
  root_dir = clean_path(os.path.abspath(root_dir))
67
65
  dir_to_check: str = os.path.dirname(root_dir) if working_dir != root_dir else root_dir
68
66
 
69
67
  # Get all modules from folder
68
+ import pkgutil
69
+ import sys
70
70
  sys.path.insert(0, dir_to_check)
71
71
  modules_file_paths: list[str] = []
72
72
  for directory_path, _, _ in os.walk(root_dir):
@@ -94,10 +94,21 @@ def launch_tests(root_dir: str, strict: bool = True) -> int:
94
94
  if not modules_file_paths:
95
95
  raise ValueError(f"No modules found in '{relative_path(root_dir)}'")
96
96
 
97
+ # Filter modules based on pattern
98
+ if pattern != "*":
99
+ import fnmatch
100
+ modules_file_paths = [
101
+ path for path in modules_file_paths
102
+ if fnmatch.fnmatch(path.split(".")[-1], pattern)
103
+ ]
104
+ if not modules_file_paths:
105
+ raise ValueError(f"No modules matching pattern '{pattern}' found in '{relative_path(root_dir)}'")
106
+
97
107
  # Find longest module path for alignment
98
108
  max_length: int = max(len(path) for path in modules_file_paths)
99
109
 
100
110
  # Dynamically import all modules from iacob package recursively using pkgutil and importlib
111
+ import importlib
101
112
  modules: list[ModuleType] = []
102
113
  separators: list[str] = []
103
114
  for module_path in modules_file_paths:
@@ -143,7 +154,7 @@ def launch_tests(root_dir: str, strict: bool = True) -> int:
143
154
  return total_failed
144
155
 
145
156
 
146
- def test_module_with_progress(module: ModuleType, separator: str) -> "TestResults":
157
+ def test_module_with_progress(module: ModuleType, separator: str) -> TestResults:
147
158
  """ Test a module with testmod and measure the time taken with progress printing.
148
159
 
149
160
  Args:
@@ -152,7 +163,7 @@ def test_module_with_progress(module: ModuleType, separator: str) -> "TestResult
152
163
  Returns:
153
164
  TestResults: The results of the tests
154
165
  """
155
- from doctest import TestResults, testmod
166
+ from doctest import testmod
156
167
  @measure_time(message=f"Testing module '{module.__name__}' {separator}took")
157
168
  def internal() -> TestResults:
158
169
  return testmod(m=module)
@@ -1,16 +1,17 @@
1
1
  from . import decorators as decorators
2
2
  from .decorators import measure_time as measure_time
3
3
  from .io import clean_path as clean_path, relative_path as relative_path
4
- from .print import error as error, info as info, progress as progress, warning as warning
4
+ from .print import error as error, info as info, warning as warning
5
5
  from doctest import TestResults as TestResults
6
6
  from types import ModuleType
7
7
 
8
- def launch_tests(root_dir: str, strict: bool = True) -> int:
8
+ def launch_tests(root_dir: str, strict: bool = True, pattern: str = '*') -> int:
9
9
  ''' Main function to launch tests for all modules in the given directory.
10
10
 
11
11
  \tArgs:
12
12
  \t\troot_dir\t\t\t\t(str):\t\t\tRoot directory to search for modules
13
13
  \t\tstrict\t\t\t\t\t(bool):\t\t\tModify the force_raise_exception variable to True in the decorators module
14
+ \t\tpattern\t\t\t\t\t(str):\t\t\tPattern to filter module names (fnmatch style, e.g., \'*typ*\', \'io\', etc.)
14
15
 
15
16
  \tReturns:
16
17
  \t\tint: The number of failed tests
@@ -141,6 +141,10 @@ def get_sphinx_conf_content(
141
141
  # Imports
142
142
  import sys
143
143
  from typing import Any
144
+ import typing
145
+
146
+ # Set TYPE_CHECKING to avoid import issues during documentation generation
147
+ typing.TYPE_CHECKING = True
144
148
 
145
149
  # Add project_dir directory to Python path for module discovery
146
150
  sys.path.insert(0, "{parent_of_project_dir}")
@@ -30,7 +30,7 @@ T = TypeVar("T")
30
30
 
31
31
  # Functions
32
32
  def unique_list[T](list_to_clean: Iterable[T], method: Literal["id", "hash", "str"] = "str") -> list[T]:
33
- """ Remove duplicates from the list while keeping the order using ids (default) or hash or str
33
+ """ Remove duplicates from the list while keeping the order using ids, hash, or str
34
34
 
35
35
  Args:
36
36
  list_to_clean (Iterable[T]): The list to clean
@@ -7,7 +7,7 @@ from typing import Any, Literal, TypeVar
7
7
  T = TypeVar('T')
8
8
 
9
9
  def unique_list[T](list_to_clean: Iterable[T], method: Literal['id', 'hash', 'str'] = 'str') -> list[T]:
10
- ''' Remove duplicates from the list while keeping the order using ids (default) or hash or str
10
+ ''' Remove duplicates from the list while keeping the order using ids, hash, or str
11
11
 
12
12
  \tArgs:
13
13
  \t\tlist_to_clean\t(Iterable[T]):\t\t\t\t\tThe list to clean
@@ -10,6 +10,7 @@
10
10
 
11
11
  # Imports
12
12
  import os
13
+ import subprocess
13
14
  import sys
14
15
  from collections.abc import Callable
15
16
  from typing import Any
@@ -22,17 +23,17 @@ def update_pip_and_required_packages() -> int:
22
23
  """ Update pip and required packages.
23
24
 
24
25
  Returns:
25
- int: Return code of the os.system call.
26
+ int: Return code of the subprocess.run call.
26
27
  """
27
- return os.system(f"{sys.executable} -m pip install --upgrade pip setuptools build twine pkginfo packaging")
28
+ return subprocess.run(f"{sys.executable} -m pip install --upgrade pip setuptools build twine pkginfo packaging", shell=True).returncode
28
29
 
29
30
  def build_package() -> int:
30
31
  """ Build the package.
31
32
 
32
33
  Returns:
33
- int: Return code of the os.system call.
34
+ int: Return code of the subprocess.run call.
34
35
  """
35
- return os.system(f"{sys.executable} -m build")
36
+ return subprocess.run(f"{sys.executable} -m build", shell=True).returncode
36
37
 
37
38
  def upload_package(repository: str, filepath: str) -> int:
38
39
  """ Upload the package to PyPI.
@@ -42,9 +43,9 @@ def upload_package(repository: str, filepath: str) -> int:
42
43
  filepath (str): Path to the file to upload.
43
44
 
44
45
  Returns:
45
- int: Return code of the os.system call.
46
+ int: Return code of the subprocess.run call.
46
47
  """
47
- return os.system(f"{sys.executable} -m twine upload --verbose -r {repository} {filepath}")
48
+ return subprocess.run(f"{sys.executable} -m twine upload --verbose -r {repository} {filepath}", shell=True).returncode
48
49
 
49
50
  @handle_error(message="Error while doing the pypi full routine", error_log=LogLevels.ERROR_TRACEBACK)
50
51
  def pypi_full_routine(
@@ -115,16 +116,16 @@ def pypi_full_routine_using_uv() -> None:
115
116
  # Increment version in pyproject.toml
116
117
  if "--no-bump" not in sys.argv and "--no_bump" not in sys.argv:
117
118
  increment: str = "patch" if sys.argv[-1] not in ("minor", "major") else sys.argv[-1]
118
- if os.system(f"uv version --bump {increment} --frozen") != 0:
119
+ if subprocess.run(f"uv version --bump {increment} --frozen", shell=True).returncode != 0:
119
120
  raise Exception("Error while incrementing version using 'uv version'")
120
121
 
121
122
  # Build the package using 'uv build'
122
123
  import shutil
123
124
  shutil.rmtree("dist", ignore_errors=True)
124
- if os.system(f"{sys.executable} -m uv build") != 0:
125
+ if subprocess.run(f"{sys.executable} -m uv build", shell=True).returncode != 0:
125
126
  raise Exception("Error while building the package using 'uv build'")
126
127
 
127
128
  # Upload the most recent file to PyPI using 'uv publish'
128
- if os.system(f"{sys.executable} -m uv publish") != 0:
129
+ if subprocess.run(f"{sys.executable} -m uv publish", shell=True).returncode != 0:
129
130
  raise Exception("Error while publishing the package using 'uv publish'")
130
131
 
@@ -6,13 +6,13 @@ def update_pip_and_required_packages() -> int:
6
6
  """ Update pip and required packages.
7
7
 
8
8
  \tReturns:
9
- \t\tint: Return code of the os.system call.
9
+ \t\tint: Return code of the subprocess.run call.
10
10
  \t"""
11
11
  def build_package() -> int:
12
12
  """ Build the package.
13
13
 
14
14
  \tReturns:
15
- \t\tint: Return code of the os.system call.
15
+ \t\tint: Return code of the subprocess.run call.
16
16
  \t"""
17
17
  def upload_package(repository: str, filepath: str) -> int:
18
18
  """ Upload the package to PyPI.
@@ -22,7 +22,7 @@ def upload_package(repository: str, filepath: str) -> int:
22
22
  \t\tfilepath (str): Path to the file to upload.
23
23
 
24
24
  \tReturns:
25
- \t\tint: Return code of the os.system call.
25
+ \t\tint: Return code of the subprocess.run call.
26
26
  \t"""
27
27
  def pypi_full_routine(repository: str, dist_directory: str, last_files: int = 1, endswith: str = '.tar.gz', update_all_function: Callable[[], int] = ..., build_package_function: Callable[[], int] = ..., upload_package_function: Callable[[str, str], int] = ...) -> None:
28
28
  ''' Upload the most recent file(s) to PyPI after updating pip and required packages and building the package.
@@ -24,7 +24,7 @@ def generate_stubs(
24
24
  package_name (str): Name of the package to generate stubs for.
25
25
  extra_args (str): Extra arguments to pass to stubgen. Defaults to "--include-docstrings --include-private".
26
26
  Returns:
27
- int: Return code of the os.system call.
27
+ int: 0 if successful, non-zero otherwise.
28
28
  """
29
29
  try:
30
30
  from mypy.stubgen import main as stubgen_main
@@ -10,7 +10,7 @@ def generate_stubs(package_name: str, extra_args: str = '--include-docstrings --
10
10
  \t\tpackage_name (str): Name of the package to generate stubs for.
11
11
  \t\textra_args (str): Extra arguments to pass to stubgen. Defaults to "--include-docstrings --include-private".
12
12
  \tReturns:
13
- \t\tint: Return code of the os.system call.
13
+ \t\tint: 0 if successful, non-zero otherwise.
14
14
  \t'''
15
15
  def clean_stubs_directory(output_directory: str, package_name: str) -> None:
16
16
  """ Clean the stubs directory by deleting all .pyi files.
@@ -2,11 +2,12 @@
2
2
  # Imports
3
3
  import argparse
4
4
  import os
5
+ import subprocess
5
6
  import sys
6
7
 
7
8
  from ...decorators import handle_error, measure_time
8
- from ...print import info
9
9
  from ...parallel import multithreading
10
+ from ...print import info
10
11
  from ..config.get import DataScienceConfig
11
12
  from ..dataset import LOWER_GS
12
13
  from ..models.all import ALL_MODELS, CLASS_MAP
@@ -122,7 +123,7 @@ def exhaustive_process(
122
123
  info(f"Executing command: '{cmd}'")
123
124
  sys.stdout.flush()
124
125
  sys.stderr.flush()
125
- os.system(cmd)
126
+ subprocess.run(cmd, shell=True)
126
127
  multithreading(
127
128
  runner,
128
129
  commands,
@@ -28,7 +28,7 @@ PIL_Image_or_NDArray = TypeVar("PIL_Image_or_NDArray", bound="Image.Image | NDAr
28
28
  def image_resize[PIL_Image_or_NDArray](
29
29
  image: PIL_Image_or_NDArray,
30
30
  max_result_size: int,
31
- resampling: "Image.Resampling | None" = None,
31
+ resampling: Image.Resampling | None = None,
32
32
  min_or_max: Callable[[int, int], int] = max,
33
33
  return_type: type[PIL_Image_or_NDArray] | str = "same",
34
34
  keep_aspect_ratio: bool = True,
@@ -37,7 +37,7 @@ def image_resize[PIL_Image_or_NDArray](
37
37
  Scales the image so that its largest dimension equals max_result_size.
38
38
 
39
39
  Args:
40
- image (Image.Image | np.ndarray): The image to resize.
40
+ image (Image.Image | NDArray): The image to resize.
41
41
  max_result_size (int): Maximum size for the largest dimension.
42
42
  resampling (Image.Resampling | None): PIL resampling filter to use (default: Image.Resampling.LANCZOS).
43
43
  min_or_max (Callable): Function to use to get the minimum or maximum of the two ratios.
@@ -84,7 +84,7 @@ def image_resize[PIL_Image_or_NDArray](
84
84
 
85
85
  # Convert numpy array to PIL Image if needed
86
86
  if not original_was_pil:
87
- image = Image.fromarray(image)
87
+ image = Image.fromarray(image) # type: ignore
88
88
 
89
89
  if keep_aspect_ratio:
90
90
 
@@ -123,8 +123,8 @@ def image_resize[PIL_Image_or_NDArray](
123
123
 
124
124
  def auto_crop[PIL_Image_or_NDArray](
125
125
  image: PIL_Image_or_NDArray,
126
- mask: "NDArray[np.bool_] | None" = None,
127
- threshold: int | float | Callable[["NDArray[np.number]"], int | float] | None = None,
126
+ mask: NDArray[np.bool_] | None = None,
127
+ threshold: int | float | Callable[[NDArray[np.number]], int | float] | None = None,
128
128
  return_type: type[PIL_Image_or_NDArray] | str = "same",
129
129
  contiguous: bool = True,
130
130
  ) -> Any:
@@ -263,7 +263,7 @@ def auto_crop[PIL_Image_or_NDArray](
263
263
 
264
264
  def numpy_to_gif(
265
265
  path: str,
266
- array: "NDArray[np.integer | np.floating | np.bool_]",
266
+ array: NDArray[np.integer | np.floating | np.bool_],
267
267
  duration: int = 100,
268
268
  loop: int = 0,
269
269
  mkdir: bool = True,
@@ -344,7 +344,7 @@ def numpy_to_gif(
344
344
 
345
345
  def numpy_to_obj(
346
346
  path: str,
347
- array: "NDArray[np.integer | np.floating | np.bool_]",
347
+ array: NDArray[np.integer | np.floating | np.bool_],
348
348
  threshold: float = 0.5,
349
349
  step_size: int = 1,
350
350
  pad_array: bool = True,
@@ -372,7 +372,6 @@ def numpy_to_obj(
372
372
  """
373
373
  # Imports
374
374
  import numpy as np
375
- from numpy.typing import NDArray
376
375
  from skimage import measure
377
376
 
378
377
  # Assertions
@@ -13,7 +13,7 @@ def image_resize[PIL_Image_or_NDArray](image: PIL_Image_or_NDArray, max_result_s
13
13
  \tScales the image so that its largest dimension equals max_result_size.
14
14
 
15
15
  \tArgs:
16
- \t\timage (Image.Image | np.ndarray): The image to resize.
16
+ \t\timage (Image.Image | NDArray): The image to resize.
17
17
  \t\tmax_result_size (int): Maximum size for the largest dimension.
18
18
  \t\tresampling (Image.Resampling | None): PIL resampling filter to use (default: Image.Resampling.LANCZOS).
19
19
  \t\tmin_or_max (Callable): Function to use to get the minimum or maximum of the two ratios.
@@ -432,36 +432,21 @@ def _set_process_priority(nice_value: int) -> None:
432
432
  if sys.platform == "win32":
433
433
  # Map Unix nice values to Windows priority classes
434
434
  # -20 to -10: HIGH, -9 to -1: ABOVE_NORMAL, 0: NORMAL, 1-9: BELOW_NORMAL, 10-19: IDLE
435
- try:
436
- import psutil
437
- if nice_value <= -10:
438
- priority = psutil.HIGH_PRIORITY_CLASS
439
- elif nice_value < 0:
440
- priority = psutil.ABOVE_NORMAL_PRIORITY_CLASS
441
- elif nice_value == 0:
442
- priority = psutil.NORMAL_PRIORITY_CLASS
443
- elif nice_value < 10:
444
- priority = psutil.BELOW_NORMAL_PRIORITY_CLASS
445
- else:
446
- priority = psutil.IDLE_PRIORITY_CLASS
447
- psutil.Process().nice(priority)
448
- except ImportError:
449
- # Fallback to ctypes if psutil is not available
450
- import ctypes
451
- # Windows priority class constants
452
- if nice_value <= -10:
453
- priority = 0x00000080 # HIGH_PRIORITY_CLASS
454
- elif nice_value < 0:
455
- priority = 0x00008000 # ABOVE_NORMAL_PRIORITY_CLASS
456
- elif nice_value == 0:
457
- priority = 0x00000020 # NORMAL_PRIORITY_CLASS
458
- elif nice_value < 10:
459
- priority = 0x00004000 # BELOW_NORMAL_PRIORITY_CLASS
460
- else:
461
- priority = 0x00000040 # IDLE_PRIORITY_CLASS
462
- kernel32 = ctypes.windll.kernel32
463
- handle = kernel32.GetCurrentProcess()
464
- kernel32.SetPriorityClass(handle, priority)
435
+ import ctypes
436
+ # Windows priority class constants
437
+ if nice_value <= -10:
438
+ priority = 0x00000080 # HIGH_PRIORITY_CLASS
439
+ elif nice_value < 0:
440
+ priority = 0x00008000 # ABOVE_NORMAL_PRIORITY_CLASS
441
+ elif nice_value == 0:
442
+ priority = 0x00000020 # NORMAL_PRIORITY_CLASS
443
+ elif nice_value < 10:
444
+ priority = 0x00004000 # BELOW_NORMAL_PRIORITY_CLASS
445
+ else:
446
+ priority = 0x00000040 # IDLE_PRIORITY_CLASS
447
+ kernel32 = ctypes.windll.kernel32
448
+ handle = kernel32.GetCurrentProcess()
449
+ kernel32.SetPriorityClass(handle, priority)
465
450
  else:
466
451
  # Unix-like systems
467
452
  os.nice(nice_value)
@@ -36,7 +36,8 @@ T = TypeVar("T")
36
36
 
37
37
  # Enable colors on Windows 10 terminal if applicable
38
38
  if os.name == "nt":
39
- os.system("color")
39
+ import subprocess
40
+ subprocess.run("color", shell=True)
40
41
 
41
42
  # Print functions
42
43
  previous_args_kwards: tuple[Any, Any] = ((), {})
@@ -0,0 +1,71 @@
1
+ """
2
+ This module provides utilities for typing enhancements such as JSON type aliases:
3
+ - JsonDict
4
+ - JsonList
5
+ - JsonMap
6
+ - MutJsonMap
7
+ - IterAny
8
+ """
9
+
10
+ # Imports
11
+ from collections.abc import Iterable, Mapping, MutableMapping
12
+ from dataclasses import asdict, is_dataclass
13
+ from typing import Any, cast
14
+
15
+ # Typing aliases
16
+ JsonDict = dict[str, Any]
17
+ """ A type alias for JSON dictionaries """
18
+ JsonList = list[Any]
19
+ """ A type alias for JSON lists """
20
+ JsonMap = Mapping[str, Any]
21
+ """ A type alias for JSON mapping """
22
+ MutJsonMap = MutableMapping[str, Any]
23
+ """ A type alias for mutable JSON mapping """
24
+ IterAny = Iterable[Any]
25
+ """ A type alias for iterable of any type """
26
+
27
+
28
+ ## Utility functions
29
+ def convert_to_serializable(obj: Any) -> Any:
30
+ """ Recursively convert objects to JSON-serializable forms.
31
+
32
+ Objects with a `to_dict()` or `asdict()` method are converted to their dictionary representation.
33
+ Dictionaries and lists are recursively processed.
34
+
35
+ Can also be used to convert nested structures containing custom objects,
36
+ such as defaultdict, dataclasses, or other user-defined types.
37
+
38
+ Args:
39
+ obj (Any): The object to convert
40
+ Returns:
41
+ Any: The JSON-serializable version of the object
42
+ Examples:
43
+ >>> from typing import defaultdict
44
+ >>> my_dict = defaultdict(lambda: defaultdict(int))
45
+ >>> my_dict['a']['b'] += 6
46
+ >>> my_dict['c']['d'] = 4
47
+ >>> my_dict['a']
48
+ defaultdict(<class 'int'>, {'b': 6})
49
+ >>> my_dict['c']
50
+ defaultdict(<class 'int'>, {'d': 4})
51
+ >>> convert_to_serializable(my_dict)
52
+ {'a': {'b': 6}, 'c': {'d': 4}}
53
+
54
+ >>> from dataclasses import dataclass
55
+ >>> @dataclass
56
+ ... class Point:
57
+ ... x: int
58
+ ... y: int
59
+ >>> convert_to_serializable(Point(3, 4))
60
+ {'x': 3, 'y': 4}
61
+ """
62
+ if hasattr(obj, "to_dict"):
63
+ return obj.to_dict()
64
+ elif is_dataclass(obj):
65
+ return asdict(obj) # pyright: ignore[reportArgumentType]
66
+ elif isinstance(obj, dict | Mapping | MutableMapping):
67
+ return {k: convert_to_serializable(v) for k, v in cast(JsonDict, obj).items()}
68
+ elif isinstance(obj, Iterable) and not isinstance(obj, (str, bytes)):
69
+ return [convert_to_serializable(item) for item in cast(IterAny, obj)]
70
+ return obj
71
+
@@ -0,0 +1,42 @@
1
+ from collections.abc import Iterable, Mapping, MutableMapping
2
+ from typing import Any
3
+
4
+ JsonDict = dict[str, Any]
5
+ JsonList = list[Any]
6
+ JsonMap = Mapping[str, Any]
7
+ MutJsonMap = MutableMapping[str, Any]
8
+ IterAny = Iterable[Any]
9
+
10
+ def convert_to_serializable(obj: Any) -> Any:
11
+ """ Recursively convert objects to JSON-serializable forms.
12
+
13
+ \tObjects with a `to_dict()` or `asdict()` method are converted to their dictionary representation.
14
+ \tDictionaries and lists are recursively processed.
15
+
16
+ \tCan also be used to convert nested structures containing custom objects,
17
+ \tsuch as defaultdict, dataclasses, or other user-defined types.
18
+
19
+ \tArgs:
20
+ \t\tobj (Any): The object to convert
21
+ \tReturns:
22
+ \t\tAny: The JSON-serializable version of the object
23
+ \tExamples:
24
+ \t\t>>> from typing import defaultdict
25
+ \t\t>>> my_dict = defaultdict(lambda: defaultdict(int))
26
+ \t\t>>> my_dict['a']['b'] += 6
27
+ \t\t>>> my_dict['c']['d'] = 4
28
+ \t\t>>> my_dict['a']
29
+ \t\tdefaultdict(<class 'int'>, {'b': 6})
30
+ \t\t>>> my_dict['c']
31
+ \t\tdefaultdict(<class 'int'>, {'d': 4})
32
+ \t\t>>> convert_to_serializable(my_dict)
33
+ \t\t{'a': {'b': 6}, 'c': {'d': 4}}
34
+
35
+ \t\t>>> from dataclasses import dataclass
36
+ \t\t>>> @dataclass
37
+ \t\t... class Point:
38
+ \t\t... x: int
39
+ \t\t... y: int
40
+ \t\t>>> convert_to_serializable(Point(3, 4))
41
+ \t\t{'x': 3, 'y': 4}
42
+ \t"""