stouputils 1.2.19__tar.gz → 1.2.21__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.2.19 → stouputils-1.2.21}/PKG-INFO +1 -1
- {stouputils-1.2.19 → stouputils-1.2.21}/pyproject.toml +1 -1
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/ctx.py +9 -52
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/parallel.py +5 -5
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/print.py +73 -1
- {stouputils-1.2.19 → stouputils-1.2.21}/.gitignore +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/LICENSE +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/README.md +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/__init__.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/all_doctests.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/applications/__init__.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/applications/automatic_docs.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/archive.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/backup.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/collections.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/__init__.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/cd_utils.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/github.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/pypi.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/pyproject.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/decorators.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/dont_look/zip_file_override.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/image.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/io.py +0 -0
- {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stouputils
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.21
|
|
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
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "stouputils"
|
|
8
|
-
version = "1.2.
|
|
8
|
+
version = "1.2.21"
|
|
9
9
|
description = "Stouputils is a collection of utility modules designed to simplify and enhance the development process. It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers, and many more."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
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
|
-
- LogToFile: Context manager to log to a file every
|
|
5
|
+
- LogToFile: Context manager to log to a file every print call (with LINE_UP handling)
|
|
6
6
|
|
|
7
7
|
.. image:: https://raw.githubusercontent.com/Stoupy51/stouputils/refs/heads/main/assets/ctx_module.gif
|
|
8
8
|
:alt: stouputils ctx examples
|
|
@@ -12,7 +12,7 @@ This module provides context managers for temporarily silencing output.
|
|
|
12
12
|
import os
|
|
13
13
|
import sys
|
|
14
14
|
from typing import IO, TextIO, Callable, Any
|
|
15
|
-
from .print import
|
|
15
|
+
from .print import TeeMultiOutput
|
|
16
16
|
from .io import super_open
|
|
17
17
|
|
|
18
18
|
|
|
@@ -55,53 +55,6 @@ class Muffle:
|
|
|
55
55
|
sys.stderr = self.original_stderr
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
# TeeMultiOutput class to duplicate output to multiple file-like objects
|
|
59
|
-
class TeeMultiOutput(object):
|
|
60
|
-
""" File-like object that duplicates output to multiple file objects.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
*files (IO[Any]): One or more file-like objects that have write and flush methods
|
|
64
|
-
strip_colors (bool): Whether to strip ANSI color codes from output sent to non-stdout/stderr files
|
|
65
|
-
|
|
66
|
-
Examples:
|
|
67
|
-
>>> f = open("logfile.txt", "w")
|
|
68
|
-
>>> original_stdout = sys.stdout
|
|
69
|
-
>>> sys.stdout = TeeMultiOutput(sys.stdout, f)
|
|
70
|
-
>>> print("Hello World") # Output goes to both console and file
|
|
71
|
-
>>> sys.stdout = original_stdout
|
|
72
|
-
>>> f.close()
|
|
73
|
-
"""
|
|
74
|
-
def __init__(self, *files: IO[Any], strip_colors: bool = True) -> None:
|
|
75
|
-
self.files: tuple[IO[Any], ...] = files
|
|
76
|
-
""" File-like objects to write to """
|
|
77
|
-
self.strip_colors: bool = strip_colors
|
|
78
|
-
""" Whether to strip ANSI color codes from output sent to non-stdout/stderr files """
|
|
79
|
-
|
|
80
|
-
def write(self, obj: str) -> None:
|
|
81
|
-
""" Write the object to all files while stripping colors if needed.
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
obj (str): String to write
|
|
85
|
-
"""
|
|
86
|
-
for i, f in enumerate(self.files):
|
|
87
|
-
if self.strip_colors and i != 0:
|
|
88
|
-
f.write(remove_colors(obj))
|
|
89
|
-
else:
|
|
90
|
-
f.write(obj)
|
|
91
|
-
|
|
92
|
-
def flush(self) -> None:
|
|
93
|
-
""" Flush all files. """
|
|
94
|
-
for f in self.files:
|
|
95
|
-
f.flush()
|
|
96
|
-
|
|
97
|
-
# Add other methods that might be expected from a file-like object
|
|
98
|
-
def isatty(self) -> bool:
|
|
99
|
-
""" Return whether the first file is connected to a tty-like device. """
|
|
100
|
-
return hasattr(self.files[0], 'isatty') and self.files[0].isatty()
|
|
101
|
-
|
|
102
|
-
def fileno(self) -> int:
|
|
103
|
-
""" Return the file descriptor of the first file. """
|
|
104
|
-
return self.files[0].fileno()
|
|
105
58
|
|
|
106
59
|
# Context manager to log to a file
|
|
107
60
|
class LogToFile:
|
|
@@ -116,6 +69,7 @@ class LogToFile:
|
|
|
116
69
|
encoding (str): Encoding to use for the file (default: "utf-8")
|
|
117
70
|
tee_stdout (bool): Whether to redirect stdout to the file (default: True)
|
|
118
71
|
tee_stderr (bool): Whether to redirect stderr to the file (default: True)
|
|
72
|
+
ignore_lineup (bool): Whether to ignore lines containing LINE_UP escape sequence in files (default: False)
|
|
119
73
|
|
|
120
74
|
Examples:
|
|
121
75
|
.. code-block:: python
|
|
@@ -131,7 +85,8 @@ class LogToFile:
|
|
|
131
85
|
mode: str = "w",
|
|
132
86
|
encoding: str = "utf-8",
|
|
133
87
|
tee_stdout: bool = True,
|
|
134
|
-
tee_stderr: bool = True
|
|
88
|
+
tee_stderr: bool = True,
|
|
89
|
+
ignore_lineup: bool = True
|
|
135
90
|
) -> None:
|
|
136
91
|
self.path: str = path
|
|
137
92
|
""" Attribute remembering path to the log file """
|
|
@@ -143,6 +98,8 @@ class LogToFile:
|
|
|
143
98
|
""" Whether to redirect stdout to the file """
|
|
144
99
|
self.tee_stderr: bool = tee_stderr
|
|
145
100
|
""" Whether to redirect stderr to the file """
|
|
101
|
+
self.ignore_lineup: bool = ignore_lineup
|
|
102
|
+
""" Whether to ignore lines containing LINE_UP escape sequence in files """
|
|
146
103
|
self.file: IO[Any] = super_open(self.path, mode=self.mode, encoding=self.encoding)
|
|
147
104
|
""" Attribute remembering opened file """
|
|
148
105
|
self.original_stdout: TextIO = sys.stdout
|
|
@@ -154,10 +111,10 @@ class LogToFile:
|
|
|
154
111
|
""" Enter context manager which opens the log file and redirects stdout/stderr """
|
|
155
112
|
# Redirect stdout and stderr if requested
|
|
156
113
|
if self.tee_stdout:
|
|
157
|
-
sys.stdout = TeeMultiOutput(self.original_stdout, self.file)
|
|
114
|
+
sys.stdout = TeeMultiOutput(self.original_stdout, self.file, ignore_lineup=self.ignore_lineup)
|
|
158
115
|
|
|
159
116
|
if self.tee_stderr:
|
|
160
|
-
sys.stderr = TeeMultiOutput(self.original_stderr, self.file)
|
|
117
|
+
sys.stderr = TeeMultiOutput(self.original_stderr, self.file, ignore_lineup=self.ignore_lineup)
|
|
161
118
|
|
|
162
119
|
def __exit__(self, exc_type: type[BaseException]|None, exc_val: BaseException|None, exc_tb: Any|None) -> None:
|
|
163
120
|
""" Exit context manager which closes the log file and restores stdout/stderr """
|
|
@@ -15,7 +15,7 @@ from .print import MAGENTA, RESET
|
|
|
15
15
|
from .decorators import handle_error, LogLevels
|
|
16
16
|
from multiprocessing import Pool, cpu_count
|
|
17
17
|
from typing import Callable, TypeVar
|
|
18
|
-
from tqdm import tqdm
|
|
18
|
+
from tqdm.auto import tqdm
|
|
19
19
|
from tqdm.contrib.concurrent import process_map # type: ignore
|
|
20
20
|
from concurrent.futures import ThreadPoolExecutor
|
|
21
21
|
import time
|
|
@@ -123,7 +123,7 @@ def multiprocessing(func: Callable[[T], R], args: list[T], use_starmap: bool = F
|
|
|
123
123
|
# Do multiprocessing only if there is more than 1 argument and more than 1 CPU
|
|
124
124
|
if max_workers > 1 and len(args) > 1:
|
|
125
125
|
if verbose > 0:
|
|
126
|
-
return list(process_map(func, args, max_workers=max_workers, chunksize=chunksize, desc=desc, bar_format=BAR_FORMAT)) # type: ignore
|
|
126
|
+
return list(process_map(func, args, max_workers=max_workers, chunksize=chunksize, desc=desc, bar_format=BAR_FORMAT, ascii=False)) # type: ignore
|
|
127
127
|
else:
|
|
128
128
|
with Pool(max_workers) as pool:
|
|
129
129
|
return list(pool.map(func, args, chunksize=chunksize)) # type: ignore
|
|
@@ -131,7 +131,7 @@ def multiprocessing(func: Callable[[T], R], args: list[T], use_starmap: bool = F
|
|
|
131
131
|
# Single process execution
|
|
132
132
|
else:
|
|
133
133
|
if verbose > 0:
|
|
134
|
-
return [func(arg) for arg in tqdm(args, total=len(args), desc=desc, bar_format=BAR_FORMAT)]
|
|
134
|
+
return [func(arg) for arg in tqdm(args, total=len(args), desc=desc, bar_format=BAR_FORMAT, ascii=False)]
|
|
135
135
|
else:
|
|
136
136
|
return [func(arg) for arg in args]
|
|
137
137
|
|
|
@@ -176,7 +176,7 @@ def multithreading(func: Callable[[T], R], args: list[T], use_starmap: bool = Fa
|
|
|
176
176
|
if max_workers > 1 and len(args) > 1:
|
|
177
177
|
if verbose > 0:
|
|
178
178
|
with ThreadPoolExecutor(max_workers) as executor:
|
|
179
|
-
return list(tqdm(executor.map(func, args), total=len(args), desc=desc, bar_format=BAR_FORMAT))
|
|
179
|
+
return list(tqdm(executor.map(func, args), total=len(args), desc=desc, bar_format=BAR_FORMAT, ascii=False))
|
|
180
180
|
else:
|
|
181
181
|
with ThreadPoolExecutor(max_workers) as executor:
|
|
182
182
|
return list(executor.map(func, args))
|
|
@@ -184,7 +184,7 @@ def multithreading(func: Callable[[T], R], args: list[T], use_starmap: bool = Fa
|
|
|
184
184
|
# Single process execution
|
|
185
185
|
else:
|
|
186
186
|
if verbose > 0:
|
|
187
|
-
return [func(arg) for arg in tqdm(args, total=len(args), desc=desc, bar_format=BAR_FORMAT)]
|
|
187
|
+
return [func(arg) for arg in tqdm(args, total=len(args), desc=desc, bar_format=BAR_FORMAT, ascii=False)]
|
|
188
188
|
else:
|
|
189
189
|
return [func(arg) for arg in args]
|
|
190
190
|
|
|
@@ -10,7 +10,7 @@ If a message is printed multiple times, it will be displayed as "(xN) message" w
|
|
|
10
10
|
# Imports
|
|
11
11
|
import sys
|
|
12
12
|
import time
|
|
13
|
-
from typing import Callable, TextIO, Any
|
|
13
|
+
from typing import Callable, IO, TextIO, Any
|
|
14
14
|
|
|
15
15
|
# Colors constants
|
|
16
16
|
RESET: str = "\033[0m"
|
|
@@ -224,7 +224,79 @@ def breakpoint(*values: Any, print_function: Callable[..., None] = warning, **pr
|
|
|
224
224
|
sys.exit(1)
|
|
225
225
|
|
|
226
226
|
|
|
227
|
+
# TeeMultiOutput class to duplicate output to multiple file-like objects
|
|
228
|
+
class TeeMultiOutput(object):
|
|
229
|
+
""" File-like object that duplicates output to multiple file-like objects.
|
|
227
230
|
|
|
231
|
+
Args:
|
|
232
|
+
*files (IO[Any]): One or more file-like objects that have write and flush methods
|
|
233
|
+
strip_colors (bool): Whether to strip ANSI color codes from output sent to non-stdout/stderr files
|
|
234
|
+
ascii_only (bool): Whether to replace non-ASCII characters with their ASCII equivalents for non-stdout/stderr files
|
|
235
|
+
ignore_lineup (bool): Whether to ignore lines containing LINE_UP escape sequence in non-terminal outputs
|
|
236
|
+
|
|
237
|
+
Examples:
|
|
238
|
+
>>> f = open("logfile.txt", "w")
|
|
239
|
+
>>> original_stdout = sys.stdout
|
|
240
|
+
>>> sys.stdout = TeeMultiOutput(sys.stdout, f)
|
|
241
|
+
>>> print("Hello World") # Output goes to both console and file
|
|
242
|
+
>>> sys.stdout = original_stdout
|
|
243
|
+
>>> f.close()
|
|
244
|
+
"""
|
|
245
|
+
def __init__(self, *files: IO[Any], strip_colors: bool = True, ascii_only: bool = True, ignore_lineup: bool = True) -> None:
|
|
246
|
+
self.files: tuple[IO[Any], ...] = files
|
|
247
|
+
""" File-like objects to write to """
|
|
248
|
+
self.strip_colors: bool = strip_colors
|
|
249
|
+
""" Whether to strip ANSI color codes from output sent to non-stdout/stderr files """
|
|
250
|
+
self.ascii_only: bool = ascii_only
|
|
251
|
+
""" Whether to replace non-ASCII characters with their ASCII equivalents for non-stdout/stderr files """
|
|
252
|
+
self.ignore_lineup: bool = ignore_lineup
|
|
253
|
+
""" Whether to ignore lines containing LINE_UP escape sequence in non-terminal outputs """
|
|
254
|
+
|
|
255
|
+
def write(self, obj: str) -> None:
|
|
256
|
+
""" Write the object to all files while stripping colors if needed.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
obj (str): String to write
|
|
260
|
+
"""
|
|
261
|
+
for i, f in enumerate(self.files):
|
|
262
|
+
try:
|
|
263
|
+
content = obj
|
|
264
|
+
if i != 0:
|
|
265
|
+
# First file (i = 0) (often stdout/stderr) gets the original content
|
|
266
|
+
# Other files (i != 0) get processed content
|
|
267
|
+
|
|
268
|
+
# Skip content if it contains LINE_UP and ignore_lineup is True
|
|
269
|
+
if self.ignore_lineup and (LINE_UP in content or "\r" in content):
|
|
270
|
+
continue
|
|
271
|
+
|
|
272
|
+
# Strip colors if needed
|
|
273
|
+
if self.strip_colors:
|
|
274
|
+
content = remove_colors(content)
|
|
275
|
+
|
|
276
|
+
# Replace Unicode block characters with ASCII equivalents
|
|
277
|
+
# Replace other problematic Unicode characters as needed
|
|
278
|
+
if self.ascii_only:
|
|
279
|
+
content = content.replace('█', '#')
|
|
280
|
+
content = ''.join(c if ord(c) < 128 else '?' for c in content)
|
|
281
|
+
|
|
282
|
+
# Write content to file
|
|
283
|
+
f.write(content)
|
|
284
|
+
|
|
285
|
+
except Exception:
|
|
286
|
+
pass
|
|
287
|
+
|
|
288
|
+
def flush(self) -> None:
|
|
289
|
+
""" Flush all files. """
|
|
290
|
+
for f in self.files:
|
|
291
|
+
if hasattr(f, 'flush'):
|
|
292
|
+
try:
|
|
293
|
+
f.flush()
|
|
294
|
+
except Exception:
|
|
295
|
+
pass
|
|
296
|
+
|
|
297
|
+
def fileno(self) -> int:
|
|
298
|
+
""" Return the file descriptor of the first file. """
|
|
299
|
+
return self.files[0].fileno() if hasattr(self.files[0], "fileno") else 0
|
|
228
300
|
|
|
229
301
|
|
|
230
302
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|