Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/explanation/performance-and-the-fast-path.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ gets the full ``ProgressBar`` instead. You can also construct
renderer without going through the shortcut -- see
:doc:`../reference/progressbar`.

The known-length fast bar reserves width for ``prefix`` (or ``desc``)
and ``suffix`` before sizing its bar. It uses ``custom_len`` to measure
the fixed text, so ANSI colors and custom display-width rules do not
consume extra bar columns.

This dispatch only happens inside ``progressbar.progressbar()``.
Constructing ``ProgressBar()`` yourself always gets the full class.

Expand Down
4 changes: 3 additions & 1 deletion progressbar/fast.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ def _pure_format_fast_line(bar: FastProgressBar) -> str:
eta = '--:--:--'
left = f'{pct:3.0f}% {count} '
right = f' Elapsed Time: {elapsed_text} ETA: {eta}'
inner = max(width - len(left) - len(right) - 2, 0)
inner = max(
width - bar.custom_len(prefix + left + right + suffix) - 2, 0
)
filled = int(inner * done / total) if total else inner
barstr = '|' + '#' * filled + ' ' * (inner - filled) + '|'
return f'{prefix}{left}{barstr}{right}{suffix}'
Expand Down
50 changes: 50 additions & 0 deletions tests/test_fast_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import io
import sys

import pytest

import progressbar

# Alias (not a `from` import) so CodeQL doesn't flag `progressbar` as imported
Expand Down Expand Up @@ -248,3 +250,51 @@ def test_fast_path_does_not_import_widgets_or_colors():
)
assert out.returncode == 0, out.stderr
assert 'ok' in out.stdout


@pytest.mark.parametrize('width', [80, 110])
@pytest.mark.parametrize(
('prefix', 'suffix'),
[
('', ''),
('load ', ''),
('', ' done'),
('load ', ' done'),
('\x1b[31mload \x1b[0m', ' done'),
],
)
def test_fast_labels_share_terminal_width(width, prefix, suffix):
fd = TTY()
bar = fast_module.FastProgressBar(
max_value=10, fd=fd, term_width=width, prefix=prefix, suffix=suffix
)
assert list(bar(range(10))) == list(range(10))
for frame in fd.repaints():
assert progressbar.utils.len_color(frame.rstrip('\r\n')) == width


def test_fast_labels_use_custom_width_measurement():
def display_width(text):
return progressbar.utils.len_color(text.replace('界', 'xx'))

fd = TTY()
bar = fast_module.FastProgressBar(
max_value=10,
fd=fd,
term_width=80,
prefix='界 ',
suffix=' 界',
custom_len=display_width,
)
assert list(bar(range(10))) == list(range(10))
for frame in fd.repaints():
assert display_width(frame.rstrip('\r\n')) == 80


def test_shortcut_desc_shares_terminal_width():
fd = TTY()
assert list(
progressbar.progressbar(range(10), fd=fd, desc='load ', term_width=80)
) == list(range(10))
for frame in fd.repaints():
assert progressbar.utils.len_color(frame.rstrip('\r\n')) == 80
Loading