Skip to content

Commit 3a032d5

Browse files
committed
improve Spinner class
1 parent b3aac1e commit 3a032d5

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

src/xulbux/console.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,12 +1618,15 @@ class Spinner:
16181618
def __init__(
16191619
self,
16201620
label: Optional[str] = None,
1621-
spinner_format: str = "{l} [b]({a}) ",
1621+
spinner_format: list[str] | tuple[str, ...] = ["{l}", "[b]({a}) "],
1622+
sep: str = " ",
16221623
frames: tuple[str, ...] = ("· ", "·· ", "···", " ··", " ·", " ·", " ··", "···", "·· ", "· "),
16231624
interval: float = 0.2,
16241625
):
1625-
self.animation_format: str
1626-
"""The format string used to render the spinner."""
1626+
self.spinner_format: list[str] | tuple[str, ...]
1627+
"""The format strings used to render the spinner (joined by `sep`)."""
1628+
self.sep: str
1629+
"""The separator string used to join multiple format strings."""
16271630
self.frames: tuple[str, ...]
16281631
"""A tuple of strings representing the animation frames."""
16291632
self.interval: float
@@ -1634,7 +1637,7 @@ def __init__(
16341637
"""Whether the spinner is currently active (intercepting stdout) or not."""
16351638

16361639
self.update_label(label)
1637-
self.set_format(spinner_format)
1640+
self.set_format(spinner_format, sep)
16381641
self.set_frames(frames)
16391642
self.set_interval(interval)
16401643

@@ -1646,18 +1649,26 @@ def __init__(
16461649
self._stop_event: Optional[_threading.Event] = None
16471650
self._animation_thread: Optional[_threading.Thread] = None
16481651

1649-
def set_format(self, animation_format: str) -> None:
1652+
def set_format(self, spinner_format: list[str] | tuple[str, ...], sep: Optional[str] = None) -> None:
16501653
"""Set the format string used to render the spinner.\n
1651-
----------------------------------------------------------------------------------------------
1652-
- `animation_format` -⠀the format string used to render the spinner, containing placeholders:
1653-
* `{label}` `{l}`
1654-
* `{animation}` `{a}`"""
1655-
if not isinstance(animation_format, str):
1656-
raise TypeError(f"The 'animation_format' parameter must be a string, got {type(animation_format)}")
1657-
elif not _COMPILED["animation"].search(animation_format):
1658-
raise ValueError("The 'animation_format' parameter value must contain the '{animation}' or '{a}' placeholder.")
1654+
---------------------------------------------------------------------------------------------
1655+
- `spinner_format` -⠀the format strings used to render the spinner, containing placeholders:
1656+
* `{label}` `{l}`
1657+
* `{animation}` `{a}`
1658+
- `sep` -⠀the separator string used to join multiple format strings"""
1659+
if not isinstance(spinner_format, (list, tuple)):
1660+
raise TypeError(f"The 'spinner_format' parameter must be a list or tuple, got {type(spinner_format)}")
1661+
elif not all(isinstance(fmt, str) for fmt in spinner_format):
1662+
raise TypeError("All elements of the 'spinner_format' parameter must be strings.")
1663+
elif not any(_COMPILED["animation"].search(fmt) for fmt in spinner_format):
1664+
raise ValueError(
1665+
"At least one format string in 'spinner_format' must contain the '{animation}' or '{a}' placeholder."
1666+
)
1667+
if sep is not None and not isinstance(sep, str):
1668+
raise TypeError(f"The 'sep' parameter must be a string or None, got {type(sep)}")
16591669

1660-
self.animation_format = animation_format
1670+
self.spinner_format = spinner_format
1671+
self.sep = sep or self.sep
16611672

16621673
def set_frames(self, frames: tuple[str, ...]) -> None:
16631674
"""Set the frames used for the spinner animation.\n
@@ -1760,11 +1771,15 @@ def _animation_loop(self) -> None:
17601771
self._flush_buffer()
17611772

17621773
frame = FormatCodes.to_ansi(f"{self.frames[self._frame_index % len(self.frames)]}[*]")
1763-
formatted_template = FormatCodes.to_ansi(_COMPILED["label"].sub(self.label or "", self.animation_format))
1764-
final_str = _COMPILED["animation"].sub(frame, formatted_template)
1765-
1766-
self._current_animation_str = final_str
1767-
self._last_line_len = len(final_str)
1774+
formatted = FormatCodes.to_ansi(self.sep.join(
1775+
s for s in ( \
1776+
_COMPILED["animation"].sub(frame, _COMPILED["label"].sub(self.label or "", s))
1777+
for s in self.spinner_format
1778+
) if s
1779+
))
1780+
1781+
self._current_animation_str = formatted
1782+
self._last_line_len = len(formatted)
17681783
self._redraw_display()
17691784
self._frame_index += 1
17701785

0 commit comments

Comments
 (0)