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.
Files changed (25) hide show
  1. {stouputils-1.2.19 → stouputils-1.2.21}/PKG-INFO +1 -1
  2. {stouputils-1.2.19 → stouputils-1.2.21}/pyproject.toml +1 -1
  3. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/ctx.py +9 -52
  4. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/parallel.py +5 -5
  5. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/print.py +73 -1
  6. {stouputils-1.2.19 → stouputils-1.2.21}/.gitignore +0 -0
  7. {stouputils-1.2.19 → stouputils-1.2.21}/LICENSE +0 -0
  8. {stouputils-1.2.19 → stouputils-1.2.21}/README.md +0 -0
  9. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/__init__.py +0 -0
  10. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/all_doctests.py +0 -0
  11. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/applications/__init__.py +0 -0
  12. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/applications/automatic_docs.py +0 -0
  13. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/archive.py +0 -0
  14. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/backup.py +0 -0
  15. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/collections.py +0 -0
  16. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/__init__.py +0 -0
  17. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/cd_utils.py +0 -0
  18. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/github.py +0 -0
  19. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/pypi.py +0 -0
  20. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/continuous_delivery/pyproject.py +0 -0
  21. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/decorators.py +0 -0
  22. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/dont_look/zip_file_override.py +0 -0
  23. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/image.py +0 -0
  24. {stouputils-1.2.19 → stouputils-1.2.21}/stouputils/io.py +0 -0
  25. {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.19
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.19"
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 calls to the print functions in stouputils.print
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 remove_colors
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