progress-table 0.1.27__tar.gz → 1.0.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: progress-table
3
- Version: 0.1.27
3
+ Version: 1.0.1
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
@@ -31,6 +31,10 @@ Designed to monitor machine learning experiments, but can be used for anything.
31
31
  Allows you to quickly see what is going on.
32
32
  Increases readability and cuteness of your command line logging.
33
33
 
34
+ > Note: version 1.0 introduced new features and changes of default behaviour.
35
+ >
36
+ > The old version is still available as `progress_table.ProgressTableV0`.
37
+
34
38
  ## Features
35
39
 
36
40
  * Displaying pretty table in the terminal
@@ -60,22 +64,30 @@ import time
60
64
 
61
65
  from progress_table import ProgressTable
62
66
 
63
- # Define the columns at the beginning
64
- table = ProgressTable(
65
- columns=["step", "x", "x squared"],
67
+ # Create table object:
68
+ table = ProgressTable()
66
69
 
67
- # Default arguments:
70
+ # Or customize its settings:
71
+ table = ProgressTable(
72
+ columns=["step"],
68
73
  refresh_rate=10,
69
74
  num_decimal_places=4,
70
- default_column_width=8,
71
- default_column_alignment="center",
75
+ default_column_width=None,
76
+ default_column_color=None,
77
+ default_column_alignment=None,
78
+ default_column_aggregate=None,
79
+ default_row_color=None,
80
+ embedded_progress_bar=True,
81
+ pbar_show_throughput=True,
82
+ pbar_show_progress=False,
72
83
  print_row_on_update=True,
73
84
  reprint_header_every_n_rows=30,
74
85
  custom_format=None,
75
- embedded_progress_bar=False,
76
- table_style="normal",
86
+ table_style="round",
77
87
  file=sys.stdout,
78
88
  )
89
+
90
+ # You can define the columns at the beginning
79
91
  table.add_column("x", width=3)
80
92
  table.add_column("x root", color="red")
81
93
  table.add_column("random average", color=["bright", "red"], aggregate="mean")
@@ -83,10 +95,9 @@ table.add_column("random average", color=["bright", "red"], aggregate="mean")
83
95
  for step in range(10):
84
96
  x = random.randint(0, 200)
85
97
 
86
- # There are two equivalent ways to add new values
87
- # First:
88
- table["step"] = step
98
+ # There are two ways to add new values:
89
99
  table["x"] = x
100
+ table["step"] = step
90
101
  # Second:
91
102
  table.update("x root", x ** 0.5)
92
103
  table.update("x squared", x ** 2)
@@ -105,8 +116,8 @@ table.close()
105
116
 
106
117
  # Export your data
107
118
  data = table.to_list()
108
- pandas_df = table.to_df()
109
- np_array = table.to_numpy()
119
+ pandas_df = table.to_df() # Requires pandas to be installed
120
+ np_array = table.to_numpy() # Requires numpy to be installed
110
121
  ```
111
122
 
112
123
  ![example](https://github.com/gahaalt/progress-table/blob/main/images/example-output4.gif?raw=true)
@@ -11,6 +11,10 @@ Designed to monitor machine learning experiments, but can be used for anything.
11
11
  Allows you to quickly see what is going on.
12
12
  Increases readability and cuteness of your command line logging.
13
13
 
14
+ > Note: version 1.0 introduced new features and changes of default behaviour.
15
+ >
16
+ > The old version is still available as `progress_table.ProgressTableV0`.
17
+
14
18
  ## Features
15
19
 
16
20
  * Displaying pretty table in the terminal
@@ -40,22 +44,30 @@ import time
40
44
 
41
45
  from progress_table import ProgressTable
42
46
 
43
- # Define the columns at the beginning
44
- table = ProgressTable(
45
- columns=["step", "x", "x squared"],
47
+ # Create table object:
48
+ table = ProgressTable()
46
49
 
47
- # Default arguments:
50
+ # Or customize its settings:
51
+ table = ProgressTable(
52
+ columns=["step"],
48
53
  refresh_rate=10,
49
54
  num_decimal_places=4,
50
- default_column_width=8,
51
- default_column_alignment="center",
55
+ default_column_width=None,
56
+ default_column_color=None,
57
+ default_column_alignment=None,
58
+ default_column_aggregate=None,
59
+ default_row_color=None,
60
+ embedded_progress_bar=True,
61
+ pbar_show_throughput=True,
62
+ pbar_show_progress=False,
52
63
  print_row_on_update=True,
53
64
  reprint_header_every_n_rows=30,
54
65
  custom_format=None,
55
- embedded_progress_bar=False,
56
- table_style="normal",
66
+ table_style="round",
57
67
  file=sys.stdout,
58
68
  )
69
+
70
+ # You can define the columns at the beginning
59
71
  table.add_column("x", width=3)
60
72
  table.add_column("x root", color="red")
61
73
  table.add_column("random average", color=["bright", "red"], aggregate="mean")
@@ -63,10 +75,9 @@ table.add_column("random average", color=["bright", "red"], aggregate="mean")
63
75
  for step in range(10):
64
76
  x = random.randint(0, 200)
65
77
 
66
- # There are two equivalent ways to add new values
67
- # First:
68
- table["step"] = step
78
+ # There are two ways to add new values:
69
79
  table["x"] = x
80
+ table["step"] = step
70
81
  # Second:
71
82
  table.update("x root", x ** 0.5)
72
83
  table.update("x squared", x ** 2)
@@ -85,8 +96,8 @@ table.close()
85
96
 
86
97
  # Export your data
87
98
  data = table.to_list()
88
- pandas_df = table.to_df()
89
- np_array = table.to_numpy()
99
+ pandas_df = table.to_df() # Requires pandas to be installed
100
+ np_array = table.to_numpy() # Requires numpy to be installed
90
101
  ```
91
102
 
92
103
  ![example](https://github.com/gahaalt/progress-table/blob/main/images/example-output4.gif?raw=true)
@@ -0,0 +1,8 @@
1
+ # Copyright (c) 2022 Szymon Mikler
2
+
3
+ from progress_table.v0.progress_table import ProgressTableV0
4
+ from progress_table.v1.progress_table import ProgressTableV1
5
+
6
+ ProgressTable = ProgressTableV1
7
+
8
+ __all__ = ["ProgressTable", "ProgressTableV0", "ProgressTableV1"]
@@ -3,14 +3,13 @@ import shutil
3
3
  import sys
4
4
  import time
5
5
 
6
- from progress_table import ProgressTable
6
+ from progress_table import ProgressTableV1
7
7
 
8
8
  shutil.get_terminal_size()
9
9
 
10
10
  # Define the columns at the beginning
11
- table = ProgressTable(
11
+ table = ProgressTableV1(
12
12
  columns=["step", "x", "x squared"],
13
-
14
13
  # Default arguments:
15
14
  refresh_rate=10,
16
15
  num_decimal_places=4,
@@ -35,17 +34,17 @@ for step in range(100):
35
34
  table["step"] = step
36
35
  table["x"] = x
37
36
  # Second:
38
- table.update("x root", x ** 0.5)
39
- table.update("x squared", x ** 2)
37
+ table.update("x root", x**0.5)
38
+ table.update("x squared", x**2)
40
39
 
41
40
  # Display the progress bar by wrapping the iterator
42
41
  for _ in table(10):
43
42
  # You can use weights for aggregated values
44
43
  table.update("random average", random.random(), weight=1)
45
- time.sleep(1)
44
+ time.sleep(0.1)
46
45
 
47
46
  # Go to the next row when you're ready
48
- if step % 5 == 4:
47
+ if step % 5 == 0:
49
48
  split = True
50
49
  else:
51
50
  split = False
@@ -0,0 +1,60 @@
1
+ import random
2
+ import sys
3
+ import time
4
+
5
+ from progress_table import ProgressTableV1
6
+
7
+ # Create table object:
8
+ table = ProgressTableV1()
9
+
10
+ # Or customize its settings:
11
+ table = ProgressTableV1(
12
+ columns=["step"],
13
+ refresh_rate=10,
14
+ num_decimal_places=4,
15
+ default_column_width=None,
16
+ default_column_color=None,
17
+ default_column_alignment=None,
18
+ default_column_aggregate=None,
19
+ default_row_color=None,
20
+ embedded_progress_bar=True,
21
+ pbar_show_throughput=True,
22
+ pbar_show_progress=False,
23
+ print_row_on_update=True,
24
+ reprint_header_every_n_rows=30,
25
+ custom_format=None,
26
+ table_style="round",
27
+ file=sys.stdout,
28
+ )
29
+
30
+ # You can define the columns at the beginning
31
+ table.add_column("x", width=3)
32
+ table.add_column("x root", color="red")
33
+ table.add_column("random average", color=["bright", "red"], aggregate="mean")
34
+
35
+ for step in table(iter(range(100))):
36
+ x = random.randint(0, 200)
37
+
38
+ # There are two ways to add new values:
39
+ table["x"] = x
40
+ table["step"] = step
41
+ # Second:
42
+ table.update("x root", x**0.5)
43
+ table.update("x squared", x**2)
44
+
45
+ # Display the progress bar by wrapping the iterator
46
+ for _ in range(10): # -> Equivalent to `table(range(10))`
47
+ # You can use weights for aggregated values
48
+ table.update("random average", random.random(), weight=1)
49
+ time.sleep(0.02)
50
+
51
+ # Go to the next row when you're ready
52
+ table.next_row()
53
+
54
+ # Close the table when it's ready
55
+ table.close()
56
+
57
+ # Export your data
58
+ data = table.to_list()
59
+ pandas_df = table.to_df() # Requires pandas to be installed
60
+ np_array = table.to_numpy() # Requires numpy to be installed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: progress-table
3
- Version: 0.1.27
3
+ Version: 1.0.1
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
@@ -31,6 +31,10 @@ Designed to monitor machine learning experiments, but can be used for anything.
31
31
  Allows you to quickly see what is going on.
32
32
  Increases readability and cuteness of your command line logging.
33
33
 
34
+ > Note: version 1.0 introduced new features and changes of default behaviour.
35
+ >
36
+ > The old version is still available as `progress_table.ProgressTableV0`.
37
+
34
38
  ## Features
35
39
 
36
40
  * Displaying pretty table in the terminal
@@ -60,22 +64,30 @@ import time
60
64
 
61
65
  from progress_table import ProgressTable
62
66
 
63
- # Define the columns at the beginning
64
- table = ProgressTable(
65
- columns=["step", "x", "x squared"],
67
+ # Create table object:
68
+ table = ProgressTable()
66
69
 
67
- # Default arguments:
70
+ # Or customize its settings:
71
+ table = ProgressTable(
72
+ columns=["step"],
68
73
  refresh_rate=10,
69
74
  num_decimal_places=4,
70
- default_column_width=8,
71
- default_column_alignment="center",
75
+ default_column_width=None,
76
+ default_column_color=None,
77
+ default_column_alignment=None,
78
+ default_column_aggregate=None,
79
+ default_row_color=None,
80
+ embedded_progress_bar=True,
81
+ pbar_show_throughput=True,
82
+ pbar_show_progress=False,
72
83
  print_row_on_update=True,
73
84
  reprint_header_every_n_rows=30,
74
85
  custom_format=None,
75
- embedded_progress_bar=False,
76
- table_style="normal",
86
+ table_style="round",
77
87
  file=sys.stdout,
78
88
  )
89
+
90
+ # You can define the columns at the beginning
79
91
  table.add_column("x", width=3)
80
92
  table.add_column("x root", color="red")
81
93
  table.add_column("random average", color=["bright", "red"], aggregate="mean")
@@ -83,10 +95,9 @@ table.add_column("random average", color=["bright", "red"], aggregate="mean")
83
95
  for step in range(10):
84
96
  x = random.randint(0, 200)
85
97
 
86
- # There are two equivalent ways to add new values
87
- # First:
88
- table["step"] = step
98
+ # There are two ways to add new values:
89
99
  table["x"] = x
100
+ table["step"] = step
90
101
  # Second:
91
102
  table.update("x root", x ** 0.5)
92
103
  table.update("x squared", x ** 2)
@@ -105,8 +116,8 @@ table.close()
105
116
 
106
117
  # Export your data
107
118
  data = table.to_list()
108
- pandas_df = table.to_df()
109
- np_array = table.to_numpy()
119
+ pandas_df = table.to_df() # Requires pandas to be installed
120
+ np_array = table.to_numpy() # Requires numpy to be installed
110
121
  ```
111
122
 
112
123
  ![example](https://github.com/gahaalt/progress-table/blob/main/images/example-output4.gif?raw=true)
@@ -5,8 +5,7 @@ setup.cfg
5
5
  setup.py
6
6
  progress_table/__init__.py
7
7
  progress_table/devel.py
8
- progress_table/progress_table.py
9
- progress_table/symbols.py
8
+ progress_table/devel2.py
10
9
  progress_table/test_docs_automated.py
11
10
  progress_table.egg-info/PKG-INFO
12
11
  progress_table.egg-info/SOURCES.txt
@@ -3,11 +3,11 @@ pythonpath = ["."]
3
3
 
4
4
  [tool.mypy]
5
5
  ignore_missing_imports = true
6
- exclude = "devel"
6
+ exclude = ["devel", "build", "dist"]
7
7
 
8
8
  [tool.isort]
9
9
  profile = "black"
10
- line_length = 120
10
+ line_length = 140
11
11
 
12
12
  [tool.black]
13
- line_length = 120
13
+ line_length = 140
@@ -1,6 +1,6 @@
1
1
  [flake8]
2
- ignore = E203,W503
3
- max-line-length = 120
2
+ ignore = E203,W503,E266
3
+ max-line-length = 140
4
4
  exclude = devel
5
5
 
6
6
  [egg_info]
@@ -9,7 +9,7 @@ long_description = (this_directory / "README.md").read_text(encoding="UTF-8")
9
9
 
10
10
  setup(
11
11
  name="progress-table",
12
- version="0.1.27",
12
+ version="1.0.1",
13
13
  url="https://github.com/gahaalt/progress-table.git",
14
14
  author="Szymon Mikler",
15
15
  author_email="sjmikler@gmail.com",
@@ -27,7 +27,7 @@ setup(
27
27
  "Programming Language :: Python :: 3.11",
28
28
  "Programming Language :: Python :: 3.12",
29
29
  ],
30
- install_requires=["colorama", "pandas"],
30
+ install_requires=["colorama"],
31
31
  long_description=long_description,
32
32
  long_description_content_type="text/markdown",
33
33
  )
@@ -1,5 +0,0 @@
1
- # Copyright (c) 2022 Szymon Mikler
2
-
3
- from .progress_table import ProgressTable
4
-
5
- __all__ = ["ProgressTable"]
@@ -1,590 +0,0 @@
1
- # Copyright (c) 2022 Szymon Mikler
2
-
3
- from __future__ import annotations
4
-
5
- import inspect
6
- import logging
7
- import math
8
- import shutil
9
- import sys
10
- import time
11
- from builtins import KeyError, staticmethod
12
- from collections import defaultdict
13
- from typing import Any, Callable, Dict, List, Tuple
14
-
15
- from colorama import Fore, Style
16
-
17
- from . import symbols
18
-
19
- ALL_COLOR_NAMES = [x for x in dir(Fore) if not x.startswith("__")]
20
- ALL_STYLE_NAMES = [x for x in dir(Style) if not x.startswith("__")]
21
- ALL_COLORS_STYLES_NAMES = ALL_COLOR_NAMES + ALL_STYLE_NAMES
22
-
23
- ALL_COLORS = [getattr(Fore, x) for x in ALL_COLOR_NAMES]
24
- ALL_STYLES = [getattr(Style, x) for x in ALL_STYLE_NAMES]
25
- ALL_COLORS_STYLES = ALL_COLORS + ALL_STYLES
26
-
27
- ITERATOR_LENGTH_UNKNOWN_WARNED_ONCE = False
28
- ITERATOR_LENGTH_CACHE: Dict[int, int] = {}
29
-
30
-
31
- class ProgressTable:
32
- def __init__(
33
- self,
34
- columns: Tuple | List = (),
35
- refresh_rate: int = 10,
36
- num_decimal_places: int = 4,
37
- default_column_width: int = 8,
38
- default_column_alignment: str = "center",
39
- default_show_throughput: bool = True,
40
- default_show_progress: bool = False,
41
- print_row_on_update: bool = True,
42
- reprint_header_every_n_rows: int = 30,
43
- custom_format: Callable[[Any], Any] | None = None,
44
- embedded_progress_bar: bool = False,
45
- table_style: str = "normal",
46
- file=sys.stdout,
47
- ):
48
- """Progress Table instance.
49
-
50
- Columns have to be specified before displaying the first row or launching a progress bar!
51
-
52
- Args:
53
- columns: Columns that will be displayed in the header. Columns can be provided directly in
54
- `__init__` or through methods `add_column` and `add_columns`. Columns added through
55
- `__init__` will have default settings like alignment, color and width, while
56
- columns added through methods can have those customized.
57
- refresh_rate: The maximal number of times per second the last row of the Table will be refreshed.
58
- This applies only when using Progress Bar or when `print_row_on_update = True`.
59
- num_decimal_places: This is only applicable when using the default formatting. This won't be used
60
- if `custom_format` is set. If applicable, for every displayed value except
61
- integers there will be an attempt to round it.
62
- default_column_width: Width of the column excluding cell padding. This can be overwritten by
63
- columns by using an argument in `add_column` method.
64
- default_column_alignment: Alignment in the column. Can be aligned either to `left`, `right` or
65
- `center` (default). This can be overwritten by columns by using
66
- an argument in `add_column` method.
67
- print_row_on_update: True by default. If False, the current row will be displayed only when
68
- it's ready. Row is considered ready after calling `.next_row` or `.close`
69
- methods.
70
- reprint_header_every_n_rows: 30 by default. When table has a lot of rows, it can be useful to
71
- remind what the header is. If True, hedaer will be displayed
72
- periodically after the selected number of rows. 0 to supress.
73
- custom_format: A function that allows specyfing custom formatting for values in cells.
74
- This function should be universal and work for all datatypes as inputs.
75
- It takes one value as an input and returns one value as an output.
76
- embedded_progress_bar: False by default. If True, changes the way the progress bar looks.
77
- Embedded version is more subtle, but does not prevent the current row
78
- from being displayed. If False, the progress bar covers the current
79
- row, preventing the user from seeing values that are being updated
80
- until the progress bar finishes.
81
- table_style: Change the borders of the table. Cause KeyError to see all the available styles.
82
- file: Redirect the output to another stream. There can be multiple streams at once passed as
83
- a list or a tuple. Defaults to sys.stdout.
84
- """
85
- self.refresh_rate = refresh_rate
86
- self.default_width = default_column_width
87
- self.default_alignment = default_column_alignment
88
- self.default_show_throughput = default_show_throughput
89
- self.default_show_progress = default_show_progress
90
- self.print_row_on_update = print_row_on_update
91
- self.reprint_header_every_n_rows = reprint_header_every_n_rows
92
- self.custom_format = custom_format or self.get_default_format_func(num_decimal_places)
93
- self.embedded_progress_bar = embedded_progress_bar
94
-
95
- if not isinstance(file, list) and not isinstance(file, tuple):
96
- file = (file,)
97
- self.files = file
98
-
99
- # Helpers
100
- self._widths: Dict[str, int] = {}
101
- self._colors: Dict[str, str] = {}
102
- self._alignment: Dict[str, str] = {}
103
- self._aggregate: Dict[str, str | None] = {}
104
- self._new_row: Dict[str, Any] = defaultdict(str)
105
- self._aggregate_n: Dict[str, int] = defaultdict(int)
106
-
107
- self.num_rows = 0
108
- self.columns: List[str] = []
109
- self.finished_rows: List[Dict[Any, Any]] = []
110
-
111
- self._needs_line_ending = False
112
- self._last_time_row_printed = 0
113
- self._last_header_printed_at_row_count = 0
114
-
115
- self.header_printed = False
116
- self.progress_bar_active = False
117
- self._refresh_progress_bar: Callable = lambda: None
118
- self._needs_splitter = False
119
-
120
- self._last_row_content = None
121
-
122
- allowed_styles = [getattr(symbols, class_name) for class_name in dir(symbols)]
123
- allowed_styles = [x for x in allowed_styles if inspect.isclass(x) and issubclass(x, symbols.Symbols)]
124
- style_name_to_style = {x.name: x for x in allowed_styles if hasattr(x, "name")}
125
-
126
- if table_style in style_name_to_style:
127
- self._symbols = style_name_to_style[table_style]
128
- else:
129
- allowed_style_names = list(style_name_to_style)
130
- raise KeyError(f"Style '{table_style}' not in {allowed_style_names}!")
131
-
132
- for column in columns:
133
- self.add_column(column)
134
-
135
- @staticmethod
136
- def get_default_format_func(decimal_places):
137
- def fmt(x):
138
- if isinstance(x, int):
139
- return x
140
- else:
141
- try:
142
- return format(x, f".{decimal_places}f")
143
- except Exception:
144
- return x
145
-
146
- return fmt
147
-
148
- def add_column(self, name, *, width=None, alignment=None, color=None, aggregate=None):
149
- """Add column to the table.
150
-
151
- Args:
152
- name: Name of the column. Will be displayed in the header.
153
- width: Width of the column. If width is smaller than the name, width will be automatically
154
- increased to the smallest possible value. This means setting `width=0` will set the column
155
- to the smallest possible width allowed by `name`.
156
- alignment: Alignment of the data in the column.
157
- color: Color of the header and the data in the column.
158
- aggregate: By default, there's no aggregation. But if this is for example 'mean', then
159
- after every update in the current row, the mean of the provided values will be
160
- displayed. Aggregation is reset at every new row.
161
- """
162
- assert not self.header_printed, "Columns cannot be modified after displaying the first row!"
163
-
164
- if name in self.columns:
165
- logging.info(f"Column '{name}' already exists!")
166
- else:
167
- self.columns.append(name)
168
-
169
- self._colors.setdefault(name, "")
170
- if color is not None:
171
- if isinstance(color, str):
172
- color = [color]
173
- for str_color in color:
174
- byte_color = self._maybe_convert_to_colorama(str_color)
175
- self._check_color(byte_color, str_color)
176
- self._colors[name] += byte_color
177
-
178
- alignment = alignment if alignment is not None else self.default_alignment
179
- self._alignment[name] = alignment
180
-
181
- assert aggregate in [
182
- None,
183
- "mean",
184
- "sum",
185
- "max",
186
- "min",
187
- ], "Allowed aggregate types: [None, 'mean', 'sum', 'max', 'min']"
188
- self._aggregate[name] = aggregate
189
-
190
- width = width if width is not None else self.default_width
191
- if width < len(name):
192
- width = len(name)
193
- self._widths[name] = width
194
-
195
- def add_columns(self, iterable, **kwds):
196
- """Add multiple columns to the table."""
197
- for column in iterable:
198
- self.add_column(column, **kwds)
199
-
200
- def next_row(self, save=True, split=False, header=False):
201
- """End the current row."""
202
- self._print_row()
203
- self._maybe_line_ending()
204
- if self.progress_bar_active:
205
- self._refresh_progress_bar()
206
- sys.stdout.flush()
207
-
208
- if save and len(self._new_row) > 0:
209
- self.finished_rows.append(self._new_row)
210
- self.num_rows += 1
211
-
212
- self._new_row = defaultdict(str)
213
- self._aggregate_n = defaultdict(int)
214
-
215
- if split:
216
- self._needs_splitter = True
217
-
218
- if header:
219
- self._print_header(top=False)
220
-
221
- def close(self):
222
- """End the table and close it."""
223
- self._needs_splitter = False
224
-
225
- self.next_row()
226
- self._print_bottom_bar()
227
- self.header_printed = False
228
- self._print()
229
-
230
- def to_list(self):
231
- """Convert to Python nested list."""
232
- return [[row[col] for col in self.columns] for row in self.finished_rows]
233
-
234
- def to_numpy(self):
235
- """Convert to numpy array."""
236
- import numpy as np
237
-
238
- return np.array(self.to_list())
239
-
240
- def to_df(self):
241
- """Convert to pandas DataFrame."""
242
- import pandas as pd
243
-
244
- return pd.DataFrame(self.to_list(), columns=self.columns)
245
-
246
- def display(self):
247
- """Display the whole table. Can be used after closing the table."""
248
- if self.header_printed:
249
- self.close()
250
- self._display_custom(self.to_list())
251
-
252
- def update(self, key, value, *, weight=1):
253
- """Update value in the current row."""
254
- assert key in self.columns, f"Column '{key}' not in {self.columns}"
255
-
256
- if self._aggregate[key] == "sum":
257
- aggr_value = self._new_row[key] if self._aggregate_n[key] > 0 else 0
258
- self._new_row[key] = aggr_value + value * weight
259
- self._aggregate_n[key] += weight
260
-
261
- elif self._aggregate[key] == "mean":
262
- n = self._aggregate_n[key]
263
- aggr_value = self._new_row[key] if n > 0 else 0
264
- self._new_row[key] = (aggr_value * n + value * weight) / (n + weight)
265
- self._aggregate_n[key] += weight
266
-
267
- elif self._aggregate[key] == "max":
268
- n = self._aggregate_n[key]
269
- aggr_value = self._new_row[key] if n > 0 else -float("inf")
270
- self._new_row[key] = max(aggr_value, value)
271
- self._aggregate_n[key] += 1
272
-
273
- elif self._aggregate[key] == "min":
274
- n = self._aggregate_n[key]
275
- aggr_value = self._new_row[key] if n > 0 else float("inf")
276
- self._new_row[key] = min(aggr_value, value)
277
- self._aggregate_n[key] += 1
278
-
279
- else:
280
- self._new_row[key] = value
281
-
282
- t0 = time.time()
283
- td = t0 - self._last_time_row_printed
284
- if self.print_row_on_update and td > 1 / self.refresh_rate:
285
- self._last_time_row_printed = t0
286
-
287
- if not self.progress_bar_active:
288
- self._print_row()
289
- elif self.embedded_progress_bar:
290
- self._refresh_progress_bar()
291
-
292
- def update_from_dict(self, dictionary):
293
- """Update multiple values in the current row."""
294
- for key, value in dictionary.items():
295
- self.update(key, value)
296
-
297
- def _apply_cell_formatting(self, str_value: str, column: str):
298
- width = self._widths[column]
299
-
300
- alignment = self._alignment[column]
301
- if alignment == "center":
302
- str_value = str_value.center(width)
303
- elif alignment == "left":
304
- str_value = str_value.ljust(width)
305
- elif alignment == "right":
306
- str_value = str_value.rjust(width)
307
- else:
308
- allowed_alignments = ["center", "left", "right"]
309
- raise KeyError(f"Alignment '{alignment}' not in {allowed_alignments}!")
310
-
311
- clipped = len(str_value) > width
312
- str_value = "".join(
313
- [
314
- " ", # space at the beginning of the row
315
- str_value[:width].center(width),
316
- self._symbols.dots if clipped else " ",
317
- ]
318
- )
319
-
320
- if self._colors[column] is not None:
321
- str_value = f"{self._colors[column]}{str_value}{Style.RESET_ALL}"
322
- return str_value
323
-
324
- def _bar(self, left: str, center: str, right: str):
325
- content_list: List[str] = []
326
- for col in self.columns:
327
- content_list.append(self._symbols.horizontal * (self._widths[col] + 2))
328
-
329
- center = center.join(content_list)
330
- content = ["\r", left, center, right]
331
- self._print("".join(content), end="")
332
-
333
- def _bar_custom_center(self, left: str, center: List[str] | str, right: str):
334
- """UNUSED"""
335
- content_list: List[str] = []
336
-
337
- if isinstance(center, str):
338
- center = [center] * (len(self.columns) - 1)
339
- assert len(center) == len(self.columns) - 1
340
-
341
- for col in self.columns:
342
- content_list.append(self._symbols.horizontal * (self._widths[col] + 2))
343
- if center:
344
- content_list.append(center.pop(0))
345
-
346
- center = "".join(content_list)
347
- content = ["\r", left, center, right]
348
- self._print("".join(content), end="")
349
-
350
- def _print_transition_bar(self, previous_n_cols, new_n_cols):
351
- """UNUSED"""
352
- assert previous_n_cols > 0
353
- assert new_n_cols > previous_n_cols
354
- added_cols = new_n_cols - previous_n_cols
355
-
356
- symbols = [
357
- *([self._symbols.all] * previous_n_cols),
358
- *([self._symbols.no_up] * (added_cols - 1)),
359
- ]
360
- return self._bar_custom_center(left=self._symbols.no_left, center=symbols, right=self._symbols.down_left)
361
-
362
- def _print_top_bar(self):
363
- return self._bar(left=self._symbols.down_right, center=self._symbols.no_up, right=self._symbols.down_left)
364
-
365
- def _print_bottom_bar(self):
366
- return self._bar(left=self._symbols.up_right, center=self._symbols.no_down, right=self._symbols.up_left)
367
-
368
- def _print_center_bar(self):
369
- return self._bar(left=self._symbols.no_left, center=self._symbols.all, right=self._symbols.no_right)
370
-
371
- @staticmethod
372
- def _maybe_convert_to_colorama(color):
373
- if isinstance(color, str):
374
- if hasattr(Fore, color.upper()):
375
- return getattr(Fore, color.upper())
376
- if hasattr(Style, color.upper()):
377
- return getattr(Style, color.upper())
378
- return color
379
-
380
- @staticmethod
381
- def _check_color(color, color_str=None):
382
- if color_str is None:
383
- color_str = color
384
- assert (
385
- color in ALL_COLORS_STYLES
386
- ), f"Only colorama colors are allowed, not '{color_str}'! Available: {ALL_COLORS_STYLES_NAMES}"
387
-
388
- def _print_header(self, top=True):
389
- assert self.columns, "Columns are required! Use .add_column method or specify them in __init__!"
390
-
391
- self._needs_splitter = False
392
-
393
- if top:
394
- self._print_top_bar()
395
- else:
396
- self._print_center_bar()
397
- self._print()
398
-
399
- content = []
400
- for column in self.columns:
401
- value = self._apply_cell_formatting(column, column)
402
- content.append(value)
403
- s = "".join(["\r", self._symbols.vertical, self._symbols.vertical.join(content), self._symbols.vertical])
404
- self._print(s)
405
-
406
- self._last_header_printed_at_row_count = self.num_rows
407
- self.header_printed = True
408
- self._print_center_bar()
409
- self._print()
410
- self._last_row_content = "header"
411
-
412
- def _print_splitter(self):
413
- if self._last_row_content == "row":
414
- self._print_center_bar()
415
- self._print()
416
-
417
- self._needs_splitter = False
418
- self._last_row_content = "splitter"
419
-
420
- def _get_row(self):
421
- content = []
422
- for column in self.columns:
423
- value = self._new_row[column]
424
- value = self.custom_format(value)
425
- value = self._apply_cell_formatting(str_value=str(value), column=column)
426
- content.append(value)
427
- return "".join(["\r", self._symbols.vertical, self._symbols.vertical.join(content), self._symbols.vertical])
428
-
429
- def _print_row(self):
430
- if not self.header_printed:
431
- self._print_header(top=True)
432
-
433
- if self.reprint_header_every_n_rows != 0:
434
- if self.num_rows - self._last_header_printed_at_row_count >= self.reprint_header_every_n_rows:
435
- self._print_header(top=False)
436
-
437
- if self._needs_splitter:
438
- self._print_splitter()
439
-
440
- if len(self._new_row) == 0:
441
- return
442
- self._needs_line_ending = True
443
- self._print(self._get_row(), end="")
444
-
445
- def _print_progress_bar(self, i, n, show_before=" ", show_after=" ", embedded=False):
446
- i = min(i, n) # clip the iteration number to be not bigger than the total number of iterations
447
- terminal_width = shutil.get_terminal_size(fallback=(0, 0)).columns or float("inf")
448
-
449
- if not embedded:
450
- tot_width = sum(self._widths.values()) + 3 * (len(self._widths) - 1) + 2
451
- if tot_width >= terminal_width - 1:
452
- tot_width = terminal_width - 2
453
-
454
- tot_width = tot_width - len(show_before) - len(show_after)
455
- num_hashes = math.ceil(i / n * tot_width)
456
- num_empty = tot_width - num_hashes
457
-
458
- self._print(
459
- self._symbols.vertical,
460
- show_before,
461
- self._symbols.pbar_filled * num_hashes,
462
- self._symbols.pbar_empty * num_empty,
463
- show_after,
464
- self._symbols.vertical,
465
- end="\r",
466
- sep="",
467
- )
468
- else:
469
- row = self._get_row()
470
- new_row = []
471
- for idx, letter in enumerate(row):
472
- if idx / len(row) <= i / n:
473
- if letter == " ":
474
- letter = self._symbols.embedded_pbar_filled
475
- new_row.append(letter)
476
- row = "".join(new_row)
477
- self._print(row, end="\r")
478
- sys.stdout.flush()
479
-
480
- def _maybe_line_ending(self):
481
- if self._needs_line_ending:
482
- self._print()
483
- self._needs_splitter = False
484
- self._needs_line_ending = False
485
- self._last_row_content = "row"
486
-
487
- def _display_custom(self, data):
488
- if self.header_printed:
489
- self.close()
490
-
491
- for row in data:
492
- assert len(row) == len(self.columns)
493
- for key, value in zip(self.columns, row):
494
- self._new_row[key] = value
495
- self._print_row()
496
- self.next_row(save=False)
497
- self.close()
498
- self._last_row_content = "close"
499
-
500
- def _print(self, *args, **kwds):
501
- for file in self.files:
502
- print(*args, **kwds, file=file)
503
-
504
- def __call__(self, iterator, *range_args, length=None, prefix="", show_throughput=None, show_progress=None):
505
- """Display progress bar over the iterator. Try to figure out the iterator length."""
506
- global ITERATOR_LENGTH_UNKNOWN_WARNED_ONCE, ITERATOR_LENGTH_CACHE
507
-
508
- if not self.header_printed:
509
- self._print_header()
510
-
511
- if show_throughput is None:
512
- show_throughput = self.default_show_throughput
513
- if show_progress is None:
514
- show_progress = self.default_show_progress
515
-
516
- if isinstance(iterator, int):
517
- iterator = range(iterator, *range_args)
518
- else:
519
- assert len(range_args) == 0, "Unnamed args are not allowed here!"
520
-
521
- if length is None and not hasattr(iterator, "__len__"):
522
- if not ITERATOR_LENGTH_UNKNOWN_WARNED_ONCE:
523
- logging.warning("Iterator length is unknown!")
524
- ITERATOR_LENGTH_UNKNOWN_WARNED_ONCE = True
525
-
526
- # We have a back-up mechanism in case of unknown-length iterators.
527
- # If we progress over the same unknown length iterator more than once,
528
- # we will use its length from the previous launch.
529
- save_length_to_cache = True
530
-
531
- if id(iterator) in ITERATOR_LENGTH_CACHE:
532
- length = ITERATOR_LENGTH_CACHE[id(iterator)]
533
- else:
534
- length = 1
535
- else:
536
- save_length_to_cache = False
537
- length = length or len(iterator)
538
-
539
- t_last_printed = -float("inf")
540
- t_beginning = time.time()
541
-
542
- self.progress_bar_active = True
543
-
544
- idx = 0
545
- for idx, element in enumerate(iterator):
546
- if time.time() - t_last_printed > 1 / self.refresh_rate:
547
- # Reenable here, in case of nested progress bars
548
- self.progress_bar_active = True
549
-
550
- self._print(end="\r")
551
- s = time.time() - t_beginning
552
- throughput = idx / s if s > 0 else 0.0
553
-
554
- full_prefix = [" [", prefix]
555
-
556
- inside_brackets = []
557
- if show_throughput:
558
- inside_brackets.append(f"{throughput: <.2f} it/s")
559
- if show_progress:
560
- inside_brackets.append(f"{idx}/{length}")
561
- inside_brackets = ", ".join(inside_brackets)
562
- full_prefix.append(inside_brackets)
563
-
564
- full_prefix.append("] ")
565
- full_prefix = "".join(full_prefix)
566
- full_prefix = full_prefix if full_prefix != " [] " else " "
567
-
568
- self._refresh_progress_bar = lambda: self._print_progress_bar(
569
- idx,
570
- length,
571
- show_before=full_prefix,
572
- embedded=self.embedded_progress_bar,
573
- )
574
- self._refresh_progress_bar()
575
-
576
- t_last_printed = time.time()
577
- yield element
578
- if save_length_to_cache:
579
- ITERATOR_LENGTH_CACHE[id(iterator)] = idx
580
-
581
- self.progress_bar_active = False
582
- self._print_row()
583
-
584
- def __setitem__(self, key, value):
585
- self.update(key, value, weight=1)
586
-
587
- def __getitem__(self, key):
588
- assert key in self.columns, f"Column '{key}' not in {self.columns}"
589
-
590
- return self._new_row[key]
@@ -1,160 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
-
4
- @dataclass
5
- class Symbols:
6
- pbar_filled: str
7
- pbar_empty: str
8
- embedded_pbar_filled: str
9
- dots: str
10
- horizontal: str
11
- vertical: str
12
- all: str
13
- up_left: str
14
- up_right: str
15
- down_left: str
16
- down_right: str
17
- no_left: str
18
- no_right: str
19
- no_up: str
20
- no_down: str
21
-
22
-
23
- @dataclass
24
- class SymbolsUnicode(Symbols):
25
- name = "normal"
26
- pbar_filled = "■"
27
- pbar_empty = "□"
28
- embedded_pbar_filled = "_"
29
- dots = "…"
30
- horizontal = "─"
31
- vertical = "│"
32
- all = "┼"
33
- up_left = "┘"
34
- up_right = "└"
35
- down_left = "┐"
36
- down_right = "┌"
37
- no_left = "├"
38
- no_right = "┤"
39
- no_up = "┬"
40
- no_down = "┴"
41
-
42
-
43
- @dataclass
44
- class SymbolsUnicodeBare(Symbols):
45
- name = "bare"
46
- pbar_filled = "■"
47
- pbar_empty = "□"
48
- embedded_pbar_filled = "_"
49
- dots = "…"
50
- horizontal = "─"
51
- vertical = " "
52
- all = "─"
53
- up_left = "─"
54
- up_right = "─"
55
- down_left = "─"
56
- down_right = "─"
57
- no_left = "─"
58
- no_right = "─"
59
- no_up = "─"
60
- no_down = "─"
61
-
62
-
63
- @dataclass
64
- class SymbolsUnicodeRound(Symbols):
65
- name = "round"
66
- pbar_filled = "■"
67
- pbar_empty = "□"
68
- embedded_pbar_filled = "_"
69
- dots = "…"
70
- horizontal = "─"
71
- vertical = "│"
72
- all = "┼"
73
- up_left = "╯"
74
- up_right = "╰"
75
- down_left = "╮"
76
- down_right = "╭"
77
- no_left = "├"
78
- no_right = "┤"
79
- no_up = "┬"
80
- no_down = "┴"
81
-
82
-
83
- @dataclass
84
- class SymbolsUnicodeDouble(Symbols):
85
- name = "double"
86
- pbar_filled = "■"
87
- pbar_empty = "□"
88
- embedded_pbar_filled = "_"
89
- dots = "…"
90
- horizontal = "═"
91
- vertical = "║"
92
- all = "╬"
93
- up_left = "╝"
94
- up_right = "╚"
95
- down_left = "╗"
96
- down_right = "╔"
97
- no_left = "╠"
98
- no_right = "╣"
99
- no_up = "╦"
100
- no_down = "╩"
101
-
102
-
103
- @dataclass
104
- class SymbolsUnicodeBold(Symbols):
105
- name = "bold"
106
- pbar_filled = "■"
107
- pbar_empty = "□"
108
- embedded_pbar_filled = "_"
109
- dots = "…"
110
- horizontal = "━"
111
- vertical = "┃"
112
- all = "╋"
113
- up_left = "┛"
114
- up_right = "┗"
115
- down_left = "┓"
116
- down_right = "┏"
117
- no_left = "┣"
118
- no_right = "┫"
119
- no_up = "┳"
120
- no_down = "┻"
121
-
122
-
123
- @dataclass
124
- class SymbolsAscii(Symbols):
125
- name = "ascii"
126
- pbar_filled = "#"
127
- pbar_empty = "."
128
- embedded_pbar_filled = "_"
129
- dots = "_"
130
- horizontal = "-"
131
- vertical = "|"
132
- all = "+"
133
- up_left = "+"
134
- up_right = "+"
135
- down_left = "+"
136
- down_right = "+"
137
- no_left = "+"
138
- no_right = "+"
139
- no_up = "+"
140
- no_down = "+"
141
-
142
-
143
- @dataclass
144
- class SymbolsAsciiBare(Symbols):
145
- name = "ascii_bare"
146
- pbar_filled = "#"
147
- pbar_empty = "."
148
- embedded_pbar_filled = "_"
149
- dots = "_"
150
- horizontal = "-"
151
- vertical = " "
152
- all = "-"
153
- up_left = "-"
154
- up_right = "-"
155
- down_left = "-"
156
- down_right = "-"
157
- no_left = "-"
158
- no_right = "-"
159
- no_up = "-"
160
- no_down = "-"