stouputils 1.14.0__py3-none-any.whl → 1.14.2__py3-none-any.whl

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 (108) hide show
  1. stouputils/__init__.pyi +15 -0
  2. stouputils/_deprecated.pyi +12 -0
  3. stouputils/all_doctests.pyi +46 -0
  4. stouputils/applications/__init__.pyi +2 -0
  5. stouputils/applications/automatic_docs.py +3 -0
  6. stouputils/applications/automatic_docs.pyi +106 -0
  7. stouputils/applications/upscaler/__init__.pyi +3 -0
  8. stouputils/applications/upscaler/config.pyi +18 -0
  9. stouputils/applications/upscaler/image.pyi +109 -0
  10. stouputils/applications/upscaler/video.pyi +60 -0
  11. stouputils/archive.pyi +67 -0
  12. stouputils/backup.pyi +109 -0
  13. stouputils/collections.pyi +86 -0
  14. stouputils/continuous_delivery/__init__.pyi +5 -0
  15. stouputils/continuous_delivery/cd_utils.pyi +129 -0
  16. stouputils/continuous_delivery/github.pyi +162 -0
  17. stouputils/continuous_delivery/pypi.pyi +52 -0
  18. stouputils/continuous_delivery/pyproject.pyi +67 -0
  19. stouputils/continuous_delivery/stubs.pyi +39 -0
  20. stouputils/ctx.pyi +211 -0
  21. stouputils/data_science/config/get.py +51 -51
  22. stouputils/data_science/data_processing/image/__init__.py +66 -66
  23. stouputils/data_science/data_processing/image/auto_contrast.py +79 -79
  24. stouputils/data_science/data_processing/image/axis_flip.py +58 -58
  25. stouputils/data_science/data_processing/image/bias_field_correction.py +74 -74
  26. stouputils/data_science/data_processing/image/binary_threshold.py +73 -73
  27. stouputils/data_science/data_processing/image/blur.py +59 -59
  28. stouputils/data_science/data_processing/image/brightness.py +54 -54
  29. stouputils/data_science/data_processing/image/canny.py +110 -110
  30. stouputils/data_science/data_processing/image/clahe.py +92 -92
  31. stouputils/data_science/data_processing/image/common.py +30 -30
  32. stouputils/data_science/data_processing/image/contrast.py +53 -53
  33. stouputils/data_science/data_processing/image/curvature_flow_filter.py +74 -74
  34. stouputils/data_science/data_processing/image/denoise.py +378 -378
  35. stouputils/data_science/data_processing/image/histogram_equalization.py +123 -123
  36. stouputils/data_science/data_processing/image/invert.py +64 -64
  37. stouputils/data_science/data_processing/image/laplacian.py +60 -60
  38. stouputils/data_science/data_processing/image/median_blur.py +52 -52
  39. stouputils/data_science/data_processing/image/noise.py +59 -59
  40. stouputils/data_science/data_processing/image/normalize.py +65 -65
  41. stouputils/data_science/data_processing/image/random_erase.py +66 -66
  42. stouputils/data_science/data_processing/image/resize.py +69 -69
  43. stouputils/data_science/data_processing/image/rotation.py +80 -80
  44. stouputils/data_science/data_processing/image/salt_pepper.py +68 -68
  45. stouputils/data_science/data_processing/image/sharpening.py +55 -55
  46. stouputils/data_science/data_processing/image/shearing.py +64 -64
  47. stouputils/data_science/data_processing/image/threshold.py +64 -64
  48. stouputils/data_science/data_processing/image/translation.py +71 -71
  49. stouputils/data_science/data_processing/image/zoom.py +83 -83
  50. stouputils/data_science/data_processing/image_augmentation.py +118 -118
  51. stouputils/data_science/data_processing/image_preprocess.py +183 -183
  52. stouputils/data_science/data_processing/prosthesis_detection.py +359 -359
  53. stouputils/data_science/data_processing/technique.py +481 -481
  54. stouputils/data_science/dataset/__init__.py +45 -45
  55. stouputils/data_science/dataset/dataset.py +292 -292
  56. stouputils/data_science/dataset/dataset_loader.py +135 -135
  57. stouputils/data_science/dataset/grouping_strategy.py +296 -296
  58. stouputils/data_science/dataset/image_loader.py +100 -100
  59. stouputils/data_science/dataset/xy_tuple.py +696 -696
  60. stouputils/data_science/metric_dictionnary.py +106 -106
  61. stouputils/data_science/mlflow_utils.py +206 -206
  62. stouputils/data_science/models/abstract_model.py +149 -149
  63. stouputils/data_science/models/all.py +85 -85
  64. stouputils/data_science/models/keras/all.py +38 -38
  65. stouputils/data_science/models/keras/convnext.py +62 -62
  66. stouputils/data_science/models/keras/densenet.py +50 -50
  67. stouputils/data_science/models/keras/efficientnet.py +60 -60
  68. stouputils/data_science/models/keras/mobilenet.py +56 -56
  69. stouputils/data_science/models/keras/resnet.py +52 -52
  70. stouputils/data_science/models/keras/squeezenet.py +233 -233
  71. stouputils/data_science/models/keras/vgg.py +42 -42
  72. stouputils/data_science/models/keras/xception.py +38 -38
  73. stouputils/data_science/models/keras_utils/callbacks/__init__.py +20 -20
  74. stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +219 -219
  75. stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +148 -148
  76. stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +31 -31
  77. stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +249 -249
  78. stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +66 -66
  79. stouputils/data_science/models/keras_utils/losses/__init__.py +12 -12
  80. stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +56 -56
  81. stouputils/data_science/models/keras_utils/visualizations.py +416 -416
  82. stouputils/data_science/models/sandbox.py +116 -116
  83. stouputils/data_science/range_tuple.py +234 -234
  84. stouputils/data_science/utils.py +285 -285
  85. stouputils/decorators.pyi +242 -0
  86. stouputils/image.pyi +172 -0
  87. stouputils/installer/__init__.py +18 -18
  88. stouputils/installer/__init__.pyi +5 -0
  89. stouputils/installer/common.pyi +39 -0
  90. stouputils/installer/downloader.pyi +24 -0
  91. stouputils/installer/linux.py +144 -144
  92. stouputils/installer/linux.pyi +39 -0
  93. stouputils/installer/main.py +223 -223
  94. stouputils/installer/main.pyi +57 -0
  95. stouputils/installer/windows.py +136 -136
  96. stouputils/installer/windows.pyi +31 -0
  97. stouputils/io.pyi +213 -0
  98. stouputils/parallel.py +12 -10
  99. stouputils/parallel.pyi +211 -0
  100. stouputils/print.pyi +136 -0
  101. stouputils/py.typed +1 -1
  102. stouputils/stouputils/parallel.pyi +4 -4
  103. stouputils/version_pkg.pyi +15 -0
  104. {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/METADATA +1 -1
  105. stouputils-1.14.2.dist-info/RECORD +171 -0
  106. stouputils-1.14.0.dist-info/RECORD +0 -140
  107. {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/WHEEL +0 -0
  108. {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/entry_points.txt +0 -0
@@ -1,136 +1,136 @@
1
- """ Installer module for Windows specific functions.
2
-
3
- Provides Windows specific implementations for checking administrator privileges,
4
- determining appropriate installation paths (global/local), and modifying
5
- the user's PATH environment variable.
6
- """
7
- # Imports
8
- import os
9
-
10
- from ..decorators import LogLevels, handle_error
11
- from ..io import clean_path
12
- from ..print import debug, info, warning
13
- from .common import ask_install_type, prompt_for_path
14
-
15
-
16
- # Functions
17
- @handle_error(message="Failed to add to PATH (Windows)", error_log=LogLevels.WARNING_TRACEBACK)
18
- def add_to_path_windows(install_path: str) -> bool | None:
19
- """ Add install_path to the User PATH environment variable on Windows.
20
-
21
- Args:
22
- install_path (str): The path to add to the User PATH environment variable.
23
-
24
- Returns:
25
- bool | None: True if the path was added to the User PATH environment variable, None otherwise.
26
- """
27
- # Convert install_path to a Windows path if it's not already
28
- install_path = install_path.replace("/", "\\")
29
- os.makedirs(install_path, exist_ok=True)
30
-
31
- # Get current user PATH
32
- import winreg
33
- with winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_READ | winreg.KEY_WRITE) as key:
34
-
35
- # Get the number of values in the registry key
36
- num_values = winreg.QueryInfoKey(key)[1]
37
-
38
- # Find the index of the 'Path' value
39
- path_index = -1
40
- for i in range(num_values):
41
- if winreg.EnumValue(key, i)[0] == 'Path':
42
- path_index = i
43
- break
44
-
45
- # Get the current path value
46
- current_path: str = winreg.EnumValue(key, path_index)[1]
47
-
48
- # Check if path is already present
49
- if install_path not in current_path.split(';'):
50
- new_path: str = f"{current_path};{install_path}"
51
- winreg.SetValueEx(key, "Path", 0, winreg.REG_EXPAND_SZ, new_path)
52
- debug(f"Added '{install_path}' to user PATH. Please restart your terminal for changes to take effect.")
53
- else:
54
- debug(f"'{install_path}' is already in user PATH.")
55
- return True
56
-
57
-
58
- def check_admin_windows() -> bool:
59
- """ Check if the script is running with administrator privileges on Windows. """
60
- try:
61
- import ctypes
62
- return ctypes.windll.shell32.IsUserAnAdmin() != 0
63
- except Exception:
64
- return False
65
-
66
-
67
- @handle_error(message="Failed to get installation path (Windows)", error_log=LogLevels.ERROR_TRACEBACK)
68
- def get_install_path_windows(
69
- program_name: str,
70
- ask_global: int = 0,
71
- add_path: bool = True,
72
- append_to_path: str = "",
73
- default_global: str = os.environ.get("ProgramFiles", "C:\\Program Files")
74
- ) -> str:
75
- """ Get the installation path for the program
76
-
77
- Args:
78
- program_name (str): The name of the program to install.
79
- ask_global (int): 0 = ask for anything, 1 = install globally, 2 = install locally
80
- add_path (bool): Whether to add the program to the PATH environment variable. (Only if installed globally)
81
- append_to_path (str): String to append to the installation path when adding to PATH.
82
- (ex: "bin" if executables are in the bin folder)
83
- default_global (str): The default global installation path.
84
- (Default is "C:\\Program Files" which is the most common location for executables on Windows)
85
-
86
- Returns:
87
- str: The installation path.
88
- """
89
- # Default path is located in the current working directory
90
- default_local_path: str = clean_path(os.path.join(os.getcwd(), program_name))
91
-
92
- # Define default global path (used in prompt even if not chosen initially)
93
- default_global_path: str = clean_path(os.path.join(default_global, program_name))
94
-
95
- # Ask user for installation type (global/local)
96
- install_type: str = ask_install_type(ask_global, default_local_path, default_global_path)
97
-
98
- # If the user wants to install globally,
99
- if install_type == 'g':
100
-
101
- # Check if the user has admin privileges,
102
- if not check_admin_windows():
103
-
104
- # If the user doesn't have admin privileges, fallback to local
105
- warning(
106
- f"Global installation requires administrator privileges. Please re-run as administrator.\n"
107
- f"Install locally instead to '{default_local_path}'? (Y/n): "
108
- )
109
- if input().lower() == 'n':
110
- info("Installation cancelled.")
111
- return ""
112
- else:
113
- # Fallback to local path if user agrees
114
- return prompt_for_path(
115
- f"Falling back to local installation path: {default_local_path}.",
116
- default_local_path
117
- )
118
-
119
- # If the user has admin privileges,
120
- else:
121
- # Ask it user wants to override the default global install path
122
- install_path: str = prompt_for_path(
123
- f"Default global installation path is {default_global_path}.",
124
- default_global_path
125
- )
126
- if add_path:
127
- add_to_path_windows(os.path.join(install_path, append_to_path))
128
- return install_path
129
-
130
- # Local install
131
- else: # install_type == 'l'
132
- return prompt_for_path(
133
- f"Default local installation path is {default_local_path}.",
134
- default_local_path
135
- )
136
-
1
+ """ Installer module for Windows specific functions.
2
+
3
+ Provides Windows specific implementations for checking administrator privileges,
4
+ determining appropriate installation paths (global/local), and modifying
5
+ the user's PATH environment variable.
6
+ """
7
+ # Imports
8
+ import os
9
+
10
+ from ..decorators import LogLevels, handle_error
11
+ from ..io import clean_path
12
+ from ..print import debug, info, warning
13
+ from .common import ask_install_type, prompt_for_path
14
+
15
+
16
+ # Functions
17
+ @handle_error(message="Failed to add to PATH (Windows)", error_log=LogLevels.WARNING_TRACEBACK)
18
+ def add_to_path_windows(install_path: str) -> bool | None:
19
+ """ Add install_path to the User PATH environment variable on Windows.
20
+
21
+ Args:
22
+ install_path (str): The path to add to the User PATH environment variable.
23
+
24
+ Returns:
25
+ bool | None: True if the path was added to the User PATH environment variable, None otherwise.
26
+ """
27
+ # Convert install_path to a Windows path if it's not already
28
+ install_path = install_path.replace("/", "\\")
29
+ os.makedirs(install_path, exist_ok=True)
30
+
31
+ # Get current user PATH
32
+ import winreg
33
+ with winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_READ | winreg.KEY_WRITE) as key:
34
+
35
+ # Get the number of values in the registry key
36
+ num_values = winreg.QueryInfoKey(key)[1]
37
+
38
+ # Find the index of the 'Path' value
39
+ path_index = -1
40
+ for i in range(num_values):
41
+ if winreg.EnumValue(key, i)[0] == 'Path':
42
+ path_index = i
43
+ break
44
+
45
+ # Get the current path value
46
+ current_path: str = winreg.EnumValue(key, path_index)[1]
47
+
48
+ # Check if path is already present
49
+ if install_path not in current_path.split(';'):
50
+ new_path: str = f"{current_path};{install_path}"
51
+ winreg.SetValueEx(key, "Path", 0, winreg.REG_EXPAND_SZ, new_path)
52
+ debug(f"Added '{install_path}' to user PATH. Please restart your terminal for changes to take effect.")
53
+ else:
54
+ debug(f"'{install_path}' is already in user PATH.")
55
+ return True
56
+
57
+
58
+ def check_admin_windows() -> bool:
59
+ """ Check if the script is running with administrator privileges on Windows. """
60
+ try:
61
+ import ctypes
62
+ return ctypes.windll.shell32.IsUserAnAdmin() != 0
63
+ except Exception:
64
+ return False
65
+
66
+
67
+ @handle_error(message="Failed to get installation path (Windows)", error_log=LogLevels.ERROR_TRACEBACK)
68
+ def get_install_path_windows(
69
+ program_name: str,
70
+ ask_global: int = 0,
71
+ add_path: bool = True,
72
+ append_to_path: str = "",
73
+ default_global: str = os.environ.get("ProgramFiles", "C:\\Program Files")
74
+ ) -> str:
75
+ """ Get the installation path for the program
76
+
77
+ Args:
78
+ program_name (str): The name of the program to install.
79
+ ask_global (int): 0 = ask for anything, 1 = install globally, 2 = install locally
80
+ add_path (bool): Whether to add the program to the PATH environment variable. (Only if installed globally)
81
+ append_to_path (str): String to append to the installation path when adding to PATH.
82
+ (ex: "bin" if executables are in the bin folder)
83
+ default_global (str): The default global installation path.
84
+ (Default is "C:\\Program Files" which is the most common location for executables on Windows)
85
+
86
+ Returns:
87
+ str: The installation path.
88
+ """
89
+ # Default path is located in the current working directory
90
+ default_local_path: str = clean_path(os.path.join(os.getcwd(), program_name))
91
+
92
+ # Define default global path (used in prompt even if not chosen initially)
93
+ default_global_path: str = clean_path(os.path.join(default_global, program_name))
94
+
95
+ # Ask user for installation type (global/local)
96
+ install_type: str = ask_install_type(ask_global, default_local_path, default_global_path)
97
+
98
+ # If the user wants to install globally,
99
+ if install_type == 'g':
100
+
101
+ # Check if the user has admin privileges,
102
+ if not check_admin_windows():
103
+
104
+ # If the user doesn't have admin privileges, fallback to local
105
+ warning(
106
+ f"Global installation requires administrator privileges. Please re-run as administrator.\n"
107
+ f"Install locally instead to '{default_local_path}'? (Y/n): "
108
+ )
109
+ if input().lower() == 'n':
110
+ info("Installation cancelled.")
111
+ return ""
112
+ else:
113
+ # Fallback to local path if user agrees
114
+ return prompt_for_path(
115
+ f"Falling back to local installation path: {default_local_path}.",
116
+ default_local_path
117
+ )
118
+
119
+ # If the user has admin privileges,
120
+ else:
121
+ # Ask it user wants to override the default global install path
122
+ install_path: str = prompt_for_path(
123
+ f"Default global installation path is {default_global_path}.",
124
+ default_global_path
125
+ )
126
+ if add_path:
127
+ add_to_path_windows(os.path.join(install_path, append_to_path))
128
+ return install_path
129
+
130
+ # Local install
131
+ else: # install_type == 'l'
132
+ return prompt_for_path(
133
+ f"Default local installation path is {default_local_path}.",
134
+ default_local_path
135
+ )
136
+
@@ -0,0 +1,31 @@
1
+ from ..decorators import LogLevels as LogLevels, handle_error as handle_error
2
+ from ..io import clean_path as clean_path
3
+ from ..print import debug as debug, info as info, warning as warning
4
+ from .common import ask_install_type as ask_install_type, prompt_for_path as prompt_for_path
5
+
6
+ def add_to_path_windows(install_path: str) -> bool | None:
7
+ """ Add install_path to the User PATH environment variable on Windows.
8
+
9
+ \tArgs:
10
+ \t\tinstall_path (str): The path to add to the User PATH environment variable.
11
+
12
+ \tReturns:
13
+ \t\tbool | None: True if the path was added to the User PATH environment variable, None otherwise.
14
+ \t"""
15
+ def check_admin_windows() -> bool:
16
+ """ Check if the script is running with administrator privileges on Windows. """
17
+ def get_install_path_windows(program_name: str, ask_global: int = 0, add_path: bool = True, append_to_path: str = '', default_global: str = ...) -> str:
18
+ ''' Get the installation path for the program
19
+
20
+ \tArgs:
21
+ \t\tprogram_name (str): The name of the program to install.
22
+ \t\task_global (int): 0 = ask for anything, 1 = install globally, 2 = install locally
23
+ \t\tadd_path (bool): Whether to add the program to the PATH environment variable. (Only if installed globally)
24
+ \t\tappend_to_path (str): String to append to the installation path when adding to PATH.
25
+ \t\t\t(ex: "bin" if executables are in the bin folder)
26
+ \t\tdefault_global (str): The default global installation path.
27
+ \t\t\t(Default is "C:\\Program Files" which is the most common location for executables on Windows)
28
+
29
+ \tReturns:
30
+ \t\tstr: The installation path.
31
+ \t'''
stouputils/io.pyi ADDED
@@ -0,0 +1,213 @@
1
+ from typing import Any, IO
2
+
3
+ def get_root_path(relative_path: str, go_up: int = 0) -> str:
4
+ """ Get the absolute path of the directory.
5
+ \tUsually used to get the root path of the project using the __file__ variable.
6
+
7
+ \tArgs:
8
+ \t\trelative_path (str): The path to get the absolute directory path from
9
+ \t\tgo_up (int): Number of parent directories to go up (default: 0)
10
+ \tReturns:
11
+ \t\tstr: The absolute path of the directory
12
+
13
+ \tExamples:
14
+
15
+ \t\t.. code-block:: python
16
+
17
+ \t\t\t> get_root_path(__file__)
18
+ \t\t\t'C:/Users/Alexandre-PC/AppData/Local/Programs/Python/Python310/lib/site-packages/stouputils'
19
+
20
+ \t\t\t> get_root_path(__file__, 3)
21
+ \t\t\t'C:/Users/Alexandre-PC/AppData/Local/Programs/Python/Python310'
22
+ \t"""
23
+ def relative_path(file_path: str, relative_to: str = '') -> str:
24
+ ''' Get the relative path of a file relative to a given directory.
25
+
26
+ \tArgs:
27
+ \t\tfile_path (str): The path to get the relative path from
28
+ \t\trelative_to (str): The path to get the relative path to (default: current working directory -> os.getcwd())
29
+ \tReturns:
30
+ \t\tstr: The relative path of the file
31
+ \tExamples:
32
+
33
+ \t\t>>> relative_path("D:/some/random/path/stouputils/io.py", "D:\\\\some")
34
+ \t\t\'random/path/stouputils/io.py\'
35
+ \t\t>>> relative_path("D:/some/random/path/stouputils/io.py", "D:\\\\some\\\\")
36
+ \t\t\'random/path/stouputils/io.py\'
37
+ \t'''
38
+ def json_dump(data: Any, file: IO[Any] | str | None = None, max_level: int | None = 2, indent: str | int = '\t', suffix: str = '\n', ensure_ascii: bool = False) -> str:
39
+ ''' Writes the provided data to a JSON file with a specified indentation depth.
40
+ \tFor instance, setting max_level to 2 will limit the indentation to 2 levels.
41
+
42
+ \tArgs:
43
+ \t\tdata\t\t(Any): \t\t\t\tThe data to dump (usually a dict or a list)
44
+ \t\tfile\t\t(IO[Any] | str): \tThe file object or path to dump the data to
45
+ \t\tmax_level\t(int | None):\t\tThe depth of indentation to stop at (-1 for infinite), None will default to 2
46
+ \t\tindent\t\t(str | int):\t\tThe indentation character (default: \'\\t\')
47
+ \t\tsuffix\t\t(str):\t\t\t\tThe suffix to add at the end of the string (default: \'\\n\')
48
+ \t\tensure_ascii (bool):\t\t\tWhether to escape non-ASCII characters (default: False)
49
+ \tReturns:
50
+ \t\tstr: The content of the file in every case
51
+
52
+ \t>>> json_dump({"a": [[1,2,3]], "b": 2}, max_level = 0)
53
+ \t\'{"a": [[1,2,3]],"b": 2}\\n\'
54
+ \t>>> json_dump({"a": [[1,2,3]], "b": 2}, max_level = 1)
55
+ \t\'{\\n\\t"a": [[1,2,3]],\\n\\t"b": 2\\n}\\n\'
56
+ \t>>> json_dump({"a": [[1,2,3]], "b": 2}, max_level = 2)
57
+ \t\'{\\n\\t"a": [\\n\\t\\t[1,2,3]\\n\\t],\\n\\t"b": 2\\n}\\n\'
58
+ \t>>> json_dump({"a": [[1,2,3]], "b": 2}, max_level = 3)
59
+ \t\'{\\n\\t"a": [\\n\\t\\t[\\n\\t\\t\\t1,\\n\\t\\t\\t2,\\n\\t\\t\\t3\\n\\t\\t]\\n\\t],\\n\\t"b": 2\\n}\\n\'
60
+ \t>>> json_dump({"éà": "üñ"}, ensure_ascii = True, max_level = 0)
61
+ \t\'{"\\\\u00e9\\\\u00e0": "\\\\u00fc\\\\u00f1"}\\n\'
62
+ \t>>> json_dump({"éà": "üñ"}, ensure_ascii = False, max_level = 0)
63
+ \t\'{"éà": "üñ"}\\n\'
64
+ \t'''
65
+ def json_load(file_path: str) -> Any:
66
+ """ Load a JSON file from the given path
67
+
68
+ \tArgs:
69
+ \t\tfile_path (str): The path to the JSON file
70
+ \tReturns:
71
+ \t\tAny: The content of the JSON file
72
+ \t"""
73
+ def csv_dump(data: Any, file: IO[Any] | str | None = None, delimiter: str = ',', has_header: bool = True, index: bool = False, *args: Any, **kwargs: Any) -> str:
74
+ ''' Writes data to a CSV file with customizable options and returns the CSV content as a string.
75
+
76
+ \tArgs:
77
+ \t\tdata\t\t(list[list[Any]] | list[dict[str, Any]] | pd.DataFrame | pl.DataFrame):
78
+ \t\t\t\t\t\tThe data to write, either a list of lists, list of dicts, pandas DataFrame, or Polars DataFrame
79
+ \t\tfile\t\t(IO[Any] | str): The file object or path to dump the data to
80
+ \t\tdelimiter\t(str): The delimiter to use (default: \',\')
81
+ \t\thas_header\t(bool): Whether to include headers (default: True, applies to dict and DataFrame data)
82
+ \t\tindex\t\t(bool): Whether to include the index (default: False, only applies to pandas DataFrame)
83
+ \t\t*args\t\t(Any): Additional positional arguments to pass to the underlying CSV writer or DataFrame method
84
+ \t\t**kwargs\t(Any): Additional keyword arguments to pass to the underlying CSV writer or DataFrame method
85
+ \tReturns:
86
+ \t\tstr: The CSV content as a string
87
+
88
+ \tExamples:
89
+
90
+ \t\t>>> csv_dump([["a", "b", "c"], [1, 2, 3], [4, 5, 6]])
91
+ \t\t\'a,b,c\\r\\n1,2,3\\r\\n4,5,6\\r\\n\'
92
+
93
+ \t\t>>> csv_dump([{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}])
94
+ \t\t\'name,age\\r\\nAlice,30\\r\\nBob,25\\r\\n\'
95
+ \t'''
96
+ def csv_load(file_path: str, delimiter: str = ',', has_header: bool = True, as_dict: bool = False, as_dataframe: bool = False, use_polars: bool = False, *args: Any, **kwargs: Any) -> Any:
97
+ ''' Load a CSV file from the given path
98
+
99
+ \tArgs:
100
+ \t\tfile_path (str): The path to the CSV file
101
+ \t\tdelimiter (str): The delimiter used in the CSV (default: \',\')
102
+ \t\thas_header (bool): Whether the CSV has a header row (default: True)
103
+ \t\tas_dict (bool): Whether to return data as list of dicts (default: False)
104
+ \t\tas_dataframe (bool): Whether to return data as a DataFrame (default: False)
105
+ \t\tuse_polars (bool): Whether to use Polars instead of pandas for DataFrame (default: False, requires polars)
106
+ \t\t*args: Additional positional arguments to pass to the underlying CSV reader or DataFrame method
107
+ \t\t**kwargs: Additional keyword arguments to pass to the underlying CSV reader or DataFrame method
108
+ \tReturns:
109
+ \t\tlist[list[str]] | list[dict[str, str]] | pd.DataFrame | pl.DataFrame: The content of the CSV file
110
+
111
+ \tExamples:
112
+
113
+ \t\t.. code-block:: python
114
+
115
+ \t\t\t> Assuming "test.csv" contains: a,b,c\\n1,2,3\\n4,5,6
116
+ \t\t\t> csv_load("test.csv")
117
+ \t\t\t[[\'1\', \'2\', \'3\'], [\'4\', \'5\', \'6\']]
118
+
119
+ \t\t\t> csv_load("test.csv", as_dict=True)
120
+ \t\t\t[{\'a\': \'1\', \'b\': \'2\', \'c\': \'3\'}, {\'a\': \'4\', \'b\': \'5\', \'c\': \'6\'}]
121
+
122
+ \t\t\t> csv_load("test.csv", as_dataframe=True)
123
+ \t\t\t a b c
124
+ \t\t\t0 1 2 3
125
+ \t\t\t1 4 5 6
126
+
127
+ \t\t.. code-block:: console
128
+
129
+ \t\t\t> csv_load("test.csv", as_dataframe=True, use_polars=True)
130
+ \t\t\tshape: (2, 3)
131
+ \t\t\t┌─────┬─────┬─────┐
132
+ \t\t\t│ a ┆ b ┆ c │
133
+ \t\t\t│ --- ┆ --- ┆ --- │
134
+ \t\t\t│ i64 ┆ i64 ┆ i64 │
135
+ \t\t\t╞═════╪═════╪═════╡
136
+ \t\t\t│ 1 ┆ 2 ┆ 3 │
137
+ \t\t\t│ 4 ┆ 5 ┆ 6 │
138
+ \t\t\t└─────┴─────┴─────┘
139
+ \t'''
140
+ def super_copy(src: str, dst: str, create_dir: bool = True, symlink: bool = False) -> str:
141
+ """ Copy a file (or a folder) from the source to the destination
142
+
143
+ \tArgs:
144
+ \t\tsrc (str): The source path
145
+ \t\tdst (str): The destination path
146
+ \t\tcreate_dir (bool): Whether to create the directory if it doesn't exist (default: True)
147
+ \t\tsymlink (bool): Whether to create a symlink instead of copying (Linux only, default: True)
148
+ \tReturns:
149
+ \t\tstr: The destination path
150
+ \t"""
151
+ def super_open(file_path: str, mode: str, encoding: str = 'utf-8') -> IO[Any]:
152
+ ''' Open a file with the given mode, creating the directory if it doesn\'t exist (only if writing)
153
+
154
+ \tArgs:
155
+ \t\tfile_path\t(str): The path to the file
156
+ \t\tmode\t\t(str): The mode to open the file with, ex: "w", "r", "a", "wb", "rb", "ab"
157
+ \t\tencoding\t(str): The encoding to use when opening the file (default: "utf-8")
158
+ \tReturns:
159
+ \t\topen: The file object, ready to be used
160
+ \t'''
161
+ def read_file(file_path: str, encoding: str = 'utf-8') -> str:
162
+ ''' Read the content of a file and return it as a string
163
+
164
+ \tArgs:
165
+ \t\tfile_path (str): The path to the file
166
+ \t\tencoding (str): The encoding to use when opening the file (default: "utf-8")
167
+ \tReturns:
168
+ \t\tstr: The content of the file
169
+ \t'''
170
+ def replace_tilde(path: str) -> str:
171
+ ''' Replace the "~" by the user\'s home directory
172
+
173
+ \tArgs:
174
+ \t\tpath (str): The path to replace the "~" by the user\'s home directory
175
+ \tReturns:
176
+ \t\tstr: The path with the "~" replaced by the user\'s home directory
177
+ \tExamples:
178
+
179
+ \t\t.. code-block:: python
180
+
181
+ \t\t\t> replace_tilde("~/Documents/test.txt")
182
+ \t\t\t\'/home/user/Documents/test.txt\'
183
+ \t'''
184
+ def clean_path(file_path: str, trailing_slash: bool = True) -> str:
185
+ ''' Clean the path by replacing backslashes with forward slashes and simplifying the path
186
+
187
+ \tArgs:
188
+ \t\tfile_path (str): The path to clean
189
+ \t\ttrailing_slash (bool): Whether to keep the trailing slash, ex: "test/" -> "test/"
190
+ \tReturns:
191
+ \t\tstr: The cleaned path
192
+ \tExamples:
193
+ \t\t>>> clean_path("C:\\\\Users\\\\Stoupy\\\\Documents\\\\test.txt")
194
+ \t\t\'C:/Users/Stoupy/Documents/test.txt\'
195
+
196
+ \t\t>>> clean_path("Some Folder////")
197
+ \t\t\'Some Folder/\'
198
+
199
+ \t\t>>> clean_path("test/uwu/1/../../")
200
+ \t\t\'test/\'
201
+
202
+ \t\t>>> clean_path("some/./folder/../")
203
+ \t\t\'some/\'
204
+
205
+ \t\t>>> clean_path("folder1/folder2/../../folder3")
206
+ \t\t\'folder3\'
207
+
208
+ \t\t>>> clean_path("./test/./folder/")
209
+ \t\t\'test/folder/\'
210
+
211
+ \t\t>>> clean_path("C:/folder1\\\\folder2")
212
+ \t\t\'C:/folder1/folder2\'
213
+ \t'''
stouputils/parallel.py CHANGED
@@ -335,8 +335,8 @@ def run_in_subprocess[R](
335
335
  import multiprocessing as mp
336
336
  from multiprocessing import Queue
337
337
 
338
- # Create a queue to get the result from the subprocess
339
- result_queue: Queue[R | Exception] = Queue()
338
+ # Create a queue to get the result from the subprocess (only if we need to wait)
339
+ result_queue: Queue[R | Exception] | None = None if no_join else Queue()
340
340
 
341
341
  # Create and start the subprocess using the module-level wrapper
342
342
  process: mp.Process = mp.Process(
@@ -345,8 +345,8 @@ def run_in_subprocess[R](
345
345
  )
346
346
  process.start()
347
347
 
348
- # Join with timeout to prevent indefinite hanging
349
- if no_join:
348
+ # Detach process if no_join (fire-and-forget)
349
+ if result_queue is None:
350
350
  return None # type: ignore
351
351
  process.join(timeout=timeout)
352
352
 
@@ -394,16 +394,18 @@ def _subprocess_wrapper[R](
394
394
  Must be at module level to be pickable on Windows (spawn context).
395
395
 
396
396
  Args:
397
- result_queue (multiprocessing.Queue): Queue to store the result or exception.
398
- func (Callable): The target function to execute.
399
- args (tuple): Positional arguments for the function.
400
- kwargs (dict): Keyword arguments for the function.
397
+ result_queue (multiprocessing.Queue | None): Queue to store the result or exception (None if detached).
398
+ func (Callable): The target function to execute.
399
+ args (tuple): Positional arguments for the function.
400
+ kwargs (dict): Keyword arguments for the function.
401
401
  """
402
402
  try:
403
403
  result: R = func(*args, **kwargs)
404
- result_queue.put(result)
404
+ if result_queue is not None:
405
+ result_queue.put(result)
405
406
  except Exception as e:
406
- result_queue.put(e)
407
+ if result_queue is not None:
408
+ result_queue.put(e)
407
409
 
408
410
  # "Private" function to use starmap
409
411
  def _starmap[T, R](args: tuple[Callable[[T], R], list[T]]) -> R: