8484from prompt_toolkit .patch_stdout import patch_stdout
8585from prompt_toolkit .shortcuts import CompleteStyle , PromptSession , set_title
8686from rich .console import (
87+ Console ,
8788 Group ,
8889 RenderableType ,
8990)
157158 shlex_split ,
158159)
159160from .rich_utils import (
161+ Cmd2BaseConsole ,
160162 Cmd2ExceptionConsole ,
161163 Cmd2GeneralConsole ,
162164 RichPrintKwargs ,
@@ -1318,30 +1320,66 @@ def visible_prompt(self) -> str:
13181320
13191321 def print_to (
13201322 self ,
1321- file : IO [str ],
1323+ destination : IO [str ] | Cmd2BaseConsole ,
13221324 * objects : Any ,
13231325 sep : str = " " ,
13241326 end : str = "\n " ,
13251327 style : StyleType | None = None ,
1326- soft_wrap : bool = True ,
1327- emoji : bool = False ,
1328- markup : bool = False ,
1329- highlight : bool = False ,
1328+ soft_wrap : bool | None = None ,
1329+ emoji : bool | None = None ,
1330+ markup : bool | None = None ,
1331+ highlight : bool | None = None ,
13301332 rich_print_kwargs : RichPrintKwargs | None = None ,
13311333 ** kwargs : Any , # noqa: ARG002
13321334 ) -> None :
1333- """Print objects to a given file stream.
1335+ """Print objects to a given destination (file stream or cmd2 console).
1336+
1337+ If ``destination`` is a file-like object, it is wrapped in a ``Cmd2GeneralConsole``
1338+ which is configured for general-purpose printing. By default, it enables soft wrap and
1339+ disables Rich's automatic detection for markup, emoji, and highlighting. These defaults
1340+ can be overridden by passing explicit keyword arguments.
1341+
1342+ If ``destination`` is a ``Cmd2BaseConsole``, the console's default settings for
1343+ soft wrap, markup, emoji, and highlighting are used unless overridden by passing
1344+ explicit keyword arguments.
1345+
1346+ See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
1347+
1348+ **Why use this method instead of console.print()?**
1349+
1350+ This method calls ``cmd2.rich_utils.prepare_objects_for_rendering()`` on the objects
1351+ being printed. This ensures that strings containing ANSI style sequences are converted
1352+ to Rich Text objects, so that Rich can correctly calculate their display width when
1353+ printing.
1354+
1355+ Example:
1356+ ```py
1357+ with console.capture() as capture:
1358+ self.print_to(console, some_ansi_styled_string)
1359+ ```
1360+
1361+ !!! note
13341362
1335- This method is configured for general-purpose printing. By default, it enables
1336- soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting .
1337- These defaults can be overridden by passing explicit keyword arguments.
1363+ To ensure consistent behavior, this method requires a file-like object or
1364+ an instance of ``Cmd2BaseConsole`` .
1365+ Consoles not derived from ``Cmd2BaseConsole`` are disallowed because:
13381366
1339- :param file: file stream being written to
1367+ 1. **Style Control**: They ignore the global ``ALLOW_STYLE`` setting.
1368+ 2. **Theming**: They do not respect the application-wide ``APP_THEME``.
1369+ 3. **Error Handling**: They trigger a ``SystemExit`` on broken pipes.
1370+ ``Cmd2BaseConsole`` instead raises a catchable ``BrokenPipeError``,
1371+ ensuring the CLI application remains alive if a pipe is closed.
1372+
1373+ :param destination: The output target. File-like objects are automatically
1374+ wrapped in a ``Cmd2GeneralConsole`` to ensure they respect
1375+ cmd2 global settings; otherwise, this must be an
1376+ instance of ``Cmd2BaseConsole``.
13401377 :param objects: objects to print
13411378 :param sep: string to write between printed text. Defaults to " ".
13421379 :param end: string to write at end of printed text. Defaults to a newline.
13431380 :param style: optional style to apply to output
1344- :param soft_wrap: Enable soft wrap mode. Defaults to True.
1381+ :param soft_wrap: Enable soft wrap mode. Defaults to None.
1382+ If None, the destination console's default behavior is used.
13451383 If True, text that doesn't fit will run on to the following line,
13461384 just like with print(). This is useful for raw text and logs.
13471385 If False, Rich wraps text to fit the terminal width.
@@ -1350,24 +1388,43 @@ def print_to(
13501388 For example, when soft_wrap is True Panels truncate text
13511389 which is wider than the terminal.
13521390 :param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
1353- corresponding Unicode characters. Defaults to False.
1391+ corresponding Unicode characters. Defaults to None.
1392+ If None, the destination console's default behavior is used.
13541393 :param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold])
1355- as styled output. Defaults to False.
1394+ as styled output. Defaults to None.
1395+ If None, the destination console's default behavior is used.
13561396 :param highlight: If True, Rich will automatically apply highlighting to elements within
13571397 strings, such as common Python data types like numbers, booleans, or None.
13581398 This is particularly useful when pretty printing objects like lists and
1359- dictionaries to display them in color. Defaults to False.
1399+ dictionaries to display them in color. Defaults to None.
1400+ If None, the destination console's default behavior is used.
13601401 :param rich_print_kwargs: optional additional keyword arguments to pass to Rich's Console.print().
13611402 :param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
13621403 method and still call `super()` without encountering unexpected keyword argument errors.
13631404 These arguments are not passed to Rich's Console.print().
1405+ :raises TypeError: If ``destination`` is a non-cmd2 ``Console`` instance that
1406+ does not derive from ``Cmd2BaseConsole``.
13641407
1365- See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
13661408 """
1409+ if isinstance (destination , Console ):
1410+ if not isinstance (destination , Cmd2BaseConsole ):
1411+ # Explicitly reject non-cmd2 consoles to ensure safe behavior
1412+ raise TypeError (
1413+ f"destination must be a 'Cmd2BaseConsole' or a file-like object, "
1414+ f"not a non-cmd2 '{ type (destination ).__name__ } '. "
1415+ "Consoles not derived from 'Cmd2BaseConsole' bypass cmd2's "
1416+ "'ALLOW_STYLE' logic, 'APP_THEME' settings, and trigger 'SystemExit' "
1417+ "on broken pipes."
1418+ )
1419+ console = destination
1420+ else :
1421+ # It's a file-like object (e.g., sys.stdout, StringIO)
1422+ console = Cmd2GeneralConsole (file = destination )
1423+
13671424 prepared_objects = ru .prepare_objects_for_rendering (* objects )
13681425
13691426 try :
1370- Cmd2GeneralConsole ( file ) .print (
1427+ console .print (
13711428 * prepared_objects ,
13721429 sep = sep ,
13731430 end = end ,
@@ -1384,19 +1441,19 @@ def print_to(
13841441 # writing. If you would like your application to print a
13851442 # warning message, then set the broken_pipe_warning attribute
13861443 # to the message you want printed.
1387- if self .broken_pipe_warning and file != sys .stderr :
1388- Cmd2GeneralConsole (sys .stderr ).print (self .broken_pipe_warning )
1444+ if self .broken_pipe_warning and console . file != sys .stderr :
1445+ Cmd2GeneralConsole (file = sys .stderr ).print (self .broken_pipe_warning )
13891446
13901447 def poutput (
13911448 self ,
13921449 * objects : Any ,
13931450 sep : str = " " ,
13941451 end : str = "\n " ,
13951452 style : StyleType | None = None ,
1396- soft_wrap : bool = True ,
1397- emoji : bool = False ,
1398- markup : bool = False ,
1399- highlight : bool = False ,
1453+ soft_wrap : bool | None = None ,
1454+ emoji : bool | None = None ,
1455+ markup : bool | None = None ,
1456+ highlight : bool | None = None ,
14001457 rich_print_kwargs : RichPrintKwargs | None = None ,
14011458 ** kwargs : Any , # noqa: ARG002
14021459 ) -> None :
@@ -1423,10 +1480,10 @@ def perror(
14231480 sep : str = " " ,
14241481 end : str = "\n " ,
14251482 style : StyleType | None = Cmd2Style .ERROR ,
1426- soft_wrap : bool = True ,
1427- emoji : bool = False ,
1428- markup : bool = False ,
1429- highlight : bool = False ,
1483+ soft_wrap : bool | None = None ,
1484+ emoji : bool | None = None ,
1485+ markup : bool | None = None ,
1486+ highlight : bool | None = None ,
14301487 rich_print_kwargs : RichPrintKwargs | None = None ,
14311488 ** kwargs : Any , # noqa: ARG002
14321489 ) -> None :
@@ -1454,10 +1511,10 @@ def psuccess(
14541511 * objects : Any ,
14551512 sep : str = " " ,
14561513 end : str = "\n " ,
1457- soft_wrap : bool = True ,
1458- emoji : bool = False ,
1459- markup : bool = False ,
1460- highlight : bool = False ,
1514+ soft_wrap : bool | None = None ,
1515+ emoji : bool | None = None ,
1516+ markup : bool | None = None ,
1517+ highlight : bool | None = None ,
14611518 rich_print_kwargs : RichPrintKwargs | None = None ,
14621519 ** kwargs : Any , # noqa: ARG002
14631520 ) -> None :
@@ -1482,10 +1539,10 @@ def pwarning(
14821539 * objects : Any ,
14831540 sep : str = " " ,
14841541 end : str = "\n " ,
1485- soft_wrap : bool = True ,
1486- emoji : bool = False ,
1487- markup : bool = False ,
1488- highlight : bool = False ,
1542+ soft_wrap : bool | None = None ,
1543+ emoji : bool | None = None ,
1544+ markup : bool | None = None ,
1545+ highlight : bool | None = None ,
14891546 rich_print_kwargs : RichPrintKwargs | None = None ,
14901547 ** kwargs : Any , # noqa: ARG002
14911548 ) -> None :
@@ -1513,7 +1570,7 @@ def format_exception(self, exception: BaseException) -> str:
15131570 :param exception: the exception to be printed.
15141571 :return: a formatted exception string
15151572 """
1516- console = Cmd2ExceptionConsole ()
1573+ console = Cmd2ExceptionConsole (file = sys . stderr )
15171574 with console .capture () as capture :
15181575 # Only print a traceback if we're in debug mode and one exists.
15191576 if self .debug and sys .exc_info () != (None , None , None ):
@@ -1576,10 +1633,10 @@ def pfeedback(
15761633 sep : str = " " ,
15771634 end : str = "\n " ,
15781635 style : StyleType | None = None ,
1579- soft_wrap : bool = True ,
1580- emoji : bool = False ,
1581- markup : bool = False ,
1582- highlight : bool = False ,
1636+ soft_wrap : bool | None = None ,
1637+ emoji : bool | None = None ,
1638+ markup : bool | None = None ,
1639+ highlight : bool | None = None ,
15831640 rich_print_kwargs : RichPrintKwargs | None = None ,
15841641 ** kwargs : Any , # noqa: ARG002
15851642 ) -> None :
@@ -1624,9 +1681,9 @@ def ppaged(
16241681 style : StyleType | None = None ,
16251682 chop : bool = False ,
16261683 soft_wrap : bool = True ,
1627- emoji : bool = False ,
1628- markup : bool = False ,
1629- highlight : bool = False ,
1684+ emoji : bool | None = None ,
1685+ markup : bool | None = None ,
1686+ highlight : bool | None = None ,
16301687 rich_print_kwargs : RichPrintKwargs | None = None ,
16311688 ** kwargs : Any , # noqa: ARG002
16321689 ) -> None :
@@ -1663,17 +1720,16 @@ def ppaged(
16631720
16641721 # Check if we are outputting to a pager.
16651722 if functional_terminal and can_block :
1666- prepared_objects = ru .prepare_objects_for_rendering (* objects )
1667-
16681723 # Chopping overrides soft_wrap
16691724 if chop :
16701725 soft_wrap = True
16711726
16721727 # Generate the bytes to send to the pager
1673- console = Cmd2GeneralConsole (self .stdout )
1728+ console = Cmd2GeneralConsole (file = self .stdout )
16741729 with console .capture () as capture :
1675- console .print (
1676- * prepared_objects ,
1730+ self .print_to (
1731+ console ,
1732+ * objects ,
16771733 sep = sep ,
16781734 end = end ,
16791735 style = style ,
@@ -2477,10 +2533,12 @@ def complete(
24772533 # _NoResultsError completion hints already include a trailing "\n".
24782534 end = "" if isinstance (ex , argparse_completer ._NoResultsError ) else "\n "
24792535
2480- console = ru . Cmd2GeneralConsole ()
2536+ console = Cmd2GeneralConsole (file = self . stdout )
24812537 with console .capture () as capture :
2482- console .print (
2483- Text (err_str , style = Cmd2Style .ERROR if ex .apply_style else "" ),
2538+ self .print_to (
2539+ console ,
2540+ err_str ,
2541+ style = Cmd2Style .ERROR if ex .apply_style else "" ,
24842542 end = end ,
24852543 )
24862544 completion_error = capture .get ()
0 commit comments