progress-table 1.3.0__tar.gz → 1.3.2__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 (21) hide show
  1. {progress-table-1.3.0 → progress-table-1.3.2}/PKG-INFO +1 -1
  2. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/__init__.py +1 -1
  3. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/v0/progress_table.py +8 -0
  4. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/v1/progress_table.py +33 -15
  5. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table.egg-info/PKG-INFO +1 -1
  6. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table.egg-info/SOURCES.txt +3 -1
  7. progress-table-1.3.2/tests/test_docs_automated.py +61 -0
  8. progress-table-1.3.2/tests/test_examples_automated.py +63 -0
  9. {progress-table-1.3.0 → progress-table-1.3.2}/LICENSE.txt +0 -0
  10. {progress-table-1.3.0 → progress-table-1.3.2}/README.md +0 -0
  11. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/v0/__init__.py +0 -0
  12. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/v0/symbols.py +0 -0
  13. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/v1/__init__.py +0 -0
  14. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table/v1/styles.py +0 -0
  15. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table.egg-info/PKG-INFO.sync-conflict-20240314-015933-NXTV2IO +0 -0
  16. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table.egg-info/dependency_links.txt +0 -0
  17. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table.egg-info/requires.txt +0 -0
  18. {progress-table-1.3.0 → progress-table-1.3.2}/progress_table.egg-info/top_level.txt +0 -0
  19. {progress-table-1.3.0 → progress-table-1.3.2}/pyproject.toml +0 -0
  20. {progress-table-1.3.0 → progress-table-1.3.2}/setup.cfg +0 -0
  21. {progress-table-1.3.0 → progress-table-1.3.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: progress-table
3
- Version: 1.3.0
3
+ Version: 1.3.2
4
4
  Summary: Display progress as a pretty table in the command line.
5
5
  Home-page: https://github.com/gahaalt/progress-table.git
6
6
  Author: Szymon Mikler
@@ -1,6 +1,6 @@
1
1
  # Copyright (c) 2022-2024 Szymon Mikler
2
2
 
3
- __version__ = "1.3.0"
3
+ __version__ = "1.3.2"
4
4
 
5
5
  from progress_table.v0.progress_table import ProgressTableV0
6
6
  from progress_table.v1.progress_table import ProgressTableV1
@@ -27,6 +27,8 @@ ALL_COLORS_STYLES = ALL_COLORS + ALL_STYLES
27
27
  ITERATOR_LENGTH_UNKNOWN_WARNED_ONCE = False
28
28
  ITERATOR_LENGTH_CACHE: Dict[int, int] = {}
29
29
 
30
+ DEPRECATION_PRINTED = False
31
+
30
32
 
31
33
  class ProgressTableV0:
32
34
  def __init__(
@@ -82,6 +84,12 @@ class ProgressTableV0:
82
84
  file: Redirect the output to another stream. There can be multiple streams at once passed as
83
85
  a list or a tuple. Defaults to sys.stdout.
84
86
  """
87
+ global DEPRECATION_PRINTED
88
+
89
+ if not DEPRECATION_PRINTED:
90
+ logging.warning("ProgressTableV0 is deprecated, use ProgressTableV1 instead")
91
+ DEPRECATION_PRINTED = True
92
+
85
93
  self.refresh_rate = refresh_rate
86
94
  self.default_width = default_column_width
87
95
  self.default_alignment = default_column_alignment
@@ -219,7 +219,7 @@ class ProgressTableV1:
219
219
  self.finished_rows: list[dict[str, Any]] = []
220
220
 
221
221
  self._new_row: dict[str, Any] = {}
222
- self.new_row_cumulated_weight: dict[str, int] = {}
222
+ self._new_row_cumulated_weight: dict[str, int] = {}
223
223
  self.files = (file,) if not isinstance(file, (list, tuple)) else file
224
224
 
225
225
  self.previous_header_counter = 0
@@ -288,27 +288,35 @@ class ProgressTableV1:
288
288
  # Initialize color for the column in the new row
289
289
  self._prepare_row_color_dict()
290
290
 
291
- def add_columns(self, iterable, **kwds):
291
+ def add_columns(self, *columns, **kwds):
292
292
  """Add multiple columns to the table."""
293
- for column in iterable:
293
+ for column in columns:
294
294
  self.add_column(column, **kwds)
295
295
 
296
- def update(self, key, value, *, weight=1):
297
- """Update value in the current row."""
296
+ def update(self, key, value, *, weight=1, **column_kwds):
297
+ """Update value in the current row. This is extends capabilities of __setitem__.
298
+
299
+ Args:
300
+ key: Name of the column.
301
+ value: Value to be set.
302
+ weight: Weight of the value. This is used for aggregation.
303
+ column_kwds: Additional arguments for the column. They will be only used for column creation.
304
+ If column already exists, they will have no effect.
305
+ """
298
306
  if key not in self.column_names:
299
307
  if self._is_table_opened:
300
308
  logging.info("Closing table (new column added to opened table)")
301
- self.close(close_pbars=False)
309
+ self.close(close_pbars=False, close_row=False)
302
310
 
303
- self.add_column(key)
311
+ self.add_column(key, **column_kwds)
304
312
 
305
313
  # Set default values for new rows
306
314
  self._new_row.setdefault(key, 0)
307
- self.new_row_cumulated_weight.setdefault(key, 0)
315
+ self._new_row_cumulated_weight.setdefault(key, 0)
308
316
 
309
317
  fn = self.column_aggregates[key]
310
- self._new_row[key] = fn(value, self._new_row[key], self.new_row_cumulated_weight[key])
311
- self.new_row_cumulated_weight[key] += weight
318
+ self._new_row[key] = fn(value, self._new_row[key], self._new_row_cumulated_weight[key])
319
+ self._new_row_cumulated_weight[key] += weight
312
320
 
313
321
  t0 = time.time()
314
322
  td = t0 - self._last_time_row_printed
@@ -335,7 +343,7 @@ class ProgressTableV1:
335
343
  return
336
344
 
337
345
  logging.info("Closing table (reordering columns)")
338
- self.close(close_pbars=False)
346
+ self.close(close_pbars=False, close_row=False)
339
347
 
340
348
  assert isinstance(column_names, (List, Tuple))
341
349
  assert all([x in self.column_names for x in column_names]), f"Columns {column_names} not in {self.column_names}"
@@ -444,7 +452,8 @@ class ProgressTableV1:
444
452
 
445
453
  def _display_new_row_or_pbar(self):
446
454
  if self._active_pbars.get(0, None) is not None:
447
- self._active_pbars[0].display() # Level 0 pbar is embedded into the row, if it exists we want to display it instead of a row
455
+ # Level 0 pbar is embedded into the row, if it exists we want to display it instead of a row
456
+ self._active_pbars[0].display()
448
457
  else:
449
458
  self._display_new_row()
450
459
 
@@ -466,6 +475,9 @@ class ProgressTableV1:
466
475
  if split or self._request_splitter:
467
476
  self._print_splitter()
468
477
 
478
+ # Reset aggregated values!
479
+ self._new_row_cumulated_weight = {}
480
+
469
481
  self._prepare_row_color_dict(color)
470
482
  self._display_new_row()
471
483
  self._print() # Insert a newline
@@ -548,6 +560,7 @@ class ProgressTableV1:
548
560
  level=None,
549
561
  total=None,
550
562
  refresh_rate=None,
563
+ description="",
551
564
  show_throughput=None,
552
565
  show_progress=None,
553
566
  ):
@@ -559,6 +572,7 @@ class ProgressTableV1:
559
572
  level: Level of the progress bar. If not provided, it will be set automatically.
560
573
  total: Total number of iterations. If not provided, it will be calculated from the length of the iterable.
561
574
  refresh_rate: The maximal number of times per second the progress bar will be refreshed.
575
+ description: Custom description of the progress bar that will be shown as prefix.
562
576
  show_throughput: If True, the throughput will be displayed.
563
577
  show_progress: If True, the progress will be displayed.
564
578
  """
@@ -574,6 +588,7 @@ class ProgressTableV1:
574
588
  total=total,
575
589
  level=level,
576
590
  refresh_rate=refresh_rate if refresh_rate is not None else self.refresh_rate,
591
+ description=description,
577
592
  show_throughput=show_throughput if show_throughput is not None else self.pbar_show_throughput,
578
593
  show_progress=show_progress if show_progress is not None else self.pbar_show_progress,
579
594
  )
@@ -589,7 +604,7 @@ class ProgressTableV1:
589
604
 
590
605
 
591
606
  class TableProgressBar:
592
- def __init__(self, iterable, *, table, total, level, refresh_rate, show_throughput, show_progress):
607
+ def __init__(self, iterable, *, table, total, level, refresh_rate, description, show_throughput, show_progress):
593
608
  self.iterable: Iterable | None = iterable
594
609
 
595
610
  self._step: int = 0
@@ -600,6 +615,7 @@ class TableProgressBar:
600
615
  self.level: int = level
601
616
  self.table: ProgressTableV1 = table
602
617
  self.refresh_rate: int = refresh_rate
618
+ self.description: str = description
603
619
  self.show_progress: bool = show_progress
604
620
  self.show_throughput: bool = show_throughput
605
621
  self._is_active: bool = True
@@ -618,13 +634,15 @@ class TableProgressBar:
618
634
  throughput = self._step / time_passed if time_passed > 0 else 0.0
619
635
 
620
636
  inside_infobar = []
621
- if self.show_throughput:
622
- inside_infobar.append(f"{throughput: <.2f} it/s")
637
+ if self.description:
638
+ inside_infobar.append(self.description)
623
639
  if self.show_progress:
624
640
  if self._total:
625
641
  inside_infobar.append(f"{self._step}/{self._total}")
626
642
  else:
627
643
  inside_infobar.append(f"{self._step}")
644
+ if self.show_throughput:
645
+ inside_infobar.append(f"{throughput: <.2f} it/s")
628
646
  infobar = "[" + ", ".join(inside_infobar) + "] " if inside_infobar else ""
629
647
 
630
648
  pbar = ["\n" * self.level]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: progress-table
3
- Version: 1.3.0
3
+ Version: 1.3.2
4
4
  Summary: Display progress as a pretty table in the command line.
5
5
  Home-page: https://github.com/gahaalt/progress-table.git
6
6
  Author: Szymon Mikler
@@ -15,4 +15,6 @@ progress_table/v0/progress_table.py
15
15
  progress_table/v0/symbols.py
16
16
  progress_table/v1/__init__.py
17
17
  progress_table/v1/progress_table.py
18
- progress_table/v1/styles.py
18
+ progress_table/v1/styles.py
19
+ tests/test_docs_automated.py
20
+ tests/test_examples_automated.py
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2022-2024 Szymon Mikler
2
+
3
+ import logging
4
+ import os
5
+ import pathlib
6
+ import re
7
+
8
+
9
+ def get_header(msg):
10
+ msg_len = len(msg)
11
+ header = [
12
+ "#" * (msg_len + 6),
13
+ "## " + msg + " ##",
14
+ "#" * (msg_len + 6),
15
+ ]
16
+ return "\n".join(header) + "\n"
17
+
18
+
19
+ def scan_for_code_blobs(text):
20
+ # Capture code blocks starting with py or python
21
+ blobs = re.findall(r"```(py|python)\n([\s\S]+?)\n```", text)
22
+
23
+ new_blobs = []
24
+ for mode, blob in blobs:
25
+ if "..." in blob:
26
+ continue
27
+ header = get_header(f"Generated ({mode})")
28
+ blob = header + blob
29
+
30
+ # For `py` code blocks, we append them to previous existing block
31
+ # But `python` block starts a new scope and finishes the previous block
32
+ # They usually need to include imports
33
+ if mode == "py" and new_blobs:
34
+ new_blobs[-1] = new_blobs[-1] + "\n" + blob
35
+ else:
36
+ new_blobs.append(blob)
37
+ return new_blobs
38
+
39
+
40
+ def test_all_code_blobs():
41
+ # Testing whether code blobs from the documentation run without errors
42
+ all_code_blobs = []
43
+
44
+ for root, dirs, files in os.walk(""):
45
+ for file in files:
46
+ path = pathlib.Path(os.path.join(root, file))
47
+ if path.suffix == ".md":
48
+ code_blobs = scan_for_code_blobs(path.open("r").read())
49
+ for blob in code_blobs:
50
+ all_code_blobs.append(blob)
51
+
52
+ logging.warning(f"Detected {len(all_code_blobs)} code examples!")
53
+
54
+ for idx, blob in enumerate(all_code_blobs):
55
+ try:
56
+ globals_temp = {}
57
+ exec(blob, globals_temp)
58
+ except Exception as e:
59
+ print(f"Exception during automated documentation testing {idx}/{len(all_code_blobs)}:")
60
+ print(blob)
61
+ raise e
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2022-2024 Szymon Mikler
2
+
3
+ import hashlib
4
+ import importlib
5
+ import random
6
+ import sys
7
+ from glob import glob
8
+ from io import StringIO
9
+
10
+ EXPECTED_OUTPUTS = {
11
+ "examples.nn_training": "7e50cfd539e3e1799ecd5a7e7b7a7e18",
12
+ "examples.cumulating": "b5b7bc9b8232545ebfd5ca777bddacb4",
13
+ "examples.fibonacci": "bae5411af4bf8a4326f1bce59ca9aad9",
14
+ "examples.features": "474d4aff94a3070d2300d78d4159e0a6",
15
+ }
16
+
17
+
18
+ def set_seed():
19
+ random.seed(42)
20
+
21
+
22
+ def capture_example_stdout(main_fn):
23
+ # To eliminate run to run variation of the example outputs we need to be independent from the execution speed
24
+ # This includes removing the influence of:
25
+ # * refresh rate
26
+ # * throughput display
27
+ override_kwds = dict(
28
+ refresh_rate=1000000000,
29
+ pbar_show_throughput=False,
30
+ )
31
+
32
+ # We will replace stdout with custom StringIO and check whether example stdout is as expected
33
+ out_buffer = StringIO()
34
+ sys.stdout = out_buffer
35
+ set_seed()
36
+ main_fn(**override_kwds)
37
+ sys.stdout = sys.__stdout__
38
+ return out_buffer.getvalue()
39
+
40
+
41
+ def test_all_examples():
42
+ # Testing whether examples run exactly as intended
43
+
44
+ outputs = {}
45
+ for module in glob("examples/*"):
46
+ module = module.replace(".py", "").replace("/", ".")
47
+ print(f"Running example: {module}")
48
+ main_fn = importlib.import_module(module).main
49
+ out_str = capture_example_stdout(main_fn)
50
+
51
+ md5hash = hashlib.md5(out_str.encode()).hexdigest()
52
+ outputs[module] = md5hash
53
+
54
+ err_msg = []
55
+ for key in EXPECTED_OUTPUTS:
56
+ output = outputs.get(key, None)
57
+ expected = EXPECTED_OUTPUTS[key]
58
+ if output != expected:
59
+ err_msg.append(f" {output} instead of {expected} in {key}")
60
+ err_msg = "\n".join(err_msg)
61
+
62
+ if err_msg:
63
+ assert False, f"Errors in example outputs\n{err_msg}"
File without changes
File without changes
File without changes