From 1aeea8d6cee9803512de641d8e703adc2dfdff69 Mon Sep 17 00:00:00 2001 From: Pavel Dmytrenko Date: Wed, 18 Sep 2024 21:33:03 +0300 Subject: [PATCH] ellipsis: handle edge case when terminal is too narrow Terminal size should be sufficient to include: frame_width + timer_width + ellipsis_width --- tests/test_ellipsis.py | 16 ++++++++++++++++ yaspin/core.py | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/tests/test_ellipsis.py b/tests/test_ellipsis.py index 0352ab8..35c1057 100644 --- a/tests/test_ellipsis.py +++ b/tests/test_ellipsis.py @@ -1,7 +1,10 @@ import shutil +import threading import time from unittest.mock import patch +import pytest + from yaspin import yaspin @@ -32,3 +35,16 @@ def long_running_function(): long_running_function() mock_get_terminal_size.assert_called_once() + + +@patch("shutil.get_terminal_size") +def test_raises_when_term_is_too_small(mock_get_terminal_size): + mock_get_terminal_size.return_value.columns = 10 + sp = yaspin(ellipsis="..." * 5) + + with pytest.raises(ValueError): + # Some low-level trickery is needed here because Yaspin._compose_out() + # is called in a separate thread and the exception is not propagated. + # Pytest indicates that by warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) + sp._stop_spin = threading.Event() + sp._spin() diff --git a/yaspin/core.py b/yaspin/core.py index b6a89bf..4413c76 100644 --- a/yaspin/core.py +++ b/yaspin/core.py @@ -459,6 +459,10 @@ def _compose_out(self, frame: str, mode: Optional[str] = None) -> str: # Truncate max_text_len = self._get_max_text_length(len(frame), len(timer)) + if max_text_len < 1: + raise ValueError( + f"Terminal size {self._terminal_width} is too small to display spinner with the given settings." + ) text = text[:max_text_len] + self._ellipsis if len(text) > max_text_len else text # Colors