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.
Files changed (54) hide show
  1. {stouputils-1.0.21 → stouputils-1.0.22}/.github/workflows/documentation.yml +2 -1
  2. {stouputils-1.0.21 → stouputils-1.0.22}/PKG-INFO +1 -1
  3. stouputils-1.0.22/assets/all_doctests_module.gif +0 -0
  4. stouputils-1.0.22/assets/archive_module.gif +0 -0
  5. stouputils-1.0.22/assets/backup_module.gif +0 -0
  6. stouputils-1.0.22/assets/collections_module.gif +0 -0
  7. stouputils-1.0.22/assets/ctx_module.gif +0 -0
  8. stouputils-1.0.22/assets/decorators_module_1.gif +0 -0
  9. stouputils-1.0.22/assets/decorators_module_2.gif +0 -0
  10. stouputils-1.0.22/assets/io_module.gif +0 -0
  11. stouputils-1.0.22/assets/parallel_module.gif +0 -0
  12. stouputils-1.0.22/assets/print_module.gif +0 -0
  13. stouputils-1.0.22/examples/all_doctests.py +17 -0
  14. stouputils-1.0.22/examples/archive/corrupted.zip +0 -0
  15. stouputils-1.0.22/examples/archive.py +24 -0
  16. stouputils-1.0.22/examples/collections.py +28 -0
  17. stouputils-1.0.22/examples/ctx.py +25 -0
  18. stouputils-1.0.22/examples/decorators_1.py +29 -0
  19. stouputils-1.0.22/examples/decorators_2.py +21 -0
  20. stouputils-1.0.22/examples/delta_backup.py +7 -0
  21. stouputils-1.0.22/examples/io.py +33 -0
  22. stouputils-1.0.22/examples/parallel.py +28 -0
  23. stouputils-1.0.22/examples/print.py +27 -0
  24. {stouputils-1.0.21 → stouputils-1.0.22}/pyproject.toml +1 -1
  25. {stouputils-1.0.21 → stouputils-1.0.22}/scripts/create_docs.py +57 -16
  26. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/all_doctests.py +8 -5
  27. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/archive.py +14 -4
  28. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/backup.py +3 -0
  29. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/collections.py +3 -0
  30. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/continuous_delivery/cd_utils.py +1 -1
  31. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/continuous_delivery/github.py +11 -4
  32. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/ctx.py +10 -5
  33. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/decorators.py +25 -52
  34. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/dont_look/zip_file_override.py +1 -8
  35. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/io.py +4 -2
  36. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/parallel.py +10 -7
  37. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/print.py +9 -18
  38. stouputils-1.0.21/doctests.py +0 -17
  39. stouputils-1.0.21/examples/delta_backup.py +0 -7
  40. {stouputils-1.0.21 → stouputils-1.0.22}/.gitignore +0 -0
  41. {stouputils-1.0.21 → stouputils-1.0.22}/.python-version +0 -0
  42. {stouputils-1.0.21 → stouputils-1.0.22}/1_upgrades.py +0 -0
  43. {stouputils-1.0.21 → stouputils-1.0.22}/2_build.py +0 -0
  44. {stouputils-1.0.21 → stouputils-1.0.22}/3_upload.py +0 -0
  45. {stouputils-1.0.21 → stouputils-1.0.22}/LICENSE +0 -0
  46. {stouputils-1.0.21 → stouputils-1.0.22}/README.md +0 -0
  47. {stouputils-1.0.21 → stouputils-1.0.22}/all_in_one.py +0 -0
  48. {stouputils-1.0.21 → stouputils-1.0.22}/build_all_in_one.py +0 -0
  49. {stouputils-1.0.21 → stouputils-1.0.22}/copy_in_local.py +0 -0
  50. {stouputils-1.0.21 → stouputils-1.0.22}/github_release.py +0 -0
  51. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/__init__.py +0 -0
  52. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/continuous_delivery/__init__.py +0 -0
  53. {stouputils-1.0.21 → stouputils-1.0.22}/src/stouputils/py.typed +0 -0
  54. {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.21
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
@@ -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
+
@@ -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,7 @@
1
+
2
+ # Example of use:
3
+ # py examples/delta_backup.py delta "src" "backup" -x "*pycache*"
4
+
5
+ import stouputils as stp
6
+ stp.backup_cli()
7
+
@@ -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
+
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
5
5
 
6
6
  [project]
7
7
  name = "stouputils"
8
- version = "1.0.21"
8
+ version = "1.0.22"
9
9
  authors = [
10
10
  { name="Stoupy51", email="stoupy51@gmail.com" },
11
11
  ]
@@ -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 = 'sphinx_rtd_theme'
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
- 'style_external_links': True,
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
- # Add versions from html_context
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, force_raise_exception
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
- from .io import *
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
- """ Make an archive with consistency using FILES_TO_WRITE variable
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
@@ -3,7 +3,7 @@ It is mainly used by the `stouputils.continuous_delivery.github` module.
3
3
  """
4
4
 
5
5
  # Imports
6
- from ..print import *
6
+ from ..print import warning
7
7
  from ..decorators import handle_error
8
8
  from ..io import clean_path, super_json_load
9
9
  import requests
@@ -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 .cd_utils import *
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(f"Deleted existing tag")
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
- >>> with LogToFile("output.log"):
57
- ... from stouputils.print import info
58
- ... info("This will be logged to output.log and printed normally")
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
- >>> @measure_time(info)
86
- ... def test():
87
- ... pass
88
- >>> test() # [INFO HH:MM:SS] Execution time of test: 0.000ms (0ns)
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
- >>> @handle_error(error_log=LogLevels.WARNING)
169
- ... def test():
170
- ... raise ValueError("Let's fail")
171
- >>> test() # [WARNING HH:MM:SS] Error during test: (ValueError) Let's fail
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
- >>> @deprecated(message="Use 'this_function()' instead", error_log=LogLevels.WARNING)
275
- ... def test():
276
- ... pass
277
- >>> test() # [WARNING HH:MM:SS] Function 'test()' is deprecated. Use 'this_function()' instead
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, _FH_FILENAME_LENGTH, _FH_EXTRA_FIELD_LENGTH, _FH_GENERAL_PURPOSE_FLAG_BITS, crc32 # type: ignore
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.5)
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, list(range(10)), desc="Processing", verbose=1)
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, list(range(10)), desc="Processing with delay", max_workers=2, delay_first_calls=1.2, verbose=1)
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, list(range(10)), desc="Threading", verbose=1)
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, list(range(10)), desc="Threading with delay", max_workers=2, delay_first_calls=1.2, verbose=1)
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
- - info()
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://i.imgur.com/EIeiLwa.gif
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] = sys.stdout, **print_kwargs: Any) -> None:
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
- return f"{type(value)}, <id {id(value)}>:\t{length}{value_str}"
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:
@@ -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
-
@@ -1,7 +0,0 @@
1
-
2
- # Example of use:
3
- # py .\delta_backup.py delta "src" "backup" -x "*pycache*"
4
-
5
- import stouputils as stp
6
- stp.backup_cli()
7
-
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