6565)
6666
6767import rich .box
68- from rich .console import Group , RenderableType
68+ from rich .console import (
69+ Console ,
70+ Group ,
71+ RenderableType ,
72+ )
6973from rich .highlighter import ReprHighlighter
7074from rich .rule import Rule
7175from rich .style import Style , StyleType
133137 shlex_split ,
134138)
135139from .rich_utils import (
140+ Cmd2BaseConsole ,
136141 Cmd2ExceptionConsole ,
137142 Cmd2GeneralConsole ,
138143 RichPrintKwargs ,
165170
166171# Set up readline
167172if rl_type == RlType .NONE : # pragma: no cover
168- Cmd2GeneralConsole (sys .stderr ).print (rl_warning , style = Cmd2Style .WARNING )
173+ Cmd2GeneralConsole (file = sys .stderr ).print (rl_warning , style = Cmd2Style .WARNING )
169174else :
170175 from .rl_utils import (
171176 readline ,
@@ -1247,30 +1252,66 @@ def visible_prompt(self) -> str:
12471252
12481253 def print_to (
12491254 self ,
1250- file : IO [str ],
1255+ destination : IO [str ] | Cmd2BaseConsole ,
12511256 * objects : Any ,
12521257 sep : str = " " ,
12531258 end : str = "\n " ,
12541259 style : StyleType | None = None ,
1255- soft_wrap : bool = True ,
1256- emoji : bool = False ,
1257- markup : bool = False ,
1258- highlight : bool = False ,
1260+ soft_wrap : bool | None = None ,
1261+ emoji : bool | None = None ,
1262+ markup : bool | None = None ,
1263+ highlight : bool | None = None ,
12591264 rich_print_kwargs : RichPrintKwargs | None = None ,
12601265 ** kwargs : Any , # noqa: ARG002
12611266 ) -> None :
1262- """Print objects to a given file stream.
1267+ """Print objects to a given destination (file stream or cmd2 console).
1268+
1269+ If ``destination`` is a file-like object, it is wrapped in a ``Cmd2GeneralConsole``
1270+ which is configured for general-purpose printing. By default, it enables soft wrap and
1271+ disables Rich's automatic detection for markup, emoji, and highlighting. These defaults
1272+ can be overridden by passing explicit keyword arguments.
1273+
1274+ If ``destination`` is a ``Cmd2BaseConsole``, the console's default settings for
1275+ soft wrap, markup, emoji, and highlighting are used unless overridden by passing
1276+ explicit keyword arguments.
1277+
1278+ See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
1279+
1280+ **Why use this method instead of console.print()?**
1281+
1282+ This method calls ``cmd2.rich_utils.prepare_objects_for_rendering()`` on the objects
1283+ being printed. This ensures that strings containing ANSI style sequences are converted
1284+ to Rich Text objects, so that Rich can correctly calculate their display width when
1285+ printing.
1286+
1287+ Example:
1288+ ```py
1289+ with console.capture() as capture:
1290+ self.print_to(console, some_ansi_styled_string)
1291+ ```
1292+
1293+ !!! note
12631294
1264- This method is configured for general-purpose printing. By default, it enables
1265- soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting .
1266- These defaults can be overridden by passing explicit keyword arguments.
1295+ To ensure consistent behavior, this method requires a file-like object or
1296+ an instance of ``Cmd2BaseConsole`` .
1297+ Consoles not derived from ``Cmd2BaseConsole`` are disallowed because:
12671298
1268- :param file: file stream being written to
1299+ 1. **Style Control**: They ignore the global ``ALLOW_STYLE`` setting.
1300+ 2. **Theming**: They do not respect the application-wide ``APP_THEME``.
1301+ 3. **Error Handling**: They trigger a ``SystemExit`` on broken pipes.
1302+ ``Cmd2BaseConsole`` instead raises a catchable ``BrokenPipeError``,
1303+ ensuring the CLI application remains alive if a pipe is closed.
1304+
1305+ :param destination: The output target. File-like objects are automatically
1306+ wrapped in a ``Cmd2GeneralConsole`` to ensure they respect
1307+ cmd2 global settings; otherwise, this must be an
1308+ instance of ``Cmd2BaseConsole``.
12691309 :param objects: objects to print
12701310 :param sep: string to write between printed text. Defaults to " ".
12711311 :param end: string to write at end of printed text. Defaults to a newline.
12721312 :param style: optional style to apply to output
1273- :param soft_wrap: Enable soft wrap mode. Defaults to True.
1313+ :param soft_wrap: Enable soft wrap mode. Defaults to None.
1314+ If None, the destination console's default behavior is used.
12741315 If True, text that doesn't fit will run on to the following line,
12751316 just like with print(). This is useful for raw text and logs.
12761317 If False, Rich wraps text to fit the terminal width.
@@ -1279,24 +1320,43 @@ def print_to(
12791320 For example, when soft_wrap is True Panels truncate text
12801321 which is wider than the terminal.
12811322 :param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
1282- corresponding Unicode characters. Defaults to False.
1323+ corresponding Unicode characters. Defaults to None.
1324+ If None, the destination console's default behavior is used.
12831325 :param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold])
1284- as styled output. Defaults to False.
1326+ as styled output. Defaults to None.
1327+ If None, the destination console's default behavior is used.
12851328 :param highlight: If True, Rich will automatically apply highlighting to elements within
12861329 strings, such as common Python data types like numbers, booleans, or None.
12871330 This is particularly useful when pretty printing objects like lists and
1288- dictionaries to display them in color. Defaults to False.
1331+ dictionaries to display them in color. Defaults to None.
1332+ If None, the destination console's default behavior is used.
12891333 :param rich_print_kwargs: optional additional keyword arguments to pass to Rich's Console.print().
12901334 :param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
12911335 method and still call `super()` without encountering unexpected keyword argument errors.
12921336 These arguments are not passed to Rich's Console.print().
1337+ :raises TypeError: If ``destination`` is a non-cmd2 ``Console`` instance that
1338+ does not derive from ``Cmd2BaseConsole``.
12931339
1294- See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
12951340 """
1341+ if isinstance (destination , Console ):
1342+ if not isinstance (destination , Cmd2BaseConsole ):
1343+ # Explicitly reject non-cmd2 consoles to ensure safe behavior
1344+ raise TypeError (
1345+ f"destination must be a 'Cmd2BaseConsole' or a file-like object, "
1346+ f"not a non-cmd2 '{ type (destination ).__name__ } '. "
1347+ "Consoles not derived from 'Cmd2BaseConsole' bypass cmd2's "
1348+ "'ALLOW_STYLE' logic, 'APP_THEME' settings, and trigger 'SystemExit' "
1349+ "on broken pipes."
1350+ )
1351+ console = destination
1352+ else :
1353+ # It's a file-like object (e.g., sys.stdout, StringIO)
1354+ console = Cmd2GeneralConsole (file = destination )
1355+
12961356 prepared_objects = ru .prepare_objects_for_rendering (* objects )
12971357
12981358 try :
1299- Cmd2GeneralConsole ( file ) .print (
1359+ console .print (
13001360 * prepared_objects ,
13011361 sep = sep ,
13021362 end = end ,
@@ -1313,19 +1373,19 @@ def print_to(
13131373 # writing. If you would like your application to print a
13141374 # warning message, then set the broken_pipe_warning attribute
13151375 # to the message you want printed.
1316- if self .broken_pipe_warning and file != sys .stderr :
1317- Cmd2GeneralConsole (sys .stderr ).print (self .broken_pipe_warning )
1376+ if self .broken_pipe_warning and console . file != sys .stderr :
1377+ Cmd2GeneralConsole (file = sys .stderr ).print (self .broken_pipe_warning )
13181378
13191379 def poutput (
13201380 self ,
13211381 * objects : Any ,
13221382 sep : str = " " ,
13231383 end : str = "\n " ,
13241384 style : StyleType | None = None ,
1325- soft_wrap : bool = True ,
1326- emoji : bool = False ,
1327- markup : bool = False ,
1328- highlight : bool = False ,
1385+ soft_wrap : bool | None = None ,
1386+ emoji : bool | None = None ,
1387+ markup : bool | None = None ,
1388+ highlight : bool | None = None ,
13291389 rich_print_kwargs : RichPrintKwargs | None = None ,
13301390 ** kwargs : Any , # noqa: ARG002
13311391 ) -> None :
@@ -1352,10 +1412,10 @@ def perror(
13521412 sep : str = " " ,
13531413 end : str = "\n " ,
13541414 style : StyleType | None = Cmd2Style .ERROR ,
1355- soft_wrap : bool = True ,
1356- emoji : bool = False ,
1357- markup : bool = False ,
1358- highlight : bool = False ,
1415+ soft_wrap : bool | None = None ,
1416+ emoji : bool | None = None ,
1417+ markup : bool | None = None ,
1418+ highlight : bool | None = None ,
13591419 rich_print_kwargs : RichPrintKwargs | None = None ,
13601420 ** kwargs : Any , # noqa: ARG002
13611421 ) -> None :
@@ -1383,10 +1443,10 @@ def psuccess(
13831443 * objects : Any ,
13841444 sep : str = " " ,
13851445 end : str = "\n " ,
1386- soft_wrap : bool = True ,
1387- emoji : bool = False ,
1388- markup : bool = False ,
1389- highlight : bool = False ,
1446+ soft_wrap : bool | None = None ,
1447+ emoji : bool | None = None ,
1448+ markup : bool | None = None ,
1449+ highlight : bool | None = None ,
13901450 rich_print_kwargs : RichPrintKwargs | None = None ,
13911451 ** kwargs : Any , # noqa: ARG002
13921452 ) -> None :
@@ -1411,10 +1471,10 @@ def pwarning(
14111471 * objects : Any ,
14121472 sep : str = " " ,
14131473 end : str = "\n " ,
1414- soft_wrap : bool = True ,
1415- emoji : bool = False ,
1416- markup : bool = False ,
1417- highlight : bool = False ,
1474+ soft_wrap : bool | None = None ,
1475+ emoji : bool | None = None ,
1476+ markup : bool | None = None ,
1477+ highlight : bool | None = None ,
14181478 rich_print_kwargs : RichPrintKwargs | None = None ,
14191479 ** kwargs : Any , # noqa: ARG002
14201480 ) -> None :
@@ -1447,7 +1507,7 @@ def pexcept(
14471507 :param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
14481508 method and still call `super()` without encountering unexpected keyword argument errors.
14491509 """
1450- console = Cmd2ExceptionConsole (sys .stderr )
1510+ console = Cmd2ExceptionConsole (file = sys .stderr )
14511511
14521512 # Only print a traceback if we're in debug mode and one exists.
14531513 if self .debug and sys .exc_info () != (None , None , None ):
@@ -1494,10 +1554,10 @@ def pfeedback(
14941554 sep : str = " " ,
14951555 end : str = "\n " ,
14961556 style : StyleType | None = None ,
1497- soft_wrap : bool = True ,
1498- emoji : bool = False ,
1499- markup : bool = False ,
1500- highlight : bool = False ,
1557+ soft_wrap : bool | None = None ,
1558+ emoji : bool | None = None ,
1559+ markup : bool | None = None ,
1560+ highlight : bool | None = None ,
15011561 rich_print_kwargs : RichPrintKwargs | None = None ,
15021562 ** kwargs : Any , # noqa: ARG002
15031563 ) -> None :
@@ -1542,9 +1602,9 @@ def ppaged(
15421602 style : StyleType | None = None ,
15431603 chop : bool = False ,
15441604 soft_wrap : bool = True ,
1545- emoji : bool = False ,
1546- markup : bool = False ,
1547- highlight : bool = False ,
1605+ emoji : bool | None = None ,
1606+ markup : bool | None = None ,
1607+ highlight : bool | None = None ,
15481608 rich_print_kwargs : RichPrintKwargs | None = None ,
15491609 ** kwargs : Any , # noqa: ARG002
15501610 ) -> None :
@@ -1581,17 +1641,16 @@ def ppaged(
15811641
15821642 # Check if we are outputting to a pager.
15831643 if functional_terminal and can_block :
1584- prepared_objects = ru .prepare_objects_for_rendering (* objects )
1585-
15861644 # Chopping overrides soft_wrap
15871645 if chop :
15881646 soft_wrap = True
15891647
15901648 # Generate the bytes to send to the pager
1591- console = Cmd2GeneralConsole (self .stdout )
1649+ console = Cmd2GeneralConsole (file = self .stdout )
15921650 with console .capture () as capture :
1593- console .print (
1594- * prepared_objects ,
1651+ self .print_to (
1652+ console ,
1653+ * objects ,
15951654 sep = sep ,
15961655 end = end ,
15971656 style = style ,
0 commit comments