diff --git a/uaclient/cli/formatter.py b/uaclient/cli/formatter.py index 9e3b1b3e32..c7fd9052bf 100644 --- a/uaclient/cli/formatter.py +++ b/uaclient/cli/formatter.py @@ -2,12 +2,17 @@ import re import sys import textwrap -from typing import Any, List, Optional +from typing import Any, Dict, List, Optional # noqa: F401 from uaclient.config import UAConfig from uaclient.messages import TxtColor COLOR_FORMATTING_PATTERN = r"\033\[.*?m" +UTF8_ALTERNATIVES = { + "—": "-", + "✘": "x", + "✔": "*", +} # type: Dict[str, str] # Class attributes and methods so we don't need singletons or globals for this @@ -56,6 +61,19 @@ def _get_default_length(): return 999 +def process_formatter_config(text: str) -> str: + output = text + if not ProOutputFormatterConfig.use_color: + output = re.sub(COLOR_FORMATTING_PATTERN, "", text) + + if not ProOutputFormatterConfig.use_utf8: + for char, alternative in UTF8_ALTERNATIVES.items(): + output = output.replace(char, alternative) + output = output.encode("ascii", "ignore").decode() + + return output + + # We can't rely on textwrap because of the len_no_color function # Textwrap is using a magic regex instead def wrap_text(text: str, max_width: int) -> List[str]: @@ -151,7 +169,8 @@ def to_string(self, line_length: Optional[int] = None) -> str: for row in rows: output += self._fill_row(row) output += "\n" - return output + + return process_formatter_config(output) def _get_line_length(self) -> int: return sum(self.column_sizes) + (len(self.column_sizes) - 1) * len( @@ -227,7 +246,7 @@ def to_string(self, line_length: Optional[int] = None) -> str: item_str, self.INDENT_CHAR * self.INDENT_SIZE ) - return output + return process_formatter_config(output) class SuggestionBlock(Block): diff --git a/uaclient/cli/tests/test_cli_formatter.py b/uaclient/cli/tests/test_cli_formatter.py index 276b9c9683..f071dede1b 100644 --- a/uaclient/cli/tests/test_cli_formatter.py +++ b/uaclient/cli/tests/test_cli_formatter.py @@ -200,7 +200,8 @@ def test_wrap_last_column(self, _m_is_tty): ] @mock.patch(M_PATH + "sys.stdout.isatty", return_value=True) - def test_to_string_wraps_to_length(self, _m_is_tty): + def test_to_string_wraps_to_length(self, _m_is_tty, FakeConfig): + POFC.init(FakeConfig()) table = Table( ["header1", "h2", "h3", "h4"], [ @@ -226,8 +227,9 @@ def test_to_string_wraps_to_length(self, _m_is_tty): return_value=mock.MagicMock(columns=40), ) def test_to_string_wraps_to_terminal_size( - self, _m_terminal_size, _m_is_tty + self, _m_terminal_size, _m_is_tty, FakeConfig ): + POFC.init(FakeConfig()) table = Table( ["header1", "h2", "h3", "h4"], [ @@ -253,7 +255,10 @@ def test_to_string_wraps_to_terminal_size( M_PATH + "os.get_terminal_size", side_effect=OSError(), ) - def test_to_string_no_wrap_if_no_tty(self, _m_terminal_size, _m_is_tty): + def test_to_string_no_wrap_if_no_tty( + self, _m_terminal_size, _m_is_tty, FakeConfig + ): + POFC.init(FakeConfig()) table = Table( ["header1", "h2", "h3", "h4"], [ @@ -269,7 +274,7 @@ def test_to_string_no_wrap_if_no_tty(self, _m_terminal_size, _m_is_tty): ) assert table.to_string() == textwrap.dedent( """\ - \x1b[1mheader1 h2 h3 h4\x1b[0m + header1 h2 h3 h4 a bc de f b de fg wow this is a really big string of datawow this is a really big string of datawow this is a really big string of data c fg hijkl m @@ -284,8 +289,9 @@ class TestBlock: side_effect=OSError(), ) def test_indents_and_wraps_content_when_len_specified( - self, _m_terminal_size, _m_is_tty + self, _m_terminal_size, _m_is_tty, FakeConfig ): + POFC.init(FakeConfig()) block = Block( title="Example Title", content=[ @@ -318,10 +324,10 @@ def test_indents_and_wraps_content_when_len_specified( ) assert block.to_string() == textwrap.dedent( """\ - \x1b[1m\x1b[37mExample Title\x1b[0m + Example Title Smaller content line A slightly bigger line which needs to be wrapped to fit the screen - \x1b[1m\x1b[37mInner block\x1b[0m + Inner block Another small content line Another slightly bigger line which needs to be wrapped to fit the screen 1 Table bigger last column which needs to be wrapped to fit the screen @@ -331,11 +337,11 @@ def test_indents_and_wraps_content_when_len_specified( ) assert block.to_string(line_length=40) == textwrap.dedent( """\ - \x1b[1m\x1b[37mExample Title\x1b[0m + Example Title Smaller content line A slightly bigger line which needs to be wrapped to fit the screen - \x1b[1m\x1b[37mInner block\x1b[0m + Inner block Another small content line Another slightly bigger line which needs to be wrapped to fit @@ -359,7 +365,7 @@ def test_suggestions_can_be_disabled(self, FakeConfig): POFC.init(FakeConfig()) assert suggestion_block.to_string() == textwrap.dedent( """\ - \x1b[1m\x1b[37mSuggestion\x1b[0m + Suggestion Some content """ )