diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 78ec31ef9..c42ec40c5 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -1198,22 +1198,39 @@ def print_to( end: str = "\n", style: StyleType | None = None, soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: """Print objects to a given file stream. + This method is configured for general-purpose printing. By default, it enables + soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting. + These defaults can be overridden by passing explicit keyword arguments. + :param file: file stream being written to :param objects: objects to print :param sep: string to write between printed text. Defaults to " ". :param end: string to write at end of printed text. Defaults to a newline. :param style: optional style to apply to output - :param soft_wrap: Enable soft wrap mode. If True, lines of text will not be word-wrapped or cropped to - fit the terminal width. Defaults to True. + :param soft_wrap: Enable soft wrap mode. If True, lines of text will not be + word-wrapped or cropped to fit the terminal width. Defaults to True. + :param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their + corresponding Unicode characters. Defaults to False. + :param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold]) + as styled output. Defaults to False. + :param highlight: If True, Rich will automatically apply highlighting to elements within + strings, such as common Python data types like numbers, booleans, or None. + This is particularly useful when pretty printing objects like lists and + dictionaries to display them in color. Defaults to False. :param rich_print_kwargs: optional additional keyword arguments to pass to Rich's Console.print(). :param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this method and still call `super()` without encountering unexpected keyword argument errors. These arguments are not passed to Rich's Console.print(). + + See the Rich documentation for more details on emoji codes, markup tags, and highlighting. """ prepared_objects = ru.prepare_objects_for_rendering(*objects) @@ -1224,6 +1241,9 @@ def print_to( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, **(rich_print_kwargs if rich_print_kwargs is not None else {}), ) except BrokenPipeError: @@ -1242,6 +1262,9 @@ def poutput( end: str = "\n", style: StyleType | None = None, soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: @@ -1256,6 +1279,9 @@ def poutput( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) @@ -1266,6 +1292,9 @@ def perror( end: str = "\n", style: StyleType | None = Cmd2Style.ERROR, soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: @@ -1282,6 +1311,9 @@ def perror( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) @@ -1291,6 +1323,9 @@ def psuccess( sep: str = " ", end: str = "\n", soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: @@ -1304,6 +1339,9 @@ def psuccess( end=end, style=Cmd2Style.SUCCESS, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) @@ -1313,6 +1351,9 @@ def pwarning( sep: str = " ", end: str = "\n", soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: @@ -1326,6 +1367,9 @@ def pwarning( end=end, style=Cmd2Style.WARNING, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) @@ -1390,6 +1434,9 @@ def pfeedback( end: str = "\n", style: StyleType | None = None, soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: @@ -1408,6 +1455,9 @@ def pfeedback( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) else: @@ -1417,6 +1467,9 @@ def pfeedback( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) @@ -1428,6 +1481,9 @@ def ppaged( style: StyleType | None = None, chop: bool = False, soft_wrap: bool = True, + emoji: bool = False, + markup: bool = False, + highlight: bool = False, rich_print_kwargs: RichPrintKwargs | None = None, **kwargs: Any, # noqa: ARG002 ) -> None: @@ -1479,6 +1535,9 @@ def ppaged( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, **(rich_print_kwargs if rich_print_kwargs is not None else {}), ) output_bytes = capture.get().encode('utf-8', 'replace') @@ -1503,6 +1562,9 @@ def ppaged( end=end, style=style, soft_wrap=soft_wrap, + emoji=emoji, + markup=markup, + highlight=highlight, rich_print_kwargs=rich_print_kwargs, ) diff --git a/cmd2/rich_utils.py b/cmd2/rich_utils.py index 83fbb3e48..d8bf2f337 100644 --- a/cmd2/rich_utils.py +++ b/cmd2/rich_utils.py @@ -110,9 +110,6 @@ class RichPrintKwargs(TypedDict, total=False): justify: JustifyMethod | None overflow: OverflowMethod | None no_wrap: bool | None - markup: bool | None - emoji: bool | None - highlight: bool | None width: int | None height: int | None crop: bool @@ -216,9 +213,11 @@ def __init__(self, file: IO[str] | None = None) -> None: :param file: optional file object where the console should write to. Defaults to sys.stdout. """ - # Disable Rich's automatic detection for markup, emoji, and highlighting. - # rich-argparse does markup and highlighting without involving the console - # so these won't affect its internal functionality. + # Since this console is used to print error messages which may not have + # been pre-formatted by rich-argparse, disable Rich's automatic detection + # for markup, emoji, and highlighting. rich-argparse does markup and + # highlighting without involving the console so these won't affect its + # internal functionality. super().__init__( file=file, markup=False, @@ -236,7 +235,7 @@ class Cmd2ExceptionConsole(Cmd2BaseConsole): def console_width() -> int: """Return the width of the console.""" - return Cmd2BaseConsole().width + return Console().width def rich_text_to_string(text: Text) -> str: diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index efbf7a4b9..82497b723 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -2136,33 +2136,21 @@ def test_poutput_ansi_terminal(outsim_app) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_poutput_highlight(outsim_app): - rich_print_kwargs = RichPrintKwargs(highlight=True) - outsim_app.poutput( - "My IP Address is 192.168.1.100.", - rich_print_kwargs=rich_print_kwargs, - ) + outsim_app.poutput("My IP Address is 192.168.1.100.", highlight=True) out = outsim_app.stdout.getvalue() assert out == "My IP Address is \x1b[1;92m192.168.1.100\x1b[0m.\n" @with_ansi_style(ru.AllowStyle.ALWAYS) def test_poutput_markup(outsim_app): - rich_print_kwargs = RichPrintKwargs(markup=True) - outsim_app.poutput( - "The leaves are [green]green[/green].", - rich_print_kwargs=rich_print_kwargs, - ) + outsim_app.poutput("The leaves are [green]green[/green].", markup=True) out = outsim_app.stdout.getvalue() assert out == "The leaves are \x1b[32mgreen\x1b[0m.\n" @with_ansi_style(ru.AllowStyle.ALWAYS) def test_poutput_emoji(outsim_app): - rich_print_kwargs = RichPrintKwargs(emoji=True) - outsim_app.poutput( - "Look at the emoji :1234:.", - rich_print_kwargs=rich_print_kwargs, - ) + outsim_app.poutput("Look at the emoji :1234:.", emoji=True) out = outsim_app.stdout.getvalue() assert out == "Look at the emoji 🔢.\n" @@ -2196,13 +2184,9 @@ def test_poutput_no_wrap_and_overflow(outsim_app): @with_ansi_style(ru.AllowStyle.ALWAYS) def test_poutput_pretty_print(outsim_app): """Test that cmd2 passes objects through so they can be pretty-printed when highlighting is enabled.""" - rich_print_kwargs = RichPrintKwargs(highlight=True) dictionary = {1: 'hello', 2: 'person', 3: 'who', 4: 'codes'} - outsim_app.poutput( - dictionary, - rich_print_kwargs=rich_print_kwargs, - ) + outsim_app.poutput(dictionary, highlight=True) out = outsim_app.stdout.getvalue() assert out.startswith("\x1b[1m{\x1b[0m\x1b[1;36m1\x1b[0m: \x1b[32m'hello'\x1b[0m") @@ -2214,9 +2198,6 @@ def test_poutput_all_keyword_args(outsim_app): justify="center", overflow="ellipsis", no_wrap=True, - markup=True, - emoji=True, - highlight=True, width=40, height=50, crop=False, diff --git a/tests/test_rich_utils.py b/tests/test_rich_utils.py index ee87ef83f..5f6a07981 100644 --- a/tests/test_rich_utils.py +++ b/tests/test_rich_utils.py @@ -31,10 +31,9 @@ def test_cmd2_base_console() -> None: def test_indented_text() -> None: - console = ru.Cmd2GeneralConsole() + console = Console(width=20) # With an indention of 10, text will be evenly split across two lines. - console.width = 20 text = "A" * 20 level = 10 indented_text = ru.indent(text, level) @@ -50,7 +49,7 @@ def test_indented_text() -> None: def test_indented_table() -> None: - console = ru.Cmd2GeneralConsole() + console = Console() level = 2 table = Table("Column", box=rich.box.ASCII)