Skip to content

Commit d030ef1

Browse files
authored
Addressed bug in Rich and made it unnecessary to pass a console into print_to(). (#1596)
1 parent 0abcfe9 commit d030ef1

5 files changed

Lines changed: 247 additions & 120 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ prompt is displayed.
7979
- **max_column_completion_results**: (int) the maximum number of completion results to
8080
display in a single column
8181

82+
## 3.4.0 (TBD)
83+
84+
- Enhancements
85+
- Moved cmd2-specific printing logic from `Cmd.print_to()` into `Cmd2BaseConsole.print()` and
86+
`Cmd2BaseConsole.log()`. This removes need to pass a console object to `Cmd.print_to()`.
87+
- Addressed a bug in `rich.console.Console` where complex renderables (like `Table` and `Rule`)
88+
may not receive formatting settings passed to `console.print()` and `console.log()`.
89+
90+
- Breaking Changes
91+
- Renamed the `destination` parameter of `Cmd.print_to()` back to `file` since you can no longer
92+
pass in a console.
93+
8294
## 3.3.0 (March 1, 2026)
8395

8496
- Enhancements

cmd2/cmd2.py

Lines changed: 44 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@
8484
from prompt_toolkit.patch_stdout import patch_stdout
8585
from prompt_toolkit.shortcuts import CompleteStyle, PromptSession, set_title
8686
from rich.console import (
87-
Console,
8887
Group,
8988
RenderableType,
9089
)
@@ -1320,66 +1319,30 @@ def visible_prompt(self) -> str:
13201319

13211320
def print_to(
13221321
self,
1323-
destination: IO[str] | Cmd2BaseConsole,
1322+
file: IO[str],
13241323
*objects: Any,
13251324
sep: str = " ",
13261325
end: str = "\n",
13271326
style: StyleType | None = None,
1328-
soft_wrap: bool | None = None,
1329-
emoji: bool | None = None,
1330-
markup: bool | None = None,
1331-
highlight: bool | None = None,
1327+
soft_wrap: bool = True,
1328+
emoji: bool = False,
1329+
markup: bool = False,
1330+
highlight: bool = False,
13321331
rich_print_kwargs: RichPrintKwargs | None = None,
13331332
**kwargs: Any, # noqa: ARG002
13341333
) -> None:
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
1334+
"""Print objects to a given file stream.
13621335
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:
1336+
This method is configured for general-purpose printing. By default, it enables
1337+
soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting.
1338+
These defaults can be overridden by passing explicit keyword arguments.
13661339
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``.
1340+
:param file: file stream being written to
13771341
:param objects: objects to print
13781342
:param sep: string to write between printed text. Defaults to " ".
13791343
:param end: string to write at end of printed text. Defaults to a newline.
13801344
:param style: optional style to apply to output
1381-
:param soft_wrap: Enable soft wrap mode. Defaults to None.
1382-
If None, the destination console's default behavior is used.
1345+
:param soft_wrap: Enable soft wrap mode. Defaults to True.
13831346
If True, text that doesn't fit will run on to the following line,
13841347
just like with print(). This is useful for raw text and logs.
13851348
If False, Rich wraps text to fit the terminal width.
@@ -1388,44 +1351,23 @@ def print_to(
13881351
For example, when soft_wrap is True Panels truncate text
13891352
which is wider than the terminal.
13901353
:param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
1391-
corresponding Unicode characters. Defaults to None.
1392-
If None, the destination console's default behavior is used.
1354+
corresponding Unicode characters. Defaults to False.
13931355
:param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold])
1394-
as styled output. Defaults to None.
1395-
If None, the destination console's default behavior is used.
1356+
as styled output. Defaults to False.
13961357
:param highlight: If True, Rich will automatically apply highlighting to elements within
13971358
strings, such as common Python data types like numbers, booleans, or None.
13981359
This is particularly useful when pretty printing objects like lists and
1399-
dictionaries to display them in color. Defaults to None.
1400-
If None, the destination console's default behavior is used.
1360+
dictionaries to display them in color. Defaults to False.
14011361
:param rich_print_kwargs: optional additional keyword arguments to pass to Rich's Console.print().
14021362
:param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
14031363
method and still call `super()` without encountering unexpected keyword argument errors.
14041364
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``.
14071365
1366+
See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
14081367
"""
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-
1424-
prepared_objects = ru.prepare_objects_for_rendering(*objects)
1425-
14261368
try:
1427-
console.print(
1428-
*prepared_objects,
1369+
Cmd2BaseConsole(file=file).print(
1370+
*objects,
14291371
sep=sep,
14301372
end=end,
14311373
style=style,
@@ -1441,7 +1383,7 @@ def print_to(
14411383
# writing. If you would like your application to print a
14421384
# warning message, then set the broken_pipe_warning attribute
14431385
# to the message you want printed.
1444-
if self.broken_pipe_warning and console.file != sys.stderr:
1386+
if self.broken_pipe_warning and file != sys.stderr:
14451387
Cmd2GeneralConsole(file=sys.stderr).print(self.broken_pipe_warning)
14461388

14471389
def poutput(
@@ -1450,10 +1392,10 @@ def poutput(
14501392
sep: str = " ",
14511393
end: str = "\n",
14521394
style: StyleType | None = None,
1453-
soft_wrap: bool | None = None,
1454-
emoji: bool | None = None,
1455-
markup: bool | None = None,
1456-
highlight: bool | None = None,
1395+
soft_wrap: bool = True,
1396+
emoji: bool = False,
1397+
markup: bool = False,
1398+
highlight: bool = False,
14571399
rich_print_kwargs: RichPrintKwargs | None = None,
14581400
**kwargs: Any, # noqa: ARG002
14591401
) -> None:
@@ -1480,10 +1422,10 @@ def perror(
14801422
sep: str = " ",
14811423
end: str = "\n",
14821424
style: StyleType | None = Cmd2Style.ERROR,
1483-
soft_wrap: bool | None = None,
1484-
emoji: bool | None = None,
1485-
markup: bool | None = None,
1486-
highlight: bool | None = None,
1425+
soft_wrap: bool = True,
1426+
emoji: bool = False,
1427+
markup: bool = False,
1428+
highlight: bool = False,
14871429
rich_print_kwargs: RichPrintKwargs | None = None,
14881430
**kwargs: Any, # noqa: ARG002
14891431
) -> None:
@@ -1511,10 +1453,10 @@ def psuccess(
15111453
*objects: Any,
15121454
sep: str = " ",
15131455
end: str = "\n",
1514-
soft_wrap: bool | None = None,
1515-
emoji: bool | None = None,
1516-
markup: bool | None = None,
1517-
highlight: bool | None = None,
1456+
soft_wrap: bool = True,
1457+
emoji: bool = False,
1458+
markup: bool = False,
1459+
highlight: bool = False,
15181460
rich_print_kwargs: RichPrintKwargs | None = None,
15191461
**kwargs: Any, # noqa: ARG002
15201462
) -> None:
@@ -1539,10 +1481,10 @@ def pwarning(
15391481
*objects: Any,
15401482
sep: str = " ",
15411483
end: str = "\n",
1542-
soft_wrap: bool | None = None,
1543-
emoji: bool | None = None,
1544-
markup: bool | None = None,
1545-
highlight: bool | None = None,
1484+
soft_wrap: bool = True,
1485+
emoji: bool = False,
1486+
markup: bool = False,
1487+
highlight: bool = False,
15461488
rich_print_kwargs: RichPrintKwargs | None = None,
15471489
**kwargs: Any, # noqa: ARG002
15481490
) -> None:
@@ -1633,10 +1575,10 @@ def pfeedback(
16331575
sep: str = " ",
16341576
end: str = "\n",
16351577
style: StyleType | None = None,
1636-
soft_wrap: bool | None = None,
1637-
emoji: bool | None = None,
1638-
markup: bool | None = None,
1639-
highlight: bool | None = None,
1578+
soft_wrap: bool = True,
1579+
emoji: bool = False,
1580+
markup: bool = False,
1581+
highlight: bool = False,
16401582
rich_print_kwargs: RichPrintKwargs | None = None,
16411583
**kwargs: Any, # noqa: ARG002
16421584
) -> None:
@@ -1681,9 +1623,9 @@ def ppaged(
16811623
style: StyleType | None = None,
16821624
chop: bool = False,
16831625
soft_wrap: bool = True,
1684-
emoji: bool | None = None,
1685-
markup: bool | None = None,
1686-
highlight: bool | None = None,
1626+
emoji: bool = False,
1627+
markup: bool = False,
1628+
highlight: bool = False,
16871629
rich_print_kwargs: RichPrintKwargs | None = None,
16881630
**kwargs: Any, # noqa: ARG002
16891631
) -> None:
@@ -1725,10 +1667,9 @@ def ppaged(
17251667
soft_wrap = True
17261668

17271669
# Generate the bytes to send to the pager
1728-
console = Cmd2GeneralConsole(file=self.stdout)
1670+
console = Cmd2BaseConsole(file=self.stdout)
17291671
with console.capture() as capture:
1730-
self.print_to(
1731-
console,
1672+
console.print(
17321673
*objects,
17331674
sep=sep,
17341675
end=end,
@@ -2535,8 +2476,7 @@ def complete(
25352476

25362477
console = Cmd2GeneralConsole(file=self.stdout)
25372478
with console.capture() as capture:
2538-
self.print_to(
2539-
console,
2479+
console.print(
25402480
err_str,
25412481
style=Cmd2Style.ERROR if ex.apply_style else "",
25422482
end=end,

0 commit comments

Comments
 (0)