stouputils 1.12.2__py3-none-any.whl → 1.13.0__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.
- stouputils/__main__.py +11 -6
- stouputils/continuous_delivery/pypi.py +39 -1
- stouputils/continuous_delivery/pypi.pyi +9 -0
- stouputils/ctx.py +408 -408
- stouputils/data_science/config/set.py +125 -125
- stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +31 -31
- stouputils/data_science/utils.py +285 -285
- stouputils/installer/__init__.py +18 -18
- stouputils/installer/linux.py +144 -144
- stouputils/installer/main.py +223 -223
- stouputils/installer/windows.py +136 -136
- stouputils/py.typed +1 -1
- stouputils/stouputils/__init__.pyi +15 -0
- stouputils/stouputils/_deprecated.pyi +12 -0
- stouputils/stouputils/all_doctests.pyi +46 -0
- stouputils/stouputils/applications/__init__.pyi +2 -0
- stouputils/stouputils/applications/automatic_docs.pyi +106 -0
- stouputils/stouputils/applications/upscaler/__init__.pyi +3 -0
- stouputils/stouputils/applications/upscaler/config.pyi +18 -0
- stouputils/stouputils/applications/upscaler/image.pyi +109 -0
- stouputils/stouputils/applications/upscaler/video.pyi +60 -0
- stouputils/stouputils/archive.pyi +67 -0
- stouputils/stouputils/backup.pyi +109 -0
- stouputils/stouputils/collections.pyi +86 -0
- stouputils/stouputils/continuous_delivery/__init__.pyi +5 -0
- stouputils/stouputils/continuous_delivery/cd_utils.pyi +129 -0
- stouputils/stouputils/continuous_delivery/github.pyi +162 -0
- stouputils/stouputils/continuous_delivery/pypi.pyi +53 -0
- stouputils/stouputils/continuous_delivery/pyproject.pyi +67 -0
- stouputils/stouputils/continuous_delivery/stubs.pyi +39 -0
- stouputils/stouputils/ctx.pyi +211 -0
- stouputils/stouputils/decorators.pyi +242 -0
- stouputils/stouputils/image.pyi +172 -0
- stouputils/stouputils/installer/__init__.pyi +5 -0
- stouputils/stouputils/installer/common.pyi +39 -0
- stouputils/stouputils/installer/downloader.pyi +24 -0
- stouputils/stouputils/installer/linux.pyi +39 -0
- stouputils/stouputils/installer/main.pyi +57 -0
- stouputils/stouputils/installer/windows.pyi +31 -0
- stouputils/stouputils/io.pyi +213 -0
- stouputils/stouputils/parallel.pyi +211 -0
- stouputils/stouputils/print.pyi +136 -0
- stouputils/stouputils/version_pkg.pyi +15 -0
- {stouputils-1.12.2.dist-info → stouputils-1.13.0.dist-info}/METADATA +1 -1
- {stouputils-1.12.2.dist-info → stouputils-1.13.0.dist-info}/RECORD +47 -16
- {stouputils-1.12.2.dist-info → stouputils-1.13.0.dist-info}/WHEEL +0 -0
- {stouputils-1.12.2.dist-info → stouputils-1.13.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from .io import super_open as super_open
|
|
3
|
+
from .print import debug as debug, info as info
|
|
4
|
+
from PIL import Image
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from numpy.typing import NDArray
|
|
7
|
+
from typing import Any, TypeVar
|
|
8
|
+
|
|
9
|
+
PIL_Image_or_NDArray = TypeVar('PIL_Image_or_NDArray', bound='Image.Image | NDArray[np.number]')
|
|
10
|
+
|
|
11
|
+
def image_resize[PIL_Image_or_NDArray](image: PIL_Image_or_NDArray, max_result_size: int, resampling: Image.Resampling | None = None, min_or_max: Callable[[int, int], int] = ..., return_type: type[PIL_Image_or_NDArray] | str = 'same', keep_aspect_ratio: bool = True) -> Any:
|
|
12
|
+
''' Resize an image while preserving its aspect ratio by default.
|
|
13
|
+
\tScales the image so that its largest dimension equals max_result_size.
|
|
14
|
+
|
|
15
|
+
\tArgs:
|
|
16
|
+
\t\timage (Image.Image | np.ndarray): The image to resize.
|
|
17
|
+
\t\tmax_result_size (int): Maximum size for the largest dimension.
|
|
18
|
+
\t\tresampling (Image.Resampling | None): PIL resampling filter to use (default: Image.Resampling.LANCZOS).
|
|
19
|
+
\t\tmin_or_max (Callable): Function to use to get the minimum or maximum of the two ratios.
|
|
20
|
+
\t\treturn_type (type | str): Type of the return value (Image.Image, np.ndarray, or "same" to match input type).
|
|
21
|
+
\t\tkeep_aspect_ratio (bool): Whether to keep the aspect ratio.
|
|
22
|
+
\tReturns:
|
|
23
|
+
\t\tImage.Image | NDArray[np.number]: The resized image with preserved aspect ratio.
|
|
24
|
+
\tExamples:
|
|
25
|
+
\t\t>>> # Test with (height x width x channels) numpy array
|
|
26
|
+
\t\t>>> import numpy as np
|
|
27
|
+
\t\t>>> array = np.random.randint(0, 255, (100, 50, 3), dtype=np.uint8)
|
|
28
|
+
\t\t>>> image_resize(array, 100).shape
|
|
29
|
+
\t\t(100, 50, 3)
|
|
30
|
+
\t\t>>> image_resize(array, 100, min_or_max=max).shape
|
|
31
|
+
\t\t(100, 50, 3)
|
|
32
|
+
\t\t>>> image_resize(array, 100, min_or_max=min).shape
|
|
33
|
+
\t\t(200, 100, 3)
|
|
34
|
+
|
|
35
|
+
\t\t>>> # Test with PIL Image
|
|
36
|
+
\t\t>>> from PIL import Image
|
|
37
|
+
\t\t>>> pil_image: Image.Image = Image.new(\'RGB\', (200, 100))
|
|
38
|
+
\t\t>>> image_resize(pil_image, 50).size
|
|
39
|
+
\t\t(50, 25)
|
|
40
|
+
\t\t>>> # Test with different return types
|
|
41
|
+
\t\t>>> resized_array = image_resize(array, 50, return_type=np.ndarray)
|
|
42
|
+
\t\t>>> isinstance(resized_array, np.ndarray)
|
|
43
|
+
\t\tTrue
|
|
44
|
+
\t\t>>> resized_array.shape
|
|
45
|
+
\t\t(50, 25, 3)
|
|
46
|
+
\t\t>>> # Test with different resampling methods
|
|
47
|
+
\t\t>>> image_resize(pil_image, 50, resampling=Image.Resampling.NEAREST).size
|
|
48
|
+
\t\t(50, 25)
|
|
49
|
+
\t'''
|
|
50
|
+
def auto_crop[PIL_Image_or_NDArray](image: PIL_Image_or_NDArray, mask: NDArray[np.bool_] | None = None, threshold: int | float | Callable[[NDArray[np.number]], int | float] | None = None, return_type: type[PIL_Image_or_NDArray] | str = 'same', contiguous: bool = True) -> Any:
|
|
51
|
+
''' Automatically crop an image to remove zero or uniform regions.
|
|
52
|
+
|
|
53
|
+
\tThis function crops the image to keep only the region where pixels are non-zero
|
|
54
|
+
\t(or above a threshold). It can work with a mask or directly analyze the image.
|
|
55
|
+
|
|
56
|
+
\tArgs:
|
|
57
|
+
\t\timage (Image.Image | NDArray):\t The image to crop.
|
|
58
|
+
\t\tmask (NDArray[bool] | None): Optional binary mask indicating regions to keep.
|
|
59
|
+
\t\tthreshold (int | float | Callable): Threshold value or function (default: np.min).
|
|
60
|
+
\t\treturn_type (type | str): Type of the return value (Image.Image, NDArray[np.number], or "same" to match input type).
|
|
61
|
+
\t\tcontiguous (bool): If True (default), crop to bounding box. If False, remove entire rows/columns with no content.
|
|
62
|
+
\tReturns:
|
|
63
|
+
\t\tImage.Image | NDArray[np.number]: The cropped image.
|
|
64
|
+
|
|
65
|
+
\tExamples:
|
|
66
|
+
\t\t>>> # Test with numpy array with zeros on edges
|
|
67
|
+
\t\t>>> import numpy as np
|
|
68
|
+
\t\t>>> array = np.zeros((100, 100, 3), dtype=np.uint8)
|
|
69
|
+
\t\t>>> array[20:80, 30:70] = 255 # White rectangle in center
|
|
70
|
+
\t\t>>> cropped = auto_crop(array, return_type=np.ndarray)
|
|
71
|
+
\t\t>>> cropped.shape
|
|
72
|
+
\t\t(60, 40, 3)
|
|
73
|
+
|
|
74
|
+
\t\t>>> # Test with custom mask
|
|
75
|
+
\t\t>>> mask = np.zeros((100, 100), dtype=bool)
|
|
76
|
+
\t\t>>> mask[10:90, 10:90] = True
|
|
77
|
+
\t\t>>> cropped_with_mask = auto_crop(array, mask=mask, return_type=np.ndarray)
|
|
78
|
+
\t\t>>> cropped_with_mask.shape
|
|
79
|
+
\t\t(80, 80, 3)
|
|
80
|
+
|
|
81
|
+
\t\t>>> # Test with PIL Image
|
|
82
|
+
\t\t>>> from PIL import Image
|
|
83
|
+
\t\t>>> pil_image = Image.new(\'RGB\', (100, 100), (0, 0, 0))
|
|
84
|
+
\t\t>>> from PIL import ImageDraw
|
|
85
|
+
\t\t>>> draw = ImageDraw.Draw(pil_image)
|
|
86
|
+
\t\t>>> draw.rectangle([25, 25, 75, 75], fill=(255, 255, 255))
|
|
87
|
+
\t\t>>> cropped_pil = auto_crop(pil_image)
|
|
88
|
+
\t\t>>> cropped_pil.size
|
|
89
|
+
\t\t(51, 51)
|
|
90
|
+
|
|
91
|
+
\t\t>>> # Test with threshold
|
|
92
|
+
\t\t>>> array_gray = np.ones((100, 100), dtype=np.uint8) * 10
|
|
93
|
+
\t\t>>> array_gray[20:80, 30:70] = 255
|
|
94
|
+
\t\t>>> cropped_threshold = auto_crop(array_gray, threshold=50, return_type=np.ndarray)
|
|
95
|
+
\t\t>>> cropped_threshold.shape
|
|
96
|
+
\t\t(60, 40)
|
|
97
|
+
|
|
98
|
+
\t\t>>> # Test with callable threshold (using lambda to avoid min value)
|
|
99
|
+
\t\t>>> array_gray2 = np.ones((100, 100), dtype=np.uint8) * 10
|
|
100
|
+
\t\t>>> array_gray2[20:80, 30:70] = 255
|
|
101
|
+
\t\t>>> cropped_max = auto_crop(array_gray2, threshold=lambda x: 50, return_type=np.ndarray)
|
|
102
|
+
\t\t>>> cropped_max.shape
|
|
103
|
+
\t\t(60, 40)
|
|
104
|
+
|
|
105
|
+
\t>>> # Test with non-contiguous crop
|
|
106
|
+
\t>>> array_sparse = np.zeros((100, 100, 3), dtype=np.uint8)
|
|
107
|
+
\t>>> array_sparse[10, 10] = 255
|
|
108
|
+
\t>>> array_sparse[50, 50] = 255
|
|
109
|
+
\t>>> array_sparse[90, 90] = 255
|
|
110
|
+
\t>>> cropped_contiguous = auto_crop(array_sparse, contiguous=True, return_type=np.ndarray)
|
|
111
|
+
\t>>> cropped_contiguous.shape # Bounding box from (10,10) to (90,90)
|
|
112
|
+
\t(81, 81, 3)
|
|
113
|
+
\t>>> cropped_non_contiguous = auto_crop(array_sparse, contiguous=False, return_type=np.ndarray)
|
|
114
|
+
\t>>> cropped_non_contiguous.shape # Only rows/cols 10, 50, 90
|
|
115
|
+
\t(3, 3, 3)
|
|
116
|
+
|
|
117
|
+
\t>>> # Test with 3D crop on depth dimension
|
|
118
|
+
\t>>> array_3d = np.zeros((50, 50, 10), dtype=np.uint8)
|
|
119
|
+
\t>>> array_3d[10:40, 10:40, 2:8] = 255 # Content only in depth slices 2-7
|
|
120
|
+
\t>>> cropped_3d = auto_crop(array_3d, contiguous=True, return_type=np.ndarray)
|
|
121
|
+
\t>>> cropped_3d.shape # Should crop all 3 dimensions
|
|
122
|
+
\t(30, 30, 6)
|
|
123
|
+
\t'''
|
|
124
|
+
def numpy_to_gif(path: str, array: NDArray[np.integer | np.floating | np.bool_], duration: int = 100, loop: int = 0, mkdir: bool = True, **kwargs: Any) -> None:
|
|
125
|
+
''' Generate a \'.gif\' file from a numpy array for 3D/4D visualization.
|
|
126
|
+
|
|
127
|
+
\tArgs:
|
|
128
|
+
\t\tpath (str): Path to the output .gif file.
|
|
129
|
+
\t\tarray (NDArray): Numpy array to be dumped (must be 3D or 4D).
|
|
130
|
+
\t\t\t3D: (depth, height, width) - e.g. (64, 1024, 1024)
|
|
131
|
+
\t\t\t4D: (depth, height, width, channels) - e.g. (50, 64, 1024, 3)
|
|
132
|
+
\t\tduration (int): Duration between frames in milliseconds.
|
|
133
|
+
\t\tloop (int): Number of loops (0 = infinite).
|
|
134
|
+
\t\tmkdir (bool): Create the directory if it does not exist.
|
|
135
|
+
\t\t**kwargs (Any): Additional keyword arguments for PIL.Image.save().
|
|
136
|
+
|
|
137
|
+
\tExamples:
|
|
138
|
+
|
|
139
|
+
\t\t.. code-block:: python
|
|
140
|
+
|
|
141
|
+
\t\t\t> # 3D array example
|
|
142
|
+
\t\t\t> array = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)
|
|
143
|
+
\t\t\t> numpy_to_gif("output_10_frames_100x100.gif", array, duration=200, loop=0)
|
|
144
|
+
|
|
145
|
+
\t\t\t> # 4D array example (batch of 3D images)
|
|
146
|
+
\t\t\t> array_4d = np.random.randint(0, 256, (5, 10, 100, 3), dtype=np.uint8)
|
|
147
|
+
\t\t\t> numpy_to_gif("output_50_frames_100x100.gif", array_4d, duration=200)
|
|
148
|
+
|
|
149
|
+
\t\t\t> total_duration = 1000 # 1 second
|
|
150
|
+
\t\t\t> numpy_to_gif("output_1s.gif", array, duration=total_duration // len(array))
|
|
151
|
+
\t'''
|
|
152
|
+
def numpy_to_obj(path: str, array: NDArray[np.integer | np.floating | np.bool_], threshold: float = 0.5, step_size: int = 1, pad_array: bool = True, verbose: int = 0) -> None:
|
|
153
|
+
''' Generate a \'.obj\' file from a numpy array for 3D visualization using marching cubes.
|
|
154
|
+
|
|
155
|
+
\tArgs:
|
|
156
|
+
\t\tpath (str): Path to the output .obj file.
|
|
157
|
+
\t\tarray (NDArray): Numpy array to be dumped (must be 3D).
|
|
158
|
+
\t\tthreshold (float): Threshold level for marching cubes (0.5 for binary data).
|
|
159
|
+
\t\tstep_size (int): Step size for marching cubes (higher = simpler mesh, faster generation).
|
|
160
|
+
\t\tpad_array (bool): If True, pad array with zeros to ensure closed volumes for border cells.
|
|
161
|
+
\t\tverbose (int): Verbosity level (0 = no output, 1 = some output, 2 = full output).
|
|
162
|
+
|
|
163
|
+
\tExamples:
|
|
164
|
+
|
|
165
|
+
\t\t.. code-block:: python
|
|
166
|
+
|
|
167
|
+
\t\t\t> array = np.random.rand(64, 64, 64) > 0.5 # Binary volume
|
|
168
|
+
\t\t\t> numpy_to_obj("output_mesh.obj", array, threshold=0.5, step_size=2, pad_array=True, verbose=1)
|
|
169
|
+
|
|
170
|
+
\t\t\t> array = my_3d_data # Some 3D numpy array (e.g. human lung scan)
|
|
171
|
+
\t\t\t> numpy_to_obj("output_mesh.obj", array, threshold=0.3)
|
|
172
|
+
\t'''
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ..print import warning as warning
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
def prompt_for_path(prompt_message: str, default_path: str) -> str:
|
|
5
|
+
""" Prompt the user to override a default path.
|
|
6
|
+
|
|
7
|
+
\tArgs:
|
|
8
|
+
\t\tprompt_message (str): The message to display to the user.
|
|
9
|
+
\t\tdefault_path (str): The default path to suggest.
|
|
10
|
+
|
|
11
|
+
\tReturns:
|
|
12
|
+
\t\tstr: The path entered by the user, or the default path if they pressed Enter.
|
|
13
|
+
\t"""
|
|
14
|
+
def ask_install_type(ask_global: int, default_local_path: str, default_global_path: str | None) -> Literal['g', 'l']:
|
|
15
|
+
''' Determine the installation type (global \'g\' or local \'l\') based on user input.
|
|
16
|
+
|
|
17
|
+
\tArgs:
|
|
18
|
+
\t\task_global (int): 0 = ask, 1 = force global, 2 = force local.
|
|
19
|
+
\t\tdefault_local_path (str): The default local path.
|
|
20
|
+
\t\tdefault_global_path (str | None): The default global path (if applicable).
|
|
21
|
+
|
|
22
|
+
\tReturns:
|
|
23
|
+
\t\tLiteral["g", "l"]: \'g\' for global install, \'l\' for local install.
|
|
24
|
+
|
|
25
|
+
\tExamples:
|
|
26
|
+
\t\t.. code-block:: python
|
|
27
|
+
|
|
28
|
+
\t\t\t> # Ask the user while providing default paths
|
|
29
|
+
\t\t\t> install_choice: str = ask_install_type(0, f"{os.getcwd()}/MyProgram", "C:\\Program Files\\MyProgram")
|
|
30
|
+
\t\t\tg
|
|
31
|
+
|
|
32
|
+
\t\t\t> # Don\'t ask, force global
|
|
33
|
+
\t\t\t> install_choice: str = ask_install_type(1, ...)
|
|
34
|
+
\t\t\tg
|
|
35
|
+
|
|
36
|
+
\t\t\t> # Don\'t ask, force local
|
|
37
|
+
\t\t\t> install_choice: str = ask_install_type(2, ...)
|
|
38
|
+
\t\t\tl
|
|
39
|
+
\t'''
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from ..print import info as info, warning as warning
|
|
2
|
+
from .main import install_program as install_program
|
|
3
|
+
|
|
4
|
+
def download_executable(download_urls: dict[str, str], program_name: str, append_to_path: str = '') -> bool:
|
|
5
|
+
""" Ask the user if they want to download the program (ex: waifu2x-ncnn-vulkan).
|
|
6
|
+
\tIf yes, try to download the program from the GitHub releases page.
|
|
7
|
+
|
|
8
|
+
\tArgs:
|
|
9
|
+
\t\tdownload_urls (dict[str, str]): The URLs to download the program from.
|
|
10
|
+
\t\tprogram_name (str): The name of the program to download.
|
|
11
|
+
|
|
12
|
+
\tReturns:
|
|
13
|
+
\t\tbool: True if the program is now ready to use, False otherwise.
|
|
14
|
+
\t"""
|
|
15
|
+
def check_executable(executable: str, executable_help_text: str, download_urls: dict[str, str], append_to_path: str = '') -> None:
|
|
16
|
+
''' Check if the executable exists, optionally download it if it doesn\'t.
|
|
17
|
+
|
|
18
|
+
\tArgs:
|
|
19
|
+
\t\texecutable (str): The path to the executable.
|
|
20
|
+
\t\texecutable_help_text (str): The help text to check for in the executable\'s output.
|
|
21
|
+
\t\tdownload_urls (dict[str, str]): The URLs to download the executable from.
|
|
22
|
+
\t\tappend_to_path (str): The path to append to the executable\'s path.
|
|
23
|
+
\t\t\t(ex: "bin" if executables are in the bin folder)
|
|
24
|
+
\t'''
|
|
@@ -0,0 +1,39 @@
|
|
|
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_linux(install_path: str) -> bool:
|
|
7
|
+
""" Suggest how to add install_path to PATH on Linux.
|
|
8
|
+
|
|
9
|
+
\tChecks the current shell and provides instructions for adding the path
|
|
10
|
+
\tto the appropriate configuration file (e.g., .bashrc, .zshrc, config.fish).
|
|
11
|
+
|
|
12
|
+
\tArgs:
|
|
13
|
+
\t\tinstall_path (str): The path to add to the PATH environment variable.
|
|
14
|
+
|
|
15
|
+
\tReturns:
|
|
16
|
+
\t\tbool: True if instructions were provided, False otherwise (e.g., unknown shell).
|
|
17
|
+
\t"""
|
|
18
|
+
def check_admin_linux() -> bool:
|
|
19
|
+
""" Check if the script is running with root privileges on Linux/macOS.
|
|
20
|
+
|
|
21
|
+
\tReturns:
|
|
22
|
+
\t\tbool: True if the effective user ID is 0 (root), False otherwise.
|
|
23
|
+
\t"""
|
|
24
|
+
def get_install_path_linux(program_name: str, ask_global: int = 0, add_path: bool = True, append_to_path: str = '', default_global: str = '/usr/local/bin') -> str:
|
|
25
|
+
''' Get the installation path for the program on Linux/macOS.
|
|
26
|
+
|
|
27
|
+
\tArgs:
|
|
28
|
+
\t\tprogram_name (str): The name of the program to install.
|
|
29
|
+
\t\task_global (int): 0 = ask for anything, 1 = install globally, 2 = install locally
|
|
30
|
+
\t\tadd_path (bool): Whether to add the program to the PATH environment variable. (Only if installed globally)
|
|
31
|
+
\t\tappend_to_path (str): String to append to the installation path when adding to PATH.
|
|
32
|
+
\t\t\t(ex: "bin" if executables are in the bin folder)
|
|
33
|
+
\t\tdefault_global (str): The default global installation path.
|
|
34
|
+
\t\t\t(Default is "/usr/local/bin" which is the most common location for executables on Linux/macOS,
|
|
35
|
+
\t\t\tcould be "/opt" or any other directory)
|
|
36
|
+
|
|
37
|
+
\tReturns:
|
|
38
|
+
\t\tstr: The chosen installation path, or an empty string if installation is cancelled.
|
|
39
|
+
\t'''
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from .common import *
|
|
2
|
+
from .linux import *
|
|
3
|
+
from .windows import *
|
|
4
|
+
from ..decorators import LogLevels as LogLevels, handle_error as handle_error
|
|
5
|
+
from ..print import info as info, warning as warning
|
|
6
|
+
from collections.abc import Callable as Callable
|
|
7
|
+
|
|
8
|
+
def extract_archive(extraction_path: str, temp_dir: str, extract_func: Callable[[str], None], get_file_list_func: Callable[[], list[str]]) -> None:
|
|
9
|
+
""" Helper function to extract archive files with consistent handling.
|
|
10
|
+
|
|
11
|
+
\tArgs:
|
|
12
|
+
\t\textraction_path (str): Path where files should be extracted
|
|
13
|
+
\t\ttemp_dir (str): Temporary directory for intermediate extraction
|
|
14
|
+
\t\textract_func (Callable[[str], None]): Function to extract the archive
|
|
15
|
+
\t\tget_file_list_func (Callable[[], list[str]]): Function to get the list of files in the archive
|
|
16
|
+
\t"""
|
|
17
|
+
def get_install_path(program_name: str, platform_str: str = ..., ask_global: int = 0, add_path: bool = True, append_to_path: str = '') -> str:
|
|
18
|
+
''' Get the installation path for the program on the current platform.
|
|
19
|
+
|
|
20
|
+
\tArgs:
|
|
21
|
+
\t\tprogram_name (str): The name of the program to install.
|
|
22
|
+
\t\tplatform_str (str): The platform to get the installation path for.
|
|
23
|
+
\t\task_global (int): Whether to ask the user for a path, 0 = ask, 1 = install globally, 2 = install locally.
|
|
24
|
+
\t\tadd_path (bool): Whether to add the program to the PATH environment variable.
|
|
25
|
+
\t\tappend_to_path (str): String to append to the installation path when adding to PATH.
|
|
26
|
+
\t\t\t(ex: "bin" if executables are in the bin folder)
|
|
27
|
+
|
|
28
|
+
\tReturns:
|
|
29
|
+
\t\tstr: The installation path for the program.
|
|
30
|
+
\t'''
|
|
31
|
+
def add_to_path(install_path: str, platform_str: str = ...) -> bool:
|
|
32
|
+
''' Add the program to the PATH environment variable.
|
|
33
|
+
|
|
34
|
+
\tArgs:
|
|
35
|
+
\t\tinstall_path (str): The path to the program to add to the PATH environment variable.
|
|
36
|
+
\t\tplatform_str (str): The platform you are running on (ex: "Windows", "Linux", "Darwin", ...),
|
|
37
|
+
\t\t\twe use this to determine the installation path if not provided.
|
|
38
|
+
|
|
39
|
+
\tReturns:
|
|
40
|
+
\t\tbool: True if add to PATH was successful, False otherwise.
|
|
41
|
+
\t'''
|
|
42
|
+
def install_program(input_path: str, install_path: str = '', platform_str: str = ..., program_name: str = '', add_path: bool = True, append_to_path: str = '') -> bool:
|
|
43
|
+
''' Install a program to a specific path from a local zip file or URL.
|
|
44
|
+
|
|
45
|
+
\tArgs:
|
|
46
|
+
\t\tinput_path (str): Path to a zip file or a download URL.
|
|
47
|
+
\t\tinstall_path (str): The directory to extract the program into, we ask user for a path if not provided.
|
|
48
|
+
\t\tplatform_str (str): The platform you are running on (ex: "Windows", "Linux", "Darwin", ...),
|
|
49
|
+
\t\t\twe use this to determine the installation path if not provided.
|
|
50
|
+
\t\tadd_path (bool): Whether to add the program to the PATH environment variable.
|
|
51
|
+
\t\tprogram_name (str): Override the program name, we get it from the input path if not provided.
|
|
52
|
+
\t\tappend_to_path (str): String to append to the installation path when adding to PATH.
|
|
53
|
+
\t\t\t(ex: "bin" if executables are in the bin folder)
|
|
54
|
+
|
|
55
|
+
\tReturns:
|
|
56
|
+
\t\tbool: True if installation was successful, False otherwise.
|
|
57
|
+
\t'''
|
|
@@ -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'''
|
|
@@ -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'''
|