Skip to content

Commit 62906c8

Browse files
committed
make bar format params take a list of strings instead of a single string
1 parent 3a032d5 commit 62906c8

2 files changed

Lines changed: 62 additions & 29 deletions

File tree

src/xulbux/console.py

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,8 +1296,9 @@ def __init__(
12961296
self,
12971297
min_width: int = 10,
12981298
max_width: int = 50,
1299-
bar_format: str = "{l} |{b}| [b]({c})/{t} [dim](([i]({p}%)))",
1300-
limited_bar_format: str = "|{b}|",
1299+
bar_format: list[str] | tuple[str, ...] = ["{l}", "|{b}|", "[b]({c})/{t}", "[dim](([i]({p}%)))"],
1300+
limited_bar_format: list[str] | tuple[str, ...] = ["|{b}|"],
1301+
sep: str = " ",
13011302
chars: tuple[str, ...] = ("█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", " "),
13021303
):
13031304
self.active: bool = False
@@ -1306,15 +1307,17 @@ def __init__(
13061307
"""The min width of the progress bar in chars."""
13071308
self.max_width: int
13081309
"""The max width of the progress bar in chars."""
1309-
self.bar_format: str
1310-
"""The format string used to render the progress bar."""
1311-
self.limited_bar_format: str
1312-
"""The simplified format string used when the console width is too small."""
1310+
self.bar_format: list[str] | tuple[str, ...]
1311+
"""The format strings used to render the progress bar (joined by `sep`)."""
1312+
self.limited_bar_format: list[str] | tuple[str, ...]
1313+
"""The simplified format strings used when the console width is too small."""
1314+
self.sep: str
1315+
"""The separator string used to join multiple format strings."""
13131316
self.chars: tuple[str, ...]
13141317
"""A tuple of characters ordered from full to empty progress."""
13151318

13161319
self.set_width(min_width, max_width)
1317-
self.set_bar_format(bar_format, limited_bar_format)
1320+
self.set_bar_format(bar_format, limited_bar_format, sep)
13181321
self.set_chars(chars)
13191322

13201323
self._buffer: list[str] = []
@@ -1345,7 +1348,12 @@ def set_width(self, min_width: Optional[int] = None, max_width: Optional[int] =
13451348

13461349
self.max_width = max(self.min_width, max_width)
13471350

1348-
def set_bar_format(self, bar_format: Optional[str] = None, limited_bar_format: Optional[str] = None) -> None:
1351+
def set_bar_format(
1352+
self,
1353+
bar_format: Optional[list[str] | tuple[str, ...]] = None,
1354+
limited_bar_format: Optional[list[str] | tuple[str, ...]] = None,
1355+
sep: Optional[str] = None,
1356+
) -> None:
13491357
"""Set the format string used to render the progress bar.\n
13501358
--------------------------------------------------------------------------------------------------
13511359
- `bar_format` -⠀the format string used to render the progress bar, containing placeholders:
@@ -1355,23 +1363,38 @@ def set_bar_format(self, bar_format: Optional[str] = None, limited_bar_format: O
13551363
* `{total}` `{t}`
13561364
* `{percentage}` `{percent}` `{p}`
13571365
- `limited_bar_format` -⠀a simplified format string used when the console width is too small
1366+
- `sep` -⠀the separator string used to join multiple format strings
13581367
--------------------------------------------------------------------------------------------------
13591368
The bar format (also limited) can additionally be formatted with special formatting codes. For
13601369
more detailed information about formatting codes, see the `format_codes` module documentation."""
1361-
if not isinstance(bar_format, (str, type(None))):
1362-
raise TypeError(f"The 'bar_format' parameter must be a string or None, got {type(bar_format)}")
1363-
if not isinstance(limited_bar_format, (str, type(None))):
1364-
raise TypeError(f"The 'limited_bar_format' parameter must be a string or None, got {type(limited_bar_format)}")
1365-
13661370
if bar_format is not None:
1367-
if not _COMPILED["bar"].search(bar_format):
1371+
if not isinstance(bar_format, (list, tuple)):
1372+
raise TypeError(f"The 'bar_format' parameter must be a list or tuple of strings, got {type(bar_format)}")
1373+
elif not all(isinstance(s, str) for s in bar_format):
1374+
raise ValueError("All elements of the 'bar_format' parameter must be strings.")
1375+
elif not any(_COMPILED["bar"].search(s) for s in bar_format):
13681376
raise ValueError("The 'bar_format' parameter value must contain the '{bar}' or '{b}' placeholder.")
1377+
13691378
self.bar_format = bar_format
1379+
13701380
if limited_bar_format is not None:
1371-
if not _COMPILED["bar"].search(limited_bar_format):
1381+
if not isinstance(limited_bar_format, (list, tuple)):
1382+
raise TypeError(
1383+
f"The 'limited_bar_format' parameter must be a list or tuple of strings, got {type(limited_bar_format)}"
1384+
)
1385+
elif not all(isinstance(s, str) for s in limited_bar_format):
1386+
raise ValueError("All elements of the 'limited_bar_format' parameter must be strings.")
1387+
elif not any(_COMPILED["bar"].search(s) for s in limited_bar_format):
13721388
raise ValueError("The 'limited_bar_format' parameter value must contain the '{bar}' or '{b}' placeholder.")
1389+
13731390
self.limited_bar_format = limited_bar_format
13741391

1392+
if sep is not None:
1393+
if not isinstance(sep, str):
1394+
raise TypeError(f"The 'sep' parameter must be a string or None, got {type(sep)}")
1395+
1396+
self.sep = sep
1397+
13751398
def set_chars(self, chars: tuple[str, ...]) -> None:
13761399
"""Set the characters used to render the progress bar.\n
13771400
--------------------------------------------------------------------------
@@ -1529,20 +1552,29 @@ def _draw_progress_bar(self, current: int, total: int, label: Optional[str] = No
15291552

15301553
def _get_formatted_info_and_bar_width(
15311554
self,
1532-
bar_format: str,
1555+
bar_format: list[str] | tuple[str, ...],
15331556
current: int,
15341557
total: int,
15351558
percentage: float,
15361559
label: Optional[str] = None,
15371560
) -> tuple[str, int]:
1538-
formatted = _COMPILED["label"].sub(label or "", bar_format)
1539-
formatted = _COMPILED["current"].sub(str(current), formatted)
1540-
formatted = _COMPILED["total"].sub(str(total), formatted)
1541-
formatted = _COMPILED["percentage"].sub(f"{percentage:.1f}", formatted)
1542-
formatted = FormatCodes.to_ansi(formatted)
1543-
bar_space = Console.w - len(FormatCodes.remove_ansi(_COMPILED["bar"].sub("", formatted)))
1561+
fmt_parts = []
1562+
1563+
for s in bar_format:
1564+
fmt_part = _COMPILED["label"].sub(label or "", s)
1565+
fmt_part = _COMPILED["current"].sub(str(current), fmt_part)
1566+
fmt_part = _COMPILED["total"].sub(str(total), fmt_part)
1567+
fmt_part = _COMPILED["percentage"].sub(f"{percentage:.1f}", fmt_part)
1568+
if fmt_part:
1569+
fmt_parts.append(fmt_part)
1570+
1571+
fmt_str = self.sep.join(fmt_parts)
1572+
fmt_str = FormatCodes.to_ansi(fmt_str)
1573+
1574+
bar_space = Console.w - len(FormatCodes.remove_ansi(_COMPILED["bar"].sub("", fmt_str)))
15441575
bar_width = min(bar_space, self.max_width) if bar_space > 0 else 0
1545-
return formatted, bar_width
1576+
1577+
return fmt_str, bar_width
15461578

15471579
def _create_bar(self, current: int, total: int, bar_width: int) -> str:
15481580
progress = current / total if total > 0 else 0

tests/test_console.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -987,17 +987,17 @@ def test_progressbar_set_width_invalid():
987987

988988
def test_progressbar_set_bar_format():
989989
pb = ProgressBar()
990-
pb.set_bar_format(bar_format="{l} [{b}] {p}%", limited_bar_format="[{b}]")
991-
assert pb.bar_format == "{l} [{b}] {p}%"
992-
assert pb.limited_bar_format == "[{b}]"
990+
pb.set_bar_format(bar_format=["{l}", "[{b}]", "{p}%"], limited_bar_format=["[{b}]"])
991+
assert pb.bar_format == ["{l}", "[{b}]", "{p}%"]
992+
assert pb.limited_bar_format == ["[{b}]"]
993993

994994

995995
def test_progressbar_set_bar_format_invalid():
996996
pb = ProgressBar()
997997
with pytest.raises(ValueError, match="must contain the '{bar}' or '{b}' placeholder"):
998-
pb.set_bar_format(bar_format="Progress: {p}%")
998+
pb.set_bar_format(bar_format=["Progress: {p}%"])
999999
with pytest.raises(ValueError, match="must contain the '{bar}' or '{b}' placeholder"):
1000-
pb.set_bar_format(limited_bar_format="Progress: {p}%")
1000+
pb.set_bar_format(limited_bar_format=["Progress: {p}%"])
10011001

10021002

10031003
def test_progressbar_set_chars():
@@ -1102,7 +1102,8 @@ def test_progressbar_emergency_cleanup():
11021102

11031103
def test_progressbar_get_formatted_info_and_bar_width(mock_terminal_size):
11041104
pb = ProgressBar()
1105-
formatted, bar_width = pb._get_formatted_info_and_bar_width("{l} |{b}| {c}/{t} ({p}%)", 50, 100, 50.0, "Loading")
1105+
formatted, bar_width = pb._get_formatted_info_and_bar_width(["{l}", "|{b}|", "{c}/{t}", "({p}%)"], 50, 100, 50.0,
1106+
"Loading")
11061107
assert "Loading" in formatted
11071108
assert "50" in formatted
11081109
assert "100" in formatted

0 commit comments

Comments
 (0)