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