stouputils 1.0.21__tar.gz → 1.0.22__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stouputils-1.0.21 → stouputils-1.0.22}/.github/workflows/documentation.yml +2 -1
- {stouputils-1.0.21 → stouputils-1.0.22}/PKG-INFO +1 -1
- stouputils-1.0.22/assets/all_doctests_module.gif +0 -0
- stouputils-1.0.22/assets/archive_module.gif +0 -0
- stouputils-1.0.22/assets/backup_module.gif +0 -0
- stouputils-1.0.22/assets/collections_module.gif +0 -0
- stouputils-1.0.22/assets/ctx_module.gif +0 -0
- stouputils-1.0.22/assets/decorators_module_1.gif +0 -0
- stouputils-1.0.22/assets/decorators_module_2.gif +0 -0
- stouputils-1.0.22/assets/io_module.gif +0 -0
- stouputils-1.0.22/assets/parallel_module.gif +0 -0
- stouputils-1.0.22/assets/print_module.gif +0 -0
- stouputils-1.0.22/examples/all_doctests.py +17 -0
- stouputils-1.0.22/examples/archive/corrupted.zip +0 -0
- stouputils-1.0.22/examples/archive.py +24 -0
- stouputils-1.0.22/examples/collections.py +28 -0
- stouputils-1.0.22/examples/ctx.py +25 -0
- stouputils-1.0.22/examples/decorators_1.py +29 -0
- stouputils-1.0.22/examples/decorators_2.py +21 -0
- stouputils-1.0.22/examples/delta_backup.py +7 -0
- stouputils-1.0.22/examples/io.py +33 -0
- stouputils-1.0.22/examples/parallel.py +28 -0
- stouputils-1.0.22/examples/print.py +27 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/pyproject.toml +1 -1
- {stouputils-1.0.21 → stouputils-1.0.22}/scripts/create_docs.py +57 -16
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/all_doctests.py +8 -5
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/archive.py +14 -4
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/backup.py +3 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/collections.py +3 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/continuous_delivery/cd_utils.py +1 -1
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/continuous_delivery/github.py +11 -4
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/ctx.py +10 -5
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/decorators.py +25 -52
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/dont_look/zip_file_override.py +1 -8
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/io.py +4 -2
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/parallel.py +10 -7
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/print.py +9 -18
- stouputils-1.0.21/doctests.py +0 -17
- stouputils-1.0.21/examples/delta_backup.py +0 -7
- {stouputils-1.0.21 → stouputils-1.0.22}/.gitignore +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/.python-version +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/1_upgrades.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/2_build.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/3_upload.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/LICENSE +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/README.md +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/all_in_one.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/build_all_in_one.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/copy_in_local.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/github_release.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/__init__.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/continuous_delivery/__init__.py +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/py.typed +0 -0
- {stouputils-1.0.21 → stouputils-1.0.22}/upgrade.py +0 -0
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
- uses: actions/setup-python@v5
|
|
21
21
|
- name: Install dependencies
|
|
22
22
|
run: |
|
|
23
|
-
pip install hatch stouputils sphinx sphinx_rtd_theme myst_parser
|
|
23
|
+
pip install hatch stouputils sphinx sphinx_rtd_theme myst_parser furo
|
|
24
24
|
hatch build
|
|
25
25
|
- name: Build latest docs
|
|
26
26
|
if: github.ref == 'refs/heads/main'
|
|
@@ -37,5 +37,6 @@ jobs:
|
|
|
37
37
|
publish_branch: gh-pages
|
|
38
38
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
39
39
|
publish_dir: docs/build/html
|
|
40
|
+
keep_files: true
|
|
40
41
|
force_orphan: false
|
|
41
42
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stouputils
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.22
|
|
4
4
|
Summary: Stouputils is a collection of utility modules designed to simplify and enhance the development process. It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers, and many more.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Stoupy51/stouputils
|
|
6
6
|
Project-URL: Issues, https://github.com/Stoupy51/stouputils/issues
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import os
|
|
4
|
+
import stouputils as stp
|
|
5
|
+
|
|
6
|
+
# Constants
|
|
7
|
+
ROOT: str = os.path.dirname(os.path.abspath(__file__))
|
|
8
|
+
FOLDER_TO_TEST: str = f"{ROOT}/../src"
|
|
9
|
+
|
|
10
|
+
# Main
|
|
11
|
+
@stp.measure_time(stp.info, message="All doctests finished")
|
|
12
|
+
def main() -> None:
|
|
13
|
+
stp.launch_tests(FOLDER_TO_TEST)
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
main()
|
|
17
|
+
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
from zipfile import ZipFile, BadZipFile
|
|
5
|
+
|
|
6
|
+
# Main
|
|
7
|
+
if __name__ == "__main__":
|
|
8
|
+
PREFIX: str = "examples/archive"
|
|
9
|
+
|
|
10
|
+
## Repair a corrupted zip file
|
|
11
|
+
# Try to read the first file
|
|
12
|
+
@stp.handle_error(BadZipFile)
|
|
13
|
+
def read_file() -> None:
|
|
14
|
+
with ZipFile(f"{PREFIX}/corrupted.zip", "r") as zip_file:
|
|
15
|
+
stp.info(zip_file.read("pack.mcmeta"))
|
|
16
|
+
read_file()
|
|
17
|
+
|
|
18
|
+
# Repair it
|
|
19
|
+
stp.repair_zip_file(f"{PREFIX}/corrupted.zip", f"{PREFIX}/repaired.zip")
|
|
20
|
+
|
|
21
|
+
# Read the first file
|
|
22
|
+
with ZipFile(f"{PREFIX}/repaired.zip", "r") as zip_file:
|
|
23
|
+
stp.info(zip_file.read("pack.mcmeta"))
|
|
24
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
|
|
5
|
+
# Main
|
|
6
|
+
if __name__ == "__main__":
|
|
7
|
+
|
|
8
|
+
# Example with numbers
|
|
9
|
+
numbers: list[int] = [1, 2, 3, 2, 1, 4, 3]
|
|
10
|
+
unique_numbers: list[int] = stp.unique_list(numbers)
|
|
11
|
+
stp.info(f"Original numbers: {numbers}")
|
|
12
|
+
stp.info(f"Unique numbers: {unique_numbers}")
|
|
13
|
+
|
|
14
|
+
# Example with sets using different methods
|
|
15
|
+
s1: set[int] = {1, 2, 3}
|
|
16
|
+
s2: set[int] = {2, 3, 4}
|
|
17
|
+
s3: set[int] = {1, 2, 3}
|
|
18
|
+
sets: list[set[int]] = [s1, s2, s1, s1, s3, s2, s3]
|
|
19
|
+
|
|
20
|
+
# Using id method (keeps s1 and s3 as separate objects)
|
|
21
|
+
unique_sets_id: list[set[int]] = stp.unique_list(sets, method="id")
|
|
22
|
+
stp.info(f"Unique sets (id method): {unique_sets_id}")
|
|
23
|
+
|
|
24
|
+
# Using str method (combines s1 and s3 as they have same string representation)
|
|
25
|
+
unique_sets_str: list[set[int]] = stp.unique_list(sets, method="str")
|
|
26
|
+
stp.info(f"Unique sets (str method): {unique_sets_str}")
|
|
27
|
+
|
|
28
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
# Main
|
|
8
|
+
if __name__ == "__main__":
|
|
9
|
+
|
|
10
|
+
with stp.Muffle(mute_stderr=True):
|
|
11
|
+
print("Nothing")
|
|
12
|
+
print("here", file=sys.stderr)
|
|
13
|
+
stp.info("will be")
|
|
14
|
+
stp.info("printed", file=sys.stderr)
|
|
15
|
+
|
|
16
|
+
OUTPUT_PATH: str = "_super_idol_de_xiao_rong.log"
|
|
17
|
+
with stp.LogToFile(OUTPUT_PATH):
|
|
18
|
+
stp.info("""
|
|
19
|
+
Why did the programmer always bring a ladder to work?
|
|
20
|
+
Because they spent so much time debugging and climbing through their log files!
|
|
21
|
+
""")
|
|
22
|
+
|
|
23
|
+
stp.breakpoint("Press Enter to continue...")
|
|
24
|
+
os.remove(OUTPUT_PATH)
|
|
25
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
# Main
|
|
7
|
+
if __name__ == "__main__":
|
|
8
|
+
|
|
9
|
+
# Cache the result of the function and measure the time it takes to execute
|
|
10
|
+
@stp.measure_time(stp.progress, "Time taken to execute long_function")
|
|
11
|
+
@stp.simple_cache()
|
|
12
|
+
def long_function() -> dict[str, int]:
|
|
13
|
+
stp.info("Starting long function...")
|
|
14
|
+
time.sleep(1)
|
|
15
|
+
stp.info("Long function finished!")
|
|
16
|
+
return {"a": 1, "b": 2}
|
|
17
|
+
|
|
18
|
+
a = long_function() # Takes 1 second
|
|
19
|
+
b = long_function() # Takes 0 second
|
|
20
|
+
stp.info(f"a: {a}, b: {b}, a is b: {a is b}")
|
|
21
|
+
b["c"] = 3
|
|
22
|
+
stp.info(f"a has been modified because a is b: {a}")
|
|
23
|
+
|
|
24
|
+
# Silent decorator
|
|
25
|
+
@stp.silent
|
|
26
|
+
def silent_function():
|
|
27
|
+
print("ON THE CONSOLE")
|
|
28
|
+
silent_function()
|
|
29
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
|
|
5
|
+
# Main
|
|
6
|
+
if __name__ == "__main__":
|
|
7
|
+
|
|
8
|
+
# Handle exceptions
|
|
9
|
+
@stp.handle_error(ZeroDivisionError,
|
|
10
|
+
"Debugging: The process of removing software bugs, and putting in new ones",
|
|
11
|
+
error_log=stp.LogLevels.WARNING_TRACEBACK
|
|
12
|
+
)
|
|
13
|
+
def raise_value_error():
|
|
14
|
+
return 1 / 0
|
|
15
|
+
|
|
16
|
+
@stp.handle_error()
|
|
17
|
+
def raise_value_error_2():
|
|
18
|
+
return 1 / 0
|
|
19
|
+
|
|
20
|
+
raise_value_error() # This will show the error using stp.warning
|
|
21
|
+
raise_value_error_2() # This will show the error using stp.error
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
# Main
|
|
7
|
+
if __name__ == "__main__":
|
|
8
|
+
|
|
9
|
+
path: str = "C:\\\\Users\\\\Stoupy\\\\Documents\\\\test.txt"
|
|
10
|
+
path = stp.clean_path(path)
|
|
11
|
+
stp.info(path)
|
|
12
|
+
|
|
13
|
+
tilde_path: str = "~/Desktop/OnlyFansIncome.txt"
|
|
14
|
+
tilde_path = stp.replace_tilde(tilde_path)
|
|
15
|
+
stp.info(tilde_path)
|
|
16
|
+
|
|
17
|
+
this_folder_dont_exist: str = "./this_folder_dont_exist/a/c/feff/efefe/a"
|
|
18
|
+
with stp.super_open(this_folder_dont_exist, "w") as file: # Automatically create the folder
|
|
19
|
+
file.write("Hello, world!")
|
|
20
|
+
|
|
21
|
+
# Copy a file to a folder, or rename the copied file
|
|
22
|
+
stp.super_copy("LICENSE", "this_folder_dont_exist/a/") # .../a/LICENSE
|
|
23
|
+
stp.super_copy("LICENSE", "this_folder_dont_exist/a/LICENSE_2") # .../a/LICENSE_2
|
|
24
|
+
stp.breakpoint("Waiting for input to continue code execution...")
|
|
25
|
+
|
|
26
|
+
# Dump a JSON file with a specified indentation depth
|
|
27
|
+
data: dict[str, Any] = {"name": "John", "array": [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]}
|
|
28
|
+
stp.info("\n", stp.super_json_dump(data, max_level=2))
|
|
29
|
+
|
|
30
|
+
# Remove the folder
|
|
31
|
+
import shutil
|
|
32
|
+
shutil.rmtree("this_folder_dont_exist")
|
|
33
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import stouputils as stp
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
# Functions
|
|
7
|
+
def is_even(n: int) -> bool:
|
|
8
|
+
return n % 2 == 0
|
|
9
|
+
|
|
10
|
+
def multiple_args(a: int, b: int) -> int:
|
|
11
|
+
return a * b
|
|
12
|
+
|
|
13
|
+
# Main
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
|
|
16
|
+
# Multi-threading (blazingly fast for IO-bound tasks)
|
|
17
|
+
args_1: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
18
|
+
results_1: list[bool] = stp.multithreading(is_even, args_1)
|
|
19
|
+
stp.info(f"Results: {results_1}")
|
|
20
|
+
|
|
21
|
+
# Multi-processing (better for CPU-bound tasks)
|
|
22
|
+
time.sleep(1)
|
|
23
|
+
args_2: list[tuple[int, int]] = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
|
|
24
|
+
results_2: list[int] = stp.multiprocessing(
|
|
25
|
+
multiple_args, args_2, use_starmap=True, desc="Multiple args", max_workers=2, verbose=1
|
|
26
|
+
)
|
|
27
|
+
stp.info(f"Results: {results_2}")
|
|
28
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
import time
|
|
4
|
+
import stouputils as stp
|
|
5
|
+
|
|
6
|
+
# Main
|
|
7
|
+
if __name__ == "__main__":
|
|
8
|
+
stp.info("Hello", "World")
|
|
9
|
+
time.sleep(0.5)
|
|
10
|
+
stp.info("Hello", "World")
|
|
11
|
+
time.sleep(0.5)
|
|
12
|
+
stp.info("Hello", "World")
|
|
13
|
+
time.sleep(0.5)
|
|
14
|
+
stp.info("Not Hello World !")
|
|
15
|
+
time.sleep(0.5)
|
|
16
|
+
stp.info("Hello", "World")
|
|
17
|
+
time.sleep(0.5)
|
|
18
|
+
stp.info("Hello", "World")
|
|
19
|
+
|
|
20
|
+
# All remaining print functions
|
|
21
|
+
stp.debug("Hello", "World")
|
|
22
|
+
stp.suggestion("Hello", "World")
|
|
23
|
+
stp.progress("Hello", "World")
|
|
24
|
+
stp.warning("Hello", "World")
|
|
25
|
+
stp.error("Hello", "World", exit=False)
|
|
26
|
+
stp.whatisit("Hello", "World")
|
|
27
|
+
|
|
@@ -4,7 +4,8 @@ import os
|
|
|
4
4
|
import shutil
|
|
5
5
|
import subprocess
|
|
6
6
|
import sys
|
|
7
|
-
from stouputils import clean_path, handle_error
|
|
7
|
+
from stouputils import clean_path, handle_error, warning, simple_cache
|
|
8
|
+
from stouputils.continuous_delivery.github import version_to_float
|
|
8
9
|
clean_exec: str = clean_path(sys.executable)
|
|
9
10
|
|
|
10
11
|
conf_content: str = """
|
|
@@ -26,21 +27,45 @@ release: str = current_version
|
|
|
26
27
|
extensions: list[str] = [
|
|
27
28
|
'sphinx.ext.autodoc',
|
|
28
29
|
'sphinx.ext.napoleon',
|
|
29
|
-
'sphinx.ext.viewcode',
|
|
30
|
+
'sphinx.ext.viewcode',
|
|
30
31
|
'sphinx.ext.githubpages',
|
|
31
32
|
'sphinx.ext.intersphinx',
|
|
33
|
+
'furo.sphinxext',
|
|
32
34
|
]
|
|
33
35
|
|
|
34
36
|
templates_path: list[str] = ['_templates']
|
|
35
37
|
exclude_patterns: list[str] = []
|
|
36
38
|
|
|
37
39
|
# HTML output options
|
|
38
|
-
html_theme: str = '
|
|
40
|
+
html_theme: str = 'furo'
|
|
39
41
|
html_static_path: list[str] = ['_static']
|
|
42
|
+
html_logo: str = 'https://avatars.githubusercontent.com/u/35665974'
|
|
43
|
+
html_title: str = 'stouputils'
|
|
44
|
+
html_favicon: str = 'https://avatars.githubusercontent.com/u/35665974'
|
|
40
45
|
|
|
41
46
|
# Theme options
|
|
42
47
|
html_theme_options: dict[str, Any] = {
|
|
43
|
-
'
|
|
48
|
+
'light_css_variables': {
|
|
49
|
+
'color-brand-primary': '#2980B9',
|
|
50
|
+
'color-brand-content': '#2980B9',
|
|
51
|
+
'color-admonition-background': '#E8F0F8',
|
|
52
|
+
},
|
|
53
|
+
'dark_css_variables': {
|
|
54
|
+
'color-brand-primary': '#56B4E9',
|
|
55
|
+
'color-brand-content': '#56B4E9',
|
|
56
|
+
'color-admonition-background': '#1F262B',
|
|
57
|
+
},
|
|
58
|
+
'sidebar_hide_name': False,
|
|
59
|
+
'navigation_with_keys': True,
|
|
60
|
+
'announcement': 'This is the latest documentation of stouputils',
|
|
61
|
+
'footer_icons': [
|
|
62
|
+
{
|
|
63
|
+
'name': 'GitHub',
|
|
64
|
+
'url': 'https://github.com/Stoupy51/stouputils',
|
|
65
|
+
'html': '<i class="fab fa-github-square"></i>',
|
|
66
|
+
'class': '',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
44
69
|
}
|
|
45
70
|
|
|
46
71
|
# Add any paths that contain custom static files
|
|
@@ -71,6 +96,7 @@ html_context = {
|
|
|
71
96
|
'github_version': 'main',
|
|
72
97
|
'conf_py_path': '/docs/source/',
|
|
73
98
|
'source_suffix': '.rst',
|
|
99
|
+
'default_mode': 'dark',
|
|
74
100
|
}
|
|
75
101
|
|
|
76
102
|
# Only document items with docstrings
|
|
@@ -83,6 +109,30 @@ def setup(app: Any) -> None:
|
|
|
83
109
|
app.connect('autodoc-skip-member', skip_undocumented)
|
|
84
110
|
"""
|
|
85
111
|
|
|
112
|
+
@simple_cache()
|
|
113
|
+
def get_versions_from_github() -> list[str]:
|
|
114
|
+
""" Get list of versions from GitHub gh-pages branch.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
list[str]: List of versions, with 'latest' as first element
|
|
118
|
+
"""
|
|
119
|
+
import requests
|
|
120
|
+
version_list: list[str] = []
|
|
121
|
+
try:
|
|
122
|
+
response = requests.get("https://api.github.com/repos/Stoupy51/stouputils/contents?ref=gh-pages")
|
|
123
|
+
if response.status_code == 200:
|
|
124
|
+
contents = response.json()
|
|
125
|
+
version_list = ["latest"] + sorted(
|
|
126
|
+
[d["name"].replace("v", "") for d in contents
|
|
127
|
+
if d["type"] == "dir" and d["name"].startswith("v")],
|
|
128
|
+
key=version_to_float,
|
|
129
|
+
reverse=True
|
|
130
|
+
)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
warning(f"Failed to get versions from GitHub: {e}")
|
|
133
|
+
version_list = ["latest"]
|
|
134
|
+
return version_list
|
|
135
|
+
|
|
86
136
|
def generate_index_rst(readme_path: str, index_path: str) -> None:
|
|
87
137
|
""" Generate index.rst from README.md content.
|
|
88
138
|
|
|
@@ -107,14 +157,8 @@ def generate_index_rst(readme_path: str, index_path: str) -> None:
|
|
|
107
157
|
|
|
108
158
|
**Versions**: """
|
|
109
159
|
|
|
110
|
-
#
|
|
111
|
-
version_list: list[str] =
|
|
112
|
-
html_dir = "docs/build/html"
|
|
113
|
-
if os.path.exists(html_dir):
|
|
114
|
-
version_list = [d[1:] for d in os.listdir(html_dir) if d.startswith('v')]
|
|
115
|
-
from stouputils.continuous_delivery.github import version_to_float
|
|
116
|
-
version_list.sort(key=version_to_float, reverse=True)
|
|
117
|
-
version_list.insert(0, 'latest')
|
|
160
|
+
# Get versions from GitHub
|
|
161
|
+
version_list: list[str] = get_versions_from_github()
|
|
118
162
|
|
|
119
163
|
# Create version links
|
|
120
164
|
version_links: list[str] = []
|
|
@@ -218,10 +262,7 @@ def update_documentation(version: str | None = None) -> None:
|
|
|
218
262
|
os.makedirs(modules_dir)
|
|
219
263
|
|
|
220
264
|
# Update conf.py to include version selector
|
|
221
|
-
version_list: list[str] =
|
|
222
|
-
if os.path.exists(clean_path(f"{docs_dir}/build/html")):
|
|
223
|
-
version_list = [d.replace("v", "") for d in os.listdir(clean_path(f"{docs_dir}/build/html"))
|
|
224
|
-
if d.startswith("v")] + ["latest"]
|
|
265
|
+
version_list: list[str] = get_versions_from_github()
|
|
225
266
|
|
|
226
267
|
# Update html_context in conf.py
|
|
227
268
|
global conf_content
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module is used to run all the doctests for all the modules in a given directory.
|
|
3
|
+
|
|
4
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/all_doctests_module.gif
|
|
5
|
+
:alt: stouputils all_doctests examples
|
|
3
6
|
"""
|
|
4
7
|
|
|
5
8
|
# Imports
|
|
6
9
|
import os
|
|
7
10
|
import sys
|
|
8
|
-
from .print import
|
|
9
|
-
from .decorators import measure_time, handle_error, LogLevels
|
|
11
|
+
from .print import info, error, progress
|
|
12
|
+
from .decorators import measure_time, handle_error, LogLevels
|
|
13
|
+
from . import decorators
|
|
10
14
|
from doctest import TestResults, testmod
|
|
11
15
|
from types import ModuleType
|
|
12
16
|
import importlib
|
|
@@ -46,10 +50,9 @@ def launch_tests(root_dir: str, importing_errors: LogLevels = LogLevels.WARNING_
|
|
|
46
50
|
[PROGRESS HH:MM:SS] Testing module 'module3' took 0.007s
|
|
47
51
|
[PROGRESS HH:MM:SS] Testing module 'module4' took 0.008s
|
|
48
52
|
"""
|
|
49
|
-
global force_raise_exception
|
|
50
53
|
if strict:
|
|
51
54
|
old_value: bool = strict
|
|
52
|
-
force_raise_exception = True
|
|
55
|
+
decorators.force_raise_exception = True
|
|
53
56
|
strict = old_value
|
|
54
57
|
|
|
55
58
|
|
|
@@ -91,5 +94,5 @@ def launch_tests(root_dir: str, importing_errors: LogLevels = LogLevels.WARNING_
|
|
|
91
94
|
error(f"Errors in module {module.__name__}", exit=False)
|
|
92
95
|
|
|
93
96
|
# Reset force_raise_exception back
|
|
94
|
-
force_raise_exception = strict
|
|
97
|
+
decorators.force_raise_exception = strict
|
|
95
98
|
|
|
@@ -3,12 +3,17 @@ This module provides functions for creating and managing archives.
|
|
|
3
3
|
|
|
4
4
|
- make_archive: Make an archive with consistency using FILES_TO_WRITE variable
|
|
5
5
|
- repair_zip_file: Try to repair a corrupted zip file (NOT IMPLEMENTED)
|
|
6
|
+
|
|
7
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/archive_module.gif
|
|
8
|
+
:alt: stouputils archive examples
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
# Imports
|
|
9
|
-
|
|
10
|
-
from .print import *
|
|
12
|
+
import os
|
|
11
13
|
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED
|
|
14
|
+
from .io import clean_path, super_copy
|
|
15
|
+
from .decorators import handle_error, LogLevels
|
|
16
|
+
from .dont_look.zip_file_override import ZipFileOverride
|
|
12
17
|
|
|
13
18
|
# Function that makes an archive with consistency (same zip file each time)
|
|
14
19
|
@handle_error()
|
|
@@ -18,7 +23,12 @@ def make_archive(
|
|
|
18
23
|
override_time: None | tuple[int, int, int, int, int, int] = None,
|
|
19
24
|
create_dir: bool = False
|
|
20
25
|
) -> bool:
|
|
21
|
-
"""
|
|
26
|
+
""" Create a zip archive from a source directory with consistent file timestamps.
|
|
27
|
+
(Meaning deterministic zip file each time)
|
|
28
|
+
|
|
29
|
+
Creates a zip archive from the source directory and copies it to one or more destinations.
|
|
30
|
+
The archive will have consistent file timestamps across runs if override_time is specified.
|
|
31
|
+
Uses maximum compression level (9) with ZIP_DEFLATED algorithm.
|
|
22
32
|
|
|
23
33
|
Args:
|
|
24
34
|
source (str): The source folder to archive
|
|
@@ -33,6 +43,7 @@ def make_archive(
|
|
|
33
43
|
|
|
34
44
|
> make_archive("/path/to/source", "/path/to/destination.zip")
|
|
35
45
|
> make_archive("/path/to/source", ["/path/to/destination.zip", "/path/to/destination2.zip"])
|
|
46
|
+
> make_archive("src", "hello_from_year_2085.zip", override_time=(2085,1,1,0,0,0))
|
|
36
47
|
"""
|
|
37
48
|
# Fix copy_destinations type if needed
|
|
38
49
|
if destinations and isinstance(destinations, str):
|
|
@@ -66,7 +77,6 @@ def make_archive(
|
|
|
66
77
|
|
|
67
78
|
|
|
68
79
|
# Function that repair a corrupted zip file (ignoring some of the errors)
|
|
69
|
-
from .dont_look.zip_file_override import ZipFileOverride
|
|
70
80
|
@handle_error()
|
|
71
81
|
def repair_zip_file(file_path: str, destination: str) -> bool:
|
|
72
82
|
""" Try to repair a corrupted zip file by ignoring some of the errors
|
|
@@ -5,6 +5,9 @@ This module provides utilities for backup management.
|
|
|
5
5
|
- create_delta_backup: Creates a ZIP delta backup, saving only modified or new files while tracking deleted files
|
|
6
6
|
- consolidate_backups: Consolidates the files from the given backup and all previous ones into a new ZIP file
|
|
7
7
|
- backup_cli: Main entry point for command line usage
|
|
8
|
+
|
|
9
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/backup_module.gif
|
|
10
|
+
:alt: stouputils backup examples
|
|
8
11
|
"""
|
|
9
12
|
|
|
10
13
|
# Standard library imports
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
This module provides utilities for collection manipulation:
|
|
3
3
|
|
|
4
4
|
- unique_list: Remove duplicates from a list while preserving order using object id, hash or str
|
|
5
|
+
|
|
6
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/collections_module.gif
|
|
7
|
+
:alt: stouputils collections examples
|
|
5
8
|
"""
|
|
6
9
|
|
|
7
10
|
# Imports
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
""" This module contains utilities for continuous delivery on GitHub.
|
|
2
2
|
|
|
3
3
|
- upload_to_github: Upload the project to GitHub using the credentials and the configuration (make a release and upload the assets, handle existing tag, generate changelog, etc.)
|
|
4
|
+
|
|
5
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/continuous_delivery/github_module.gif
|
|
6
|
+
:alt: stouputils upload_to_github examples
|
|
4
7
|
"""
|
|
5
8
|
|
|
6
9
|
# Imports
|
|
7
|
-
from ..print import
|
|
8
|
-
from ..decorators import measure_time
|
|
9
|
-
from
|
|
10
|
+
from ..print import info, warning, progress
|
|
11
|
+
from ..decorators import measure_time, handle_error
|
|
12
|
+
from ..io import clean_path
|
|
13
|
+
from .cd_utils import handle_response
|
|
14
|
+
from typing import Any
|
|
15
|
+
import requests
|
|
16
|
+
import os
|
|
10
17
|
|
|
11
18
|
# Constants
|
|
12
19
|
GITHUB_API_URL: str = "https://api.github.com"
|
|
@@ -131,7 +138,7 @@ def delete_existing_tag(tag_url: str, headers: dict[str, str]) -> None:
|
|
|
131
138
|
"""
|
|
132
139
|
delete_response: requests.Response = requests.delete(tag_url, headers=headers)
|
|
133
140
|
handle_response(delete_response, "Failed to delete existing tag")
|
|
134
|
-
info(
|
|
141
|
+
info("Deleted existing tag")
|
|
135
142
|
|
|
136
143
|
def clean_version(version: str, keep: str = "") -> str:
|
|
137
144
|
""" Clean a version string
|
|
@@ -3,12 +3,17 @@ This module provides context managers for temporarily silencing output.
|
|
|
3
3
|
|
|
4
4
|
- Muffle: Context manager that temporarily silences output (alternative to stouputils.decorators.silent())
|
|
5
5
|
- LogToFile: Context manager to log to a file every calls to the print functions in stouputils.print
|
|
6
|
+
|
|
7
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/ctx_module.gif
|
|
8
|
+
:alt: stouputils ctx examples
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
# Imports
|
|
9
12
|
import os
|
|
10
13
|
import sys
|
|
11
14
|
from typing import IO, TextIO, Callable, Any
|
|
15
|
+
from .print import logging_to
|
|
16
|
+
from .io import super_open
|
|
12
17
|
|
|
13
18
|
# Context manager to temporarily silence output
|
|
14
19
|
class Muffle:
|
|
@@ -39,8 +44,6 @@ class Muffle:
|
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
# Context manager to log to a file
|
|
42
|
-
from .print import logging_to
|
|
43
|
-
from .io import super_open
|
|
44
47
|
class LogToFile:
|
|
45
48
|
""" Context manager to log to a file.
|
|
46
49
|
|
|
@@ -53,9 +56,11 @@ class LogToFile:
|
|
|
53
56
|
encoding (str): Encoding to use for the file (default: "utf-8")
|
|
54
57
|
|
|
55
58
|
Examples:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
.. code-block:: python
|
|
60
|
+
|
|
61
|
+
> import stouputils as stp
|
|
62
|
+
> with stp.LogToFile("output.log"):
|
|
63
|
+
> stp.info("This will be logged to output.log and printed normally")
|
|
59
64
|
"""
|
|
60
65
|
def __init__(self, path: str, mode: str = "w", encoding: str = "utf-8") -> None:
|
|
61
66
|
self.path: str = path
|
|
@@ -6,6 +6,12 @@ This module provides decorators for various purposes:
|
|
|
6
6
|
- handle_error(): Handle an error with different log levels
|
|
7
7
|
- simple_cache(): Easy cache function with parameter caching method
|
|
8
8
|
- deprecated(): Mark a function as deprecated
|
|
9
|
+
|
|
10
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/decorators_module_1.gif
|
|
11
|
+
:alt: stouputils decorators examples
|
|
12
|
+
|
|
13
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/decorators_module_2.gif
|
|
14
|
+
:alt: stouputils decorators examples
|
|
9
15
|
"""
|
|
10
16
|
|
|
11
17
|
# Imports
|
|
@@ -17,7 +23,7 @@ from pickle import dumps as pickle_dumps
|
|
|
17
23
|
from traceback import format_exc
|
|
18
24
|
from typing import Callable, Literal, Any
|
|
19
25
|
from functools import wraps
|
|
20
|
-
from .print import
|
|
26
|
+
from .print import debug, warning, error
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
# Decorator that make a function silent (disable stdout)
|
|
@@ -82,10 +88,12 @@ def measure_time(
|
|
|
82
88
|
Callable: Decorator to measure the time of the function.
|
|
83
89
|
|
|
84
90
|
Examples:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
.. code-block:: python
|
|
92
|
+
|
|
93
|
+
> @measure_time(info)
|
|
94
|
+
> def test():
|
|
95
|
+
> pass
|
|
96
|
+
> test() # [INFO HH:MM:SS] Execution time of test: 0.000ms (400ns)
|
|
89
97
|
"""
|
|
90
98
|
ns: Callable[[], int] = time.perf_counter_ns if perf_counter else time.time_ns
|
|
91
99
|
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
@@ -165,10 +173,12 @@ def handle_error(
|
|
|
165
173
|
LogLevels.RAISE_EXCEPTION: Raise exception (as if the decorator didn't exist)
|
|
166
174
|
|
|
167
175
|
Examples:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
.. code-block:: python
|
|
177
|
+
|
|
178
|
+
> @handle_error(error_log=LogLevels.WARNING)
|
|
179
|
+
> def test():
|
|
180
|
+
> raise ValueError("Let's fail")
|
|
181
|
+
> test() # [WARNING HH:MM:SS] Error during test: (ValueError) Let's fail
|
|
172
182
|
"""
|
|
173
183
|
# Update error_log if needed
|
|
174
184
|
if force_raise_exception:
|
|
@@ -271,10 +281,12 @@ def deprecated(
|
|
|
271
281
|
Callable[..., Any]: Decorator that marks a function as deprecated
|
|
272
282
|
|
|
273
283
|
Examples:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
284
|
+
.. code-block:: python
|
|
285
|
+
|
|
286
|
+
> @deprecated(message="Use 'this_function()' instead", error_log=LogLevels.WARNING)
|
|
287
|
+
> def test():
|
|
288
|
+
> pass
|
|
289
|
+
> test() # [WARNING HH:MM:SS] Function 'test()' is deprecated. Use 'this_function()' instead
|
|
278
290
|
"""
|
|
279
291
|
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
280
292
|
@wraps(func)
|
|
@@ -299,42 +311,3 @@ def deprecated(
|
|
|
299
311
|
return wrapper
|
|
300
312
|
return decorator
|
|
301
313
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
def __test_simple_cache():
|
|
308
|
-
@measure_time(progress)
|
|
309
|
-
@simple_cache(method="pickle")
|
|
310
|
-
def test_pickle_cache(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> str:
|
|
311
|
-
return "pickle cache"
|
|
312
|
-
|
|
313
|
-
@measure_time(progress)
|
|
314
|
-
@simple_cache(method="str")
|
|
315
|
-
def test_str_cache(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> str:
|
|
316
|
-
return "str cache"
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
args: list[int] = list(range(1_000_000))
|
|
320
|
-
kwargs: dict[str, int] = {str(i): i for i in args}
|
|
321
|
-
|
|
322
|
-
info("Testing with large arguments")
|
|
323
|
-
for _ in range(2):
|
|
324
|
-
test_pickle_cache(*args, **kwargs)
|
|
325
|
-
test_str_cache(*args, **kwargs)
|
|
326
|
-
|
|
327
|
-
info("Testing with small arguments")
|
|
328
|
-
for _ in range(2):
|
|
329
|
-
test_pickle_cache()
|
|
330
|
-
test_str_cache()
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if __name__ == "__main__":
|
|
334
|
-
__test_simple_cache()
|
|
335
|
-
|
|
336
|
-
@deprecated(message="Use 'this_function()' instead", error_log=LogLevels.WARNING)
|
|
337
|
-
def test_deprecated():
|
|
338
|
-
return "test"
|
|
339
|
-
test_deprecated()
|
|
340
|
-
|
|
@@ -6,7 +6,7 @@ See the archive.py module for more information.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
# Imports
|
|
9
|
-
from zipfile import ZipFile, ZipInfo, ZipExtFile, _SharedFile, sizeFileHeader, struct, structFileHeader,
|
|
9
|
+
from zipfile import ZipFile, ZipInfo, ZipExtFile, _SharedFile, sizeFileHeader, struct, structFileHeader, _FH_EXTRA_FIELD_LENGTH, crc32 # type: ignore
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
# Class overrides
|
|
@@ -73,7 +73,6 @@ class ZipFileOverride(ZipFile):
|
|
|
73
73
|
fheader = zef_file.read(sizeFileHeader) # type: ignore
|
|
74
74
|
fheader = struct.unpack(structFileHeader, fheader) # type: ignore
|
|
75
75
|
|
|
76
|
-
fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) # type: ignore
|
|
77
76
|
if fheader[_FH_EXTRA_FIELD_LENGTH]:
|
|
78
77
|
zef_file.seek(fheader[_FH_EXTRA_FIELD_LENGTH], whence=1) # type: ignore
|
|
79
78
|
|
|
@@ -85,12 +84,6 @@ class ZipFileOverride(ZipFile):
|
|
|
85
84
|
# strong encryption
|
|
86
85
|
raise NotImplementedError("strong encryption (flag bit 6)")
|
|
87
86
|
|
|
88
|
-
if fheader[_FH_GENERAL_PURPOSE_FLAG_BITS] & 0x800:
|
|
89
|
-
# UTF-8 filename
|
|
90
|
-
fname_str = fname.decode("utf-8") # type: ignore
|
|
91
|
-
else:
|
|
92
|
-
fname_str = fname.decode(self.metadata_encoding or "cp437") # type: ignore
|
|
93
|
-
|
|
94
87
|
# if (zinfo._end_offset is not None and
|
|
95
88
|
# zef_file.tell() + zinfo.compress_size > zinfo._end_offset):
|
|
96
89
|
# raise BadZipFile(f"Overlapped entries: {zinfo.orig_filename!r} (possible zip bomb)")
|
|
@@ -7,15 +7,17 @@ This module provides utilities for file management.
|
|
|
7
7
|
- super_copy: Copy a file (or a folder) from the source to the destination (always create the directory)
|
|
8
8
|
- super_json_load: Load a JSON file from the given path
|
|
9
9
|
- super_json_dump: Writes the provided data to a JSON file with a specified indentation depth.
|
|
10
|
+
|
|
11
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/io_module.gif
|
|
12
|
+
:alt: stouputils io examples
|
|
10
13
|
"""
|
|
11
14
|
|
|
12
15
|
# Imports
|
|
13
|
-
from .decorators import *
|
|
14
16
|
import shutil
|
|
15
17
|
import json
|
|
16
18
|
import os
|
|
17
19
|
import io
|
|
18
|
-
from typing import IO
|
|
20
|
+
from typing import IO, Any
|
|
19
21
|
|
|
20
22
|
# Function that replace the "~" by the user's home directory
|
|
21
23
|
def replace_tilde(path: str) -> str:
|
|
@@ -5,11 +5,14 @@ This module provides utility functions for parallel processing, such as:
|
|
|
5
5
|
- multithreading(): Execute a function in parallel using multithreading
|
|
6
6
|
|
|
7
7
|
I highly encourage you to read the function docstrings to understand when to use each method.
|
|
8
|
+
|
|
9
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/parallel_module.gif
|
|
10
|
+
:alt: stouputils parallel examples
|
|
8
11
|
"""
|
|
9
12
|
|
|
10
13
|
# Imports
|
|
11
|
-
from .print import
|
|
12
|
-
from .decorators import
|
|
14
|
+
from .print import MAGENTA, RESET
|
|
15
|
+
from .decorators import handle_error, LogLevels
|
|
13
16
|
from multiprocessing import Pool, cpu_count
|
|
14
17
|
from typing import Callable, TypeVar
|
|
15
18
|
from tqdm import tqdm
|
|
@@ -21,7 +24,7 @@ import time
|
|
|
21
24
|
def doctest_square(x: int) -> int:
|
|
22
25
|
return x * x
|
|
23
26
|
def doctest_slow(x: int) -> int:
|
|
24
|
-
time.sleep(0.
|
|
27
|
+
time.sleep(0.1)
|
|
25
28
|
return x
|
|
26
29
|
|
|
27
30
|
# Constants
|
|
@@ -107,11 +110,11 @@ def multiprocessing(func: Callable[[T], R], args: list[T], use_starmap: bool = F
|
|
|
107
110
|
[2, 12, 30]
|
|
108
111
|
|
|
109
112
|
>>> # Will process in parallel with progress bar
|
|
110
|
-
>>> multiprocessing(doctest_slow,
|
|
113
|
+
>>> multiprocessing(doctest_slow, range(10), desc="Processing", verbose=1)
|
|
111
114
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
112
115
|
|
|
113
116
|
>>> # Will process in parallel with progress bar and delay the first threads
|
|
114
|
-
>>> multiprocessing(doctest_slow,
|
|
117
|
+
>>> multiprocessing(doctest_slow, range(10), desc="Processing with delay", max_workers=2, delay_first_calls=0.6, verbose=1)
|
|
115
118
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
116
119
|
"""
|
|
117
120
|
# Handle parameters
|
|
@@ -159,11 +162,11 @@ def multithreading(func: Callable[[T], R], args: list[T], use_starmap: bool = Fa
|
|
|
159
162
|
[2, 12, 30]
|
|
160
163
|
|
|
161
164
|
>>> # Will process in parallel with progress bar
|
|
162
|
-
>>> multithreading(doctest_slow,
|
|
165
|
+
>>> multithreading(doctest_slow, range(10), desc="Threading", verbose=1)
|
|
163
166
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
164
167
|
|
|
165
168
|
>>> # Will process in parallel with progress bar and delay the first threads
|
|
166
|
-
>>> multithreading(doctest_slow,
|
|
169
|
+
>>> multithreading(doctest_slow, range(10), desc="Threading with delay", max_workers=2, delay_first_calls=0.6, verbose=1)
|
|
167
170
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
168
171
|
"""
|
|
169
172
|
# Handle parameters
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
|
-
This module provides utility functions for printing messages with different levels of importance
|
|
3
|
-
If a message is printed multiple times, it will be displayed as "(xN) message" where N is the number of times the message has been printed.
|
|
4
|
-
|
|
5
|
-
The functions are such as:
|
|
2
|
+
This module provides utility functions for printing messages with different levels of importance.
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
- debug()
|
|
9
|
-
- suggestion()
|
|
10
|
-
- progress()
|
|
11
|
-
- warning()
|
|
12
|
-
- error()
|
|
13
|
-
- whatisit(): a function to print the type of each value and the value itself (and few other things)
|
|
14
|
-
- breakpoint(): a breakpoint function to pause the program while calling whatisit()
|
|
15
|
-
- logging_to: a set of file-like objects that will receive log messages without ANSI color codes, see stouputils.ctx.LogToFile for easy logging
|
|
16
|
-
|
|
17
|
-
Here is a demonstration gif showing examples of uses:
|
|
4
|
+
If a message is printed multiple times, it will be displayed as "(xN) message" where N is the number of times the message has been printed.
|
|
18
5
|
|
|
19
|
-
.. image:: https://
|
|
6
|
+
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/print_module.gif
|
|
20
7
|
:alt: stouputils print examples
|
|
21
8
|
"""
|
|
22
9
|
|
|
@@ -67,7 +54,7 @@ def current_time() -> str:
|
|
|
67
54
|
else:
|
|
68
55
|
return time.strftime("%H:%M:%S")
|
|
69
56
|
|
|
70
|
-
def info(*values: Any, color: str = GREEN, text: str = "INFO ", prefix: str = "", file: TextIO|list[TextIO] =
|
|
57
|
+
def info(*values: Any, color: str = GREEN, text: str = "INFO ", prefix: str = "", file: TextIO|list[TextIO]|None = None, **print_kwargs: Any) -> None:
|
|
71
58
|
""" Print an information message looking like "[INFO HH:MM:SS] message" in green by default.
|
|
72
59
|
|
|
73
60
|
Args:
|
|
@@ -78,6 +65,8 @@ def info(*values: Any, color: str = GREEN, text: str = "INFO ", prefix: str = ""
|
|
|
78
65
|
file (TextIO|list[TextIO]): File(s) to write the message to (default: sys.stdout)
|
|
79
66
|
print_kwargs (dict): Keyword arguments to pass to the print function
|
|
80
67
|
"""
|
|
68
|
+
if file is None:
|
|
69
|
+
file = sys.stdout
|
|
81
70
|
if isinstance(file, list):
|
|
82
71
|
for f in file:
|
|
83
72
|
info(*values, color=color, text=text, prefix=prefix, file=f, **print_kwargs)
|
|
@@ -180,7 +169,9 @@ def whatisit(*values: Any, print_function: Callable[..., None] = debug, max_leng
|
|
|
180
169
|
value_str: str = str(value)
|
|
181
170
|
if len(value_str) > max_length:
|
|
182
171
|
value_str = value_str[:max_length] + "..."
|
|
183
|
-
|
|
172
|
+
if "\n" in value_str:
|
|
173
|
+
length = "\n" + length # Add a newline before the length if there is a newline in the value.
|
|
174
|
+
return f"{type(value)}, <id {id(value)}>: {length}{value_str}"
|
|
184
175
|
|
|
185
176
|
# Print
|
|
186
177
|
if len(values) > 1:
|
stouputils-1.0.21/doctests.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# Imports
|
|
3
|
-
import os
|
|
4
|
-
from src.stouputils import launch_tests, measure_time, info
|
|
5
|
-
|
|
6
|
-
# Constants
|
|
7
|
-
ROOT: str = os.path.dirname(os.path.abspath(__file__))
|
|
8
|
-
FOLDER_TO_TEST: str = f"{ROOT}/src"
|
|
9
|
-
|
|
10
|
-
# Main
|
|
11
|
-
@measure_time(info, message="All doctests finished")
|
|
12
|
-
def main() -> None:
|
|
13
|
-
launch_tests(FOLDER_TO_TEST)
|
|
14
|
-
|
|
15
|
-
if __name__ == "__main__":
|
|
16
|
-
main()
|
|
17
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|