stouputils 1.18.5__tar.gz → 1.19.0__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 (161) hide show
  1. {stouputils-1.18.5 → stouputils-1.19.0}/PKG-INFO +5 -4
  2. {stouputils-1.18.5 → stouputils-1.19.0}/README.md +4 -2
  3. {stouputils-1.18.5 → stouputils-1.19.0}/pyproject.toml +1 -2
  4. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/all_doctests.py +10 -3
  5. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/automatic_docs.py +44 -62
  6. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/automatic_docs.pyi +6 -12
  7. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/print.py +91 -44
  8. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/print.pyi +39 -17
  9. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/__init__.py +0 -0
  10. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/__init__.pyi +0 -0
  11. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/__main__.py +0 -0
  12. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/_deprecated.py +0 -0
  13. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/_deprecated.pyi +0 -0
  14. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/all_doctests.pyi +0 -0
  15. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/__init__.py +0 -0
  16. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/__init__.pyi +0 -0
  17. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/__init__.py +0 -0
  18. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/__init__.pyi +0 -0
  19. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/config.py +0 -0
  20. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/config.pyi +0 -0
  21. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/image.py +0 -0
  22. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/image.pyi +0 -0
  23. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/video.py +0 -0
  24. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/applications/upscaler/video.pyi +0 -0
  25. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/archive.py +0 -0
  26. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/archive.pyi +0 -0
  27. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/backup.py +0 -0
  28. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/backup.pyi +0 -0
  29. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/collections.py +0 -0
  30. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/collections.pyi +0 -0
  31. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/__init__.py +0 -0
  32. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/__init__.pyi +0 -0
  33. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/cd_utils.py +0 -0
  34. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/cd_utils.pyi +0 -0
  35. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/github.py +0 -0
  36. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/github.pyi +0 -0
  37. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/pypi.py +0 -0
  38. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/pypi.pyi +0 -0
  39. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/pyproject.py +0 -0
  40. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/pyproject.pyi +0 -0
  41. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/stubs.py +0 -0
  42. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/continuous_delivery/stubs.pyi +0 -0
  43. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/ctx.py +0 -0
  44. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/ctx.pyi +0 -0
  45. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/config/get.py +0 -0
  46. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/config/set.py +0 -0
  47. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/__init__.py +0 -0
  48. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/auto_contrast.py +0 -0
  49. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/axis_flip.py +0 -0
  50. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/bias_field_correction.py +0 -0
  51. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/binary_threshold.py +0 -0
  52. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/blur.py +0 -0
  53. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/brightness.py +0 -0
  54. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/canny.py +0 -0
  55. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/clahe.py +0 -0
  56. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/common.py +0 -0
  57. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/contrast.py +0 -0
  58. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/curvature_flow_filter.py +0 -0
  59. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/denoise.py +0 -0
  60. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/histogram_equalization.py +0 -0
  61. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/invert.py +0 -0
  62. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/laplacian.py +0 -0
  63. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/median_blur.py +0 -0
  64. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/noise.py +0 -0
  65. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/normalize.py +0 -0
  66. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/random_erase.py +0 -0
  67. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/resize.py +0 -0
  68. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/rotation.py +0 -0
  69. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/salt_pepper.py +0 -0
  70. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/sharpening.py +0 -0
  71. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/shearing.py +0 -0
  72. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/threshold.py +0 -0
  73. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/translation.py +0 -0
  74. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image/zoom.py +0 -0
  75. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image_augmentation.py +0 -0
  76. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/image_preprocess.py +0 -0
  77. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/prosthesis_detection.py +0 -0
  78. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/data_processing/technique.py +0 -0
  79. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/dataset/__init__.py +0 -0
  80. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/dataset/dataset.py +0 -0
  81. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/dataset/dataset_loader.py +0 -0
  82. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/dataset/grouping_strategy.py +0 -0
  83. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/dataset/image_loader.py +0 -0
  84. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/dataset/xy_tuple.py +0 -0
  85. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/metric_dictionnary.py +0 -0
  86. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/metric_utils.py +0 -0
  87. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/mlflow_utils.py +0 -0
  88. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/abstract_model.py +0 -0
  89. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/all.py +0 -0
  90. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/base_keras.py +0 -0
  91. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/all.py +0 -0
  92. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/convnext.py +0 -0
  93. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/densenet.py +0 -0
  94. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/efficientnet.py +0 -0
  95. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/mobilenet.py +0 -0
  96. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/resnet.py +0 -0
  97. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/squeezenet.py +0 -0
  98. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/vgg.py +0 -0
  99. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras/xception.py +0 -0
  100. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/callbacks/__init__.py +0 -0
  101. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +0 -0
  102. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +0 -0
  103. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +0 -0
  104. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +0 -0
  105. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +0 -0
  106. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/losses/__init__.py +0 -0
  107. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +0 -0
  108. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/keras_utils/visualizations.py +0 -0
  109. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/model_interface.py +0 -0
  110. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/models/sandbox.py +0 -0
  111. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/range_tuple.py +0 -0
  112. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/scripts/augment_dataset.py +0 -0
  113. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/scripts/exhaustive_process.py +0 -0
  114. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/scripts/preprocess_dataset.py +0 -0
  115. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/scripts/routine.py +0 -0
  116. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/data_science/utils.py +0 -0
  117. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/decorators.py +0 -0
  118. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/decorators.pyi +0 -0
  119. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/image.py +0 -0
  120. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/image.pyi +0 -0
  121. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/__init__.py +0 -0
  122. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/__init__.pyi +0 -0
  123. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/common.py +0 -0
  124. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/common.pyi +0 -0
  125. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/downloader.py +0 -0
  126. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/downloader.pyi +0 -0
  127. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/linux.py +0 -0
  128. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/linux.pyi +0 -0
  129. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/main.py +0 -0
  130. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/main.pyi +0 -0
  131. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/windows.py +0 -0
  132. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/installer/windows.pyi +0 -0
  133. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/io.py +0 -0
  134. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/io.pyi +0 -0
  135. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/__init__.py +0 -0
  136. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/__init__.pyi +0 -0
  137. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/base.py +0 -0
  138. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/base.pyi +0 -0
  139. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/queue.py +0 -0
  140. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/queue.pyi +0 -0
  141. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/re_entrant.py +0 -0
  142. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/re_entrant.pyi +0 -0
  143. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/redis_fifo.py +0 -0
  144. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/redis_fifo.pyi +0 -0
  145. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/shared.py +0 -0
  146. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/lock/shared.pyi +0 -0
  147. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/__init__.py +0 -0
  148. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/__init__.pyi +0 -0
  149. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/capturer.py +0 -0
  150. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/capturer.pyi +0 -0
  151. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/common.py +0 -0
  152. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/common.pyi +0 -0
  153. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/multi.py +0 -0
  154. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/multi.pyi +0 -0
  155. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/subprocess.py +0 -0
  156. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/parallel/subprocess.pyi +0 -0
  157. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/py.typed +0 -0
  158. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/typing.py +0 -0
  159. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/typing.pyi +0 -0
  160. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/version_pkg.py +0 -0
  161. {stouputils-1.18.5 → stouputils-1.19.0}/stouputils/version_pkg.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stouputils
3
- Version: 1.18.5
3
+ Version: 1.19.0
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
@@ -26,7 +26,6 @@ Requires-Dist: tensorflow ; extra == 'data-science'
26
26
  Requires-Dist: scikit-learn ; extra == 'data-science'
27
27
  Requires-Dist: pywavelets ; extra == 'data-science'
28
28
  Requires-Dist: mypy ; extra == 'docs'
29
- Requires-Dist: m2r2 ; extra == 'docs'
30
29
  Requires-Dist: myst-parser ; extra == 'docs'
31
30
  Requires-Dist: sphinx-copybutton ; extra == 'docs'
32
31
  Requires-Dist: sphinx-design ; extra == 'docs'
@@ -54,9 +53,11 @@ Stouputils is a collection of utility modules designed to simplify and enhance t
54
53
  It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers.<br>
55
54
  Start now by installing the package: `pip install stouputils`.<br>
56
55
 
57
- <a href="https://colab.research.google.com/drive/1mJ-KL-zXzIk1oKDxO6FC1SFfm-BVKG-P?usp=sharing" target="_blank" rel="noopener noreferrer" style="text-decoration: none;"><div class="admonition">
56
+ <a href="https://colab.research.google.com/drive/1mJ-KL-zXzIk1oKDxO6FC1SFfm-BVKG-P?usp=sharing" target="_blank" rel="noopener noreferrer" style="text-decoration: none;">
57
+ <div class="admonition" style="padding-bottom: 1rem;">
58
58
  📖 <b>Want to see examples?</b> Check out our <u>Google Colab notebook</u> with practical usage examples!
59
- </div></a>
59
+ </div>
60
+ </a>
60
61
 
61
62
  ## 🚀 Project File Tree
62
63
  <html>
@@ -9,9 +9,11 @@ Stouputils is a collection of utility modules designed to simplify and enhance t
9
9
  It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers.<br>
10
10
  Start now by installing the package: `pip install stouputils`.<br>
11
11
 
12
- <a href="https://colab.research.google.com/drive/1mJ-KL-zXzIk1oKDxO6FC1SFfm-BVKG-P?usp=sharing" target="_blank" rel="noopener noreferrer" style="text-decoration: none;"><div class="admonition">
12
+ <a href="https://colab.research.google.com/drive/1mJ-KL-zXzIk1oKDxO6FC1SFfm-BVKG-P?usp=sharing" target="_blank" rel="noopener noreferrer" style="text-decoration: none;">
13
+ <div class="admonition" style="padding-bottom: 1rem;">
13
14
  📖 <b>Want to see examples?</b> Check out our <u>Google Colab notebook</u> with practical usage examples!
14
- </div></a>
15
+ </div>
16
+ </a>
15
17
 
16
18
  ## 🚀 Project File Tree
17
19
  <html>
@@ -8,7 +8,7 @@ module-root = ""
8
8
 
9
9
  [project]
10
10
  name = "stouputils"
11
- version = "1.18.5"
11
+ version = "1.19.0"
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"]
@@ -37,7 +37,6 @@ authors = [
37
37
  [project.optional-dependencies]
38
38
  docs = [
39
39
  "mypy",
40
- "m2r2",
41
40
  "myst_parser",
42
41
  "sphinx_copybutton",
43
42
  "sphinx_design",
@@ -94,15 +94,22 @@ def launch_tests(root_dir: str, strict: bool = True, pattern: str = "*") -> int:
94
94
  if not modules_file_paths:
95
95
  raise ValueError(f"No modules found in '{relative_path(root_dir)}'")
96
96
 
97
+ # Sort module by number of submodules and alphabetically
98
+ modules_file_paths.sort(key=lambda x: (x.count('.'), x))
99
+
97
100
  # Filter modules based on pattern
98
101
  if pattern != "*":
99
102
  import fnmatch
100
- modules_file_paths = [
103
+ new_paths: list[str] = [
101
104
  path for path in modules_file_paths
102
105
  if fnmatch.fnmatch(path, pattern)
103
106
  ]
104
- if not modules_file_paths:
105
- raise ValueError(f"No modules matching pattern '{pattern}' found in '{relative_path(root_dir)}'")
107
+ if not new_paths:
108
+ raise ValueError(
109
+ f"No modules matching pattern '{pattern}' found in '{relative_path(root_dir)}'.\n"
110
+ f"Candidates were: {', '.join(relative_path(p) for p in modules_file_paths)[:500]}..."
111
+ )
112
+ modules_file_paths = new_paths
106
113
 
107
114
  # Find longest module path for alignment
108
115
  max_length: int = max(len(path) for path in modules_file_paths)
@@ -68,8 +68,6 @@ Example of GitHub Actions workflow:
68
68
  # Imports
69
69
  import os
70
70
  import shutil
71
- import subprocess
72
- import sys
73
71
  from collections.abc import Callable
74
72
  from typing import Any
75
73
 
@@ -79,7 +77,7 @@ from ..io import clean_path, json_dump, super_open
79
77
  from ..print import info
80
78
 
81
79
  # Constants
82
- REQUIREMENTS: list[str] = ["m2r2", "myst_parser"]
80
+ REQUIREMENTS: list[str] = ["myst_parser"]
83
81
  """ List of requirements for automatic_docs to work. """
84
82
 
85
83
  # Functions
@@ -185,6 +183,12 @@ copybutton_selector = ":not(.prompt) > div.highlight pre"
185
183
  templates_path: list[str] = ["_templates"]
186
184
  exclude_patterns: list[str] = []
187
185
 
186
+ # Allow both .rst and .md (MyST) sources
187
+ source_suffix = {{
188
+ ".rst": "restructuredtext",
189
+ ".md": "markdown",
190
+ }}
191
+
188
192
  # HTML output options
189
193
  html_theme: str = "{html_theme}"
190
194
  html_static_path: list[str] = ["_static"]
@@ -205,7 +209,6 @@ html_theme_options: dict[str, Any] = {{
205
209
  "github_repo": github_repo,
206
210
  "github_version": "main",
207
211
  "conf_py_path": "/docs/source/",
208
- "source_suffix": [".rst", ".md"],
209
212
  "default_mode": "dark",
210
213
  }
211
214
 
@@ -318,23 +321,7 @@ def get_versions_from_github(github_user: str, github_repo: str, recent_minor_ve
318
321
  version_list = ["latest"]
319
322
  return version_list
320
323
 
321
- def markdown_to_rst(markdown_content: str) -> str:
322
- """ Convert markdown content to RST format.
323
-
324
- Args:
325
- markdown_content (str): Markdown content
326
-
327
- Returns:
328
- str: RST content
329
- """
330
- if not markdown_content:
331
- return ""
332
-
333
- # Convert markdown to RST and return it
334
- import m2r2 # type: ignore
335
- return m2r2.convert(markdown_content) # type: ignore
336
-
337
- def generate_index_rst(
324
+ def generate_index_md(
338
325
  readme_path: str,
339
326
  index_path: str,
340
327
  project: str,
@@ -343,11 +330,14 @@ def generate_index_rst(
343
330
  get_versions_function: Callable[[str, str, int], list[str]] = get_versions_from_github,
344
331
  recent_minor_versions: int = 2,
345
332
  ) -> None:
346
- """ Generate index.rst from README.md content.
333
+ """ Generate `index.md` (MyST) from README.md content.
334
+
335
+ This keeps the README content as Markdown (no conversion) and uses the MyST
336
+ `toctree` directive to include module docs.
347
337
 
348
338
  Args:
349
339
  readme_path (str): Path to the README.md file
350
- index_path (str): Path where index.rst should be created
340
+ index_path (str): Path where index.md should be created
351
341
  project (str): Name of the project
352
342
  github_user (str): GitHub username
353
343
  github_repo (str): GitHub repository name
@@ -358,50 +348,44 @@ def generate_index_rst(
358
348
  with open(readme_path, encoding="utf-8") as f:
359
349
  readme_content: str = f.read()
360
350
 
361
- # Generate version selector
362
- version_selector: str = "\n\n**Versions**: "
363
-
364
- # Get versions from GitHub
351
+ # Generate version selector (markdown links)
365
352
  version_list: list[str] = get_versions_function(github_user, github_repo, recent_minor_versions)
366
-
367
- # Create version links
368
353
  version_links: list[str] = []
369
354
  for version in version_list:
370
355
  if version == "latest":
371
- version_links.append("`latest <../latest/>`_")
356
+ version_links.append("[latest](../latest/)")
372
357
  else:
373
- version_links.append(f"`v{version} <../v{version}/>`_")
374
- version_selector += ", ".join(version_links)
358
+ version_links.append(f"[v{version}](../v{version}/)")
359
+ version_selector: str = "\n\n**Versions**: " + ", ".join(version_links)
375
360
 
376
- # Generate module documentation section
361
+ # Module documentation toctree (MyST)
377
362
  project_module: str = project.lower()
378
363
  module_docs: str = f"""
379
- .. toctree::
380
- :maxdepth: 10
364
+ ```{{toctree}}
365
+ :maxdepth: 10
381
366
 
382
- modules/{project_module}
367
+ modules/{project_module}
368
+ ```
383
369
  """
384
- module_docs = markdown_to_rst(f"""
385
- Here is the complete unsorted documentation for all modules in the {project} project.<br>
386
- Prefer to use the search button at the top to find what you need!
387
- """) + module_docs
388
-
389
- # Convert markdown to RST
390
- rst_content: str = f"""
391
- ✨ Welcome to {project.capitalize()} Documentation ✨
392
- {'=' * 100}
370
+
371
+ # Build final markdown content
372
+ md_content: str = f"""
373
+ # ✨ Welcome to {project.capitalize()} Documentation
374
+
393
375
  {version_selector}
394
376
 
395
- {markdown_to_rst(readme_content)}
377
+ {readme_content}
378
+
379
+ ---
380
+
381
+ ## Module Documentation
396
382
 
397
- 📖 Module Documentation
398
- {'-' * 100}
399
383
  {module_docs}
400
384
  """
401
385
 
402
- # Write the RST file
386
+ # Write the Markdown file
403
387
  with open(index_path, "w", encoding="utf-8") as f:
404
- f.write(rst_content)
388
+ f.write(md_content)
405
389
 
406
390
  def generate_documentation(
407
391
  source_dir: str,
@@ -418,9 +402,8 @@ def generate_documentation(
418
402
  build_dir (str): Build directory
419
403
  """
420
404
  # Generate module documentation using sphinx-apidoc
421
- subprocess.run([
422
- sys.executable,
423
- "-m", "sphinx.ext.apidoc",
405
+ from sphinx.ext.apidoc import main as sphinx_apidoc_main
406
+ sphinx_apidoc_main([
424
407
  "-o", modules_dir,
425
408
  "-f", "-e", "-M",
426
409
  "--no-toc",
@@ -428,17 +411,16 @@ def generate_documentation(
428
411
  "--implicit-namespaces",
429
412
  "--module-first",
430
413
  project_dir,
431
- ], check=True)
414
+ ])
432
415
 
433
416
  # Build HTML documentation
434
- subprocess.run([
435
- sys.executable,
436
- "-m", "sphinx",
417
+ from sphinx.cmd.build import main as sphinx_build_main
418
+ sphinx_build_main([
437
419
  "-b", "html",
438
420
  "-a",
439
421
  source_dir,
440
422
  build_dir,
441
- ], check=True)
423
+ ])
442
424
 
443
425
  def generate_redirect_html(filepath: str) -> None:
444
426
  """ Generate HTML content for redirect page.
@@ -478,7 +460,7 @@ def update_documentation(
478
460
  recent_minor_versions: int = 2,
479
461
 
480
462
  get_versions_function: Callable[[str, str, int], list[str]] = get_versions_from_github,
481
- generate_index_function: Callable[..., None] = generate_index_rst,
463
+ generate_index_function: Callable[..., None] = generate_index_md,
482
464
  generate_docs_function: Callable[..., None] = generate_documentation,
483
465
  generate_redirect_function: Callable[[str], None] = generate_redirect_html,
484
466
  get_conf_content_function: Callable[..., str] = get_sphinx_conf_content
@@ -501,7 +483,7 @@ def update_documentation(
501
483
  recent_minor_versions (int): Number of recent minor versions to show all patches for. Defaults to 2
502
484
 
503
485
  get_versions_function (Callable[[str, str, int], list[str]]): Function to get versions from GitHub
504
- generate_index_function (Callable[..., None]): Function to generate index.rst
486
+ generate_index_function (Callable[..., None]): Function to generate index.md
505
487
  generate_docs_function (Callable[..., None]): Function to generate documentation
506
488
  generate_redirect_function (Callable[[str], None]): Function to create redirect file
507
489
  get_conf_content_function (Callable[..., str]): Function to get Sphinx conf.py content
@@ -573,9 +555,9 @@ a:hover {
573
555
  }
574
556
  """)
575
557
 
576
- # Generate index.rst from README.md
558
+ # Generate index.md from README.md (use MyST instead of converting to RST)
577
559
  readme_path: str = f"{root_path}/README.md"
578
- index_path: str = f"{source_dir}/index.rst"
560
+ index_path: str = f"{source_dir}/index.md"
579
561
  generate_index_function(
580
562
  readme_path=readme_path,
581
563
  index_path=index_path,
@@ -44,21 +44,15 @@ def get_versions_from_github(github_user: str, github_repo: str, recent_minor_ve
44
44
  \tReturns:
45
45
  \t\tlist[str]: List of versions, with 'latest' as first element
46
46
  \t"""
47
- def markdown_to_rst(markdown_content: str) -> str:
48
- """ Convert markdown content to RST format.
47
+ def generate_index_md(readme_path: str, index_path: str, project: str, github_user: str, github_repo: str, get_versions_function: Callable[[str, str, int], list[str]] = ..., recent_minor_versions: int = 2) -> None:
48
+ """ Generate `index.md` (MyST) from README.md content.
49
49
 
50
- \tArgs:
51
- \t\tmarkdown_content (str): Markdown content
52
-
53
- \tReturns:
54
- \t\tstr: RST content
55
- \t"""
56
- def generate_index_rst(readme_path: str, index_path: str, project: str, github_user: str, github_repo: str, get_versions_function: Callable[[str, str, int], list[str]] = ..., recent_minor_versions: int = 2) -> None:
57
- """ Generate index.rst from README.md content.
50
+ \tThis keeps the README content as Markdown (no conversion) and uses the MyST
51
+ \t`toctree` directive to include module docs.
58
52
 
59
53
  \tArgs:
60
54
  \t\treadme_path (str): Path to the README.md file
61
- \t\tindex_path (str): Path where index.rst should be created
55
+ \t\tindex_path (str): Path where index.md should be created
62
56
  \t\tproject (str): Name of the project
63
57
  \t\tgithub_user (str): GitHub username
64
58
  \t\tgithub_repo (str): GitHub repository name
@@ -99,7 +93,7 @@ def update_documentation(root_path: str, project: str, project_dir: str = '', au
99
93
  \t\trecent_minor_versions (int): Number of recent minor versions to show all patches for. Defaults to 2
100
94
 
101
95
  \t\tget_versions_function (Callable[[str, str, int], list[str]]): Function to get versions from GitHub
102
- \t\tgenerate_index_function (Callable[..., None]): Function to generate index.rst
96
+ \t\tgenerate_index_function (Callable[..., None]): Function to generate index.md
103
97
  \t\tgenerate_docs_function (Callable[..., None]): Function to generate documentation
104
98
  \t\tgenerate_redirect_function (Callable[[str], None]): Function to create redirect file
105
99
  \t\tget_conf_content_function (Callable[..., str]): Function to get Sphinx conf.py content
@@ -107,45 +107,67 @@ def format_colored(*values: Any) -> str:
107
107
  Examples:
108
108
  >>> # Test function names with parentheses
109
109
  >>> result = format_colored("Call print() with 42 items")
110
- >>> result.count(MAGENTA) == 2 # print and 42
111
- True
110
+ >>> result.count(MAGENTA) # print and 42
111
+ 2
112
112
 
113
113
  >>> # Test function names without parentheses
114
114
  >>> result = format_colored("Use len and sum functions")
115
- >>> result.count(MAGENTA) == 2 # len and sum
116
- True
115
+ >>> result.count(MAGENTA) # len and sum
116
+ 2
117
117
 
118
118
  >>> # Test exceptions (bold magenta)
119
119
  >>> result = format_colored("Got ValueError when parsing")
120
- >>> result.count(MAGENTA) == 1 and result.count(BOLD) == 1 # ValueError in bold magenta
121
- True
120
+ >>> result.count(MAGENTA), result.count(BOLD) # ValueError in bold magenta
121
+ (1, 1)
122
122
 
123
123
  >>> # Test file paths
124
124
  >>> result = format_colored("Processing ./data.csv file")
125
- >>> result.count(MAGENTA) == 1 # ./data.csv
126
- True
125
+ >>> result.count(MAGENTA) # ./data.csv
126
+ 1
127
127
 
128
128
  >>> # Test file paths with quotes
129
129
  >>> result = format_colored('File "/path/to/script.py" line 42')
130
- >>> result.count(MAGENTA) == 2 # /path/to/script.py and 42
131
- True
130
+ >>> result.count(MAGENTA) # /path/to/script.py and 42
131
+ 2
132
132
 
133
133
  >>> # Test numbers
134
- >>> result = format_colored("Found 100 items and 3.14 value")
135
- >>> result.count(MAGENTA) == 2 # 100 and 3.14
136
- True
134
+ >>> result = format_colored("Found 100 items and 3.14 value, 3.0e+10 is big")
135
+ >>> result.count(MAGENTA) # 100 and 3.14
136
+ 3
137
137
 
138
138
  >>> # Test mixed content
139
139
  >>> result = format_colored("Call sum() got IndexError at line 256 in utils.py")
140
- >>> result.count(MAGENTA) == 3 # sum, IndexError (bold), and 256
141
- True
142
- >>> result.count(BOLD) == 1 # IndexError is bold
143
- True
140
+ >>> result.count(MAGENTA) # sum, IndexError (bold), and 256
141
+ 3
142
+ >>> result.count(BOLD) # IndexError is bold
143
+ 1
144
+
145
+ >>> # Test keywords always colored
146
+ >>> result = format_colored("Check class dtype type")
147
+ >>> result.count(MAGENTA) # class, dtype, type
148
+ 3
144
149
 
145
150
  >>> # Test plain text (no coloring)
146
151
  >>> result = format_colored("This is plain text")
147
152
  >>> result.count(MAGENTA) == 0 and result == "This is plain text"
148
153
  True
154
+
155
+ >>> # Affix punctuation should not be colored (assert exact coloring, punctuation uncolored)
156
+ >>> result = format_colored("<class")
157
+ >>> result == "<" + MAGENTA + "class" + RESET
158
+ True
159
+ >>> result = format_colored("(dtype:")
160
+ >>> result == "(" + MAGENTA + "dtype" + RESET + ":"
161
+ True
162
+ >>> result = format_colored("[1.")
163
+ >>> result == "[" + MAGENTA + "1" + RESET + "."
164
+ True
165
+
166
+ >>> # Test complex
167
+ >>> text = "<class 'numpy.ndarray'>, <id 140357548266896>: (dtype: float32, shape: (6,), min: 0.0, max: 1.0) [1. 0. 0. 0. 1. 0.]"
168
+ >>> result = format_colored(text)
169
+ >>> result.count(MAGENTA) # class, numpy, ndarray, float32, 6, 0.0, 1.0, 1. 0.
170
+ 16
149
171
  """
150
172
  import builtins
151
173
  import re
@@ -163,6 +185,9 @@ def format_colored(*values: Any) -> str:
163
185
  and issubclass(getattr(builtins, name), BaseException))
164
186
  }
165
187
 
188
+ # Additional keywords always colored (case-insensitive on stripped words)
189
+ KEYWORDS: set[str] = {"class", "dtype", "type"}
190
+
166
191
  def is_filepath(word: str) -> bool:
167
192
  """ Check if a word looks like a file path """
168
193
  # Remove quotes if present
@@ -187,7 +212,7 @@ def format_colored(*values: Any) -> str:
187
212
 
188
213
  def is_number(word: str) -> bool:
189
214
  try:
190
- float(word.strip('.,;:!?'))
215
+ float(''.join(c for c in word if c.isdigit() or c in '.-+e'))
191
216
  return True
192
217
  except ValueError:
193
218
  return False
@@ -201,7 +226,22 @@ def format_colored(*values: Any) -> str:
201
226
 
202
227
  def is_exception(word: str) -> bool:
203
228
  """ Check if a word is a known exception name """
204
- return word.strip('.,;:!?') in EXCEPTION_NAMES
229
+ return ''.join(c for c in word if c.isalnum()) in EXCEPTION_NAMES
230
+
231
+ def is_keyword(word: str) -> bool:
232
+ """ Check if a word is one of the always-colored keywords """
233
+ clean_alnum = ''.join(c for c in word if c.isalnum())
234
+ return clean_alnum in KEYWORDS
235
+
236
+ def split_affixes(w: str) -> tuple[str, str, str]:
237
+ """ Split leading/trailing non-word characters and return (prefix, core, suffix).
238
+
239
+ This preserves punctuation like '<', '(', '[', '"', etc., while operating on the core text.
240
+ """
241
+ m = re.match(r'^(\W*)(.*?)(\W*)$', w, re.ASCII)
242
+ if m:
243
+ return m.group(1), m.group(2), m.group(3)
244
+ return "", w, ""
205
245
 
206
246
  # Convert all values to strings and join them and split into words while preserving separators
207
247
  text: str = " ".join(str(v) for v in values)
@@ -219,36 +259,43 @@ def format_colored(*values: Any) -> str:
219
259
  i += 1
220
260
  continue
221
261
 
222
- # Try to identify and color the word
262
+ # If the whole token looks like a filepath (e.g. './data.csv' or '/path/to/file'), color it as-is
223
263
  colored: bool = False
224
264
  if is_filepath(word):
225
265
  colored_words.append(f"{MAGENTA}{word}{RESET}")
226
266
  colored = True
227
- elif is_exception(word):
228
- colored_words.append(f"{BOLD}{MAGENTA}{word}{RESET}")
229
- colored = True
230
- elif is_number(word):
231
- # Preserve punctuation
232
- clean_word = word.strip('.,;:!?')
233
- prefix = word[:len(word) - len(word.lstrip('.,;:!?'))]
234
- suffix = word[len(clean_word) + len(prefix):]
235
- colored_words.append(f"{prefix}{MAGENTA}{clean_word}{RESET}{suffix}")
236
- colored = True
237
- elif is_function_name(word)[0]:
238
- func_name = is_function_name(word)[1]
239
- # Find where the function name ends in the original word
240
- func_start = word.find(func_name)
241
- if func_start != -1:
242
- prefix = word[:func_start]
243
- func_end = func_start + len(func_name)
244
- suffix = word[func_end:]
245
- colored_words.append(f"{prefix}{MAGENTA}{func_name}{RESET}{suffix}")
246
- else:
247
- # Fallback if we can't find it (shouldn't happen)
248
- colored_words.append(f"{MAGENTA}{word}{RESET}")
249
- colored = True
267
+ else:
268
+ # Split affixes to preserve punctuation like '<', '(', '[' etc.
269
+ prefix, core, suffix = split_affixes(word)
270
+
271
+ # Try to identify and color the word (operate on core where applicable)
272
+ if is_filepath(core):
273
+ colored_words.append(f"{prefix}{MAGENTA}{core}{RESET}{suffix}")
274
+ colored = True
275
+ elif is_exception(core):
276
+ colored_words.append(f"{prefix}{BOLD}{MAGENTA}{core}{RESET}{suffix}")
277
+ colored = True
278
+ elif is_number(core):
279
+ colored_words.append(f"{prefix}{MAGENTA}{core}{RESET}{suffix}")
280
+ colored = True
281
+ elif is_keyword(core):
282
+ colored_words.append(f"{prefix}{MAGENTA}{core}{RESET}{suffix}")
283
+ colored = True
284
+ elif is_function_name(core)[0]:
285
+ func_name = is_function_name(core)[1]
286
+ # Find where the function name ends in the core
287
+ func_start = core.find(func_name)
288
+ if func_start != -1:
289
+ pre_core = core[:func_start]
290
+ func_end = func_start + len(func_name)
291
+ post_core = core[func_end:]
292
+ colored_words.append(f"{prefix}{pre_core}{MAGENTA}{func_name}{RESET}{post_core}{suffix}")
293
+ else:
294
+ # Fallback if we can't find it (shouldn't happen)
295
+ colored_words.append(f"{prefix}{MAGENTA}{core}{RESET}{suffix}")
296
+ colored = True
250
297
 
251
- # If nothing matched, keep the word as is
298
+ # If nothing matched, keep the original word
252
299
  if not colored:
253
300
  colored_words.append(word)
254
301
  i += 1
@@ -53,45 +53,67 @@ def format_colored(*values: Any) -> str:
53
53
  \tExamples:
54
54
  \t\t>>> # Test function names with parentheses
55
55
  \t\t>>> result = format_colored("Call print() with 42 items")
56
- \t\t>>> result.count(MAGENTA) == 2 # print and 42
57
- \t\tTrue
56
+ \t\t>>> result.count(MAGENTA) # print and 42
57
+ \t\t2
58
58
 
59
59
  \t\t>>> # Test function names without parentheses
60
60
  \t\t>>> result = format_colored("Use len and sum functions")
61
- \t\t>>> result.count(MAGENTA) == 2 # len and sum
62
- \t\tTrue
61
+ \t\t>>> result.count(MAGENTA) # len and sum
62
+ \t\t2
63
63
 
64
64
  \t\t>>> # Test exceptions (bold magenta)
65
65
  \t\t>>> result = format_colored("Got ValueError when parsing")
66
- \t\t>>> result.count(MAGENTA) == 1 and result.count(BOLD) == 1 # ValueError in bold magenta
67
- \t\tTrue
66
+ \t\t>>> result.count(MAGENTA), result.count(BOLD) # ValueError in bold magenta
67
+ \t\t(1, 1)
68
68
 
69
69
  \t\t>>> # Test file paths
70
70
  \t\t>>> result = format_colored("Processing ./data.csv file")
71
- \t\t>>> result.count(MAGENTA) == 1 # ./data.csv
72
- \t\tTrue
71
+ \t\t>>> result.count(MAGENTA) # ./data.csv
72
+ \t\t1
73
73
 
74
74
  \t\t>>> # Test file paths with quotes
75
75
  \t\t>>> result = format_colored(\'File "/path/to/script.py" line 42\')
76
- \t\t>>> result.count(MAGENTA) == 2 # /path/to/script.py and 42
77
- \t\tTrue
76
+ \t\t>>> result.count(MAGENTA) # /path/to/script.py and 42
77
+ \t\t2
78
78
 
79
79
  \t\t>>> # Test numbers
80
- \t\t>>> result = format_colored("Found 100 items and 3.14 value")
81
- \t\t>>> result.count(MAGENTA) == 2 # 100 and 3.14
82
- \t\tTrue
80
+ \t\t>>> result = format_colored("Found 100 items and 3.14 value, 3.0e+10 is big")
81
+ \t\t>>> result.count(MAGENTA) # 100 and 3.14
82
+ \t\t3
83
83
 
84
84
  \t\t>>> # Test mixed content
85
85
  \t\t>>> result = format_colored("Call sum() got IndexError at line 256 in utils.py")
86
- \t\t>>> result.count(MAGENTA) == 3 # sum, IndexError (bold), and 256
87
- \t\tTrue
88
- \t\t>>> result.count(BOLD) == 1 # IndexError is bold
89
- \t\tTrue
86
+ \t\t>>> result.count(MAGENTA) # sum, IndexError (bold), and 256
87
+ \t\t3
88
+ \t\t>>> result.count(BOLD) # IndexError is bold
89
+ \t\t1
90
+
91
+ \t\t>>> # Test keywords always colored
92
+ \t\t>>> result = format_colored("Check class dtype type")
93
+ \t\t>>> result.count(MAGENTA) # class, dtype, type
94
+ \t\t3
90
95
 
91
96
  \t\t>>> # Test plain text (no coloring)
92
97
  \t\t>>> result = format_colored("This is plain text")
93
98
  \t\t>>> result.count(MAGENTA) == 0 and result == "This is plain text"
94
99
  \t\tTrue
100
+
101
+ \t\t>>> # Affix punctuation should not be colored (assert exact coloring, punctuation uncolored)
102
+ \t\t>>> result = format_colored("<class")
103
+ \t\t>>> result == "<" + MAGENTA + "class" + RESET
104
+ \t\tTrue
105
+ \t\t>>> result = format_colored("(dtype:")
106
+ \t\t>>> result == "(" + MAGENTA + "dtype" + RESET + ":"
107
+ \t\tTrue
108
+ \t\t>>> result = format_colored("[1.")
109
+ \t\t>>> result == "[" + MAGENTA + "1" + RESET + "."
110
+ \t\tTrue
111
+
112
+ \t\t>>> # Test complex
113
+ \t\t>>> text = "<class \'numpy.ndarray\'>, <id 140357548266896>: (dtype: float32, shape: (6,), min: 0.0, max: 1.0) [1. 0. 0. 0. 1. 0.]"
114
+ \t\t>>> result = format_colored(text)
115
+ \t\t>>> result.count(MAGENTA) # class, numpy, ndarray, float32, 6, 0.0, 1.0, 1. 0.
116
+ \t\t16
95
117
  \t'''
96
118
  def colored(*values: Any, file: TextIO | None = None, **print_kwargs: Any) -> None:
97
119
  ''' Print with Python 3.14 style colored formatting.