Skip to content

Commit 78eb813

Browse files
authored
Addressed bug in Rich and made it unnecessary to pass a console into print_to(). (#1595)
1 parent 25aa3e6 commit 78eb813

File tree

5 files changed

+246
-118
lines changed

5 files changed

+246
-118
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## 3.4.0 (TBD)
2+
3+
- Enhancements
4+
- Moved cmd2-specific printing logic from `Cmd.print_to()` into `Cmd2BaseConsole.print()` and
5+
`Cmd2BaseConsole.log()`. This removes need to pass a console object to `Cmd.print_to()`.
6+
- Addressed a bug in `rich.console.Console` where complex renderables (like `Table` and `Rule`)
7+
may not receive formatting settings passed to `console.print()` and `console.log()`.
8+
9+
- Breaking Changes
10+
- Renamed the `destination` parameter of `Cmd.print_to()` back to `file` since you can no longer
11+
pass in a console.
12+
113
## 3.3.0 (March 1, 2026)
214

315
- Enhancements

cmd2/cmd2.py

Lines changed: 43 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666

6767
import rich.box
6868
from rich.console import (
69-
Console,
7069
Group,
7170
RenderableType,
7271
)
@@ -1252,66 +1251,30 @@ def visible_prompt(self) -> str:
12521251

12531252
def print_to(
12541253
self,
1255-
destination: IO[str] | Cmd2BaseConsole,
1254+
file: IO[str],
12561255
*objects: Any,
12571256
sep: str = " ",
12581257
end: str = "\n",
12591258
style: StyleType | None = None,
1260-
soft_wrap: bool | None = None,
1261-
emoji: bool | None = None,
1262-
markup: bool | None = None,
1263-
highlight: bool | None = None,
1259+
soft_wrap: bool = True,
1260+
emoji: bool = False,
1261+
markup: bool = False,
1262+
highlight: bool = False,
12641263
rich_print_kwargs: RichPrintKwargs | None = None,
12651264
**kwargs: Any, # noqa: ARG002
12661265
) -> None:
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
1294-
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:
1266+
"""Print objects to a given file stream.
12981267
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.
1268+
This method is configured for general-purpose printing. By default, it enables
1269+
soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting.
1270+
These defaults can be overridden by passing explicit keyword arguments.
13041271
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``.
1272+
:param file: file stream being written to
13091273
:param objects: objects to print
13101274
:param sep: string to write between printed text. Defaults to " ".
13111275
:param end: string to write at end of printed text. Defaults to a newline.
13121276
:param style: optional style to apply to output
1313-
:param soft_wrap: Enable soft wrap mode. Defaults to None.
1314-
If None, the destination console's default behavior is used.
1277+
:param soft_wrap: Enable soft wrap mode. Defaults to True.
13151278
If True, text that doesn't fit will run on to the following line,
13161279
just like with print(). This is useful for raw text and logs.
13171280
If False, Rich wraps text to fit the terminal width.
@@ -1320,44 +1283,23 @@ def print_to(
13201283
For example, when soft_wrap is True Panels truncate text
13211284
which is wider than the terminal.
13221285
:param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
1323-
corresponding Unicode characters. Defaults to None.
1324-
If None, the destination console's default behavior is used.
1286+
corresponding Unicode characters. Defaults to False.
13251287
:param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold])
1326-
as styled output. Defaults to None.
1327-
If None, the destination console's default behavior is used.
1288+
as styled output. Defaults to False.
13281289
:param highlight: If True, Rich will automatically apply highlighting to elements within
13291290
strings, such as common Python data types like numbers, booleans, or None.
13301291
This is particularly useful when pretty printing objects like lists and
1331-
dictionaries to display them in color. Defaults to None.
1332-
If None, the destination console's default behavior is used.
1292+
dictionaries to display them in color. Defaults to False.
13331293
:param rich_print_kwargs: optional additional keyword arguments to pass to Rich's Console.print().
13341294
:param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
13351295
method and still call `super()` without encountering unexpected keyword argument errors.
13361296
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``.
13391297
1298+
See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
13401299
"""
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-
1356-
prepared_objects = ru.prepare_objects_for_rendering(*objects)
1357-
13581300
try:
1359-
console.print(
1360-
*prepared_objects,
1301+
Cmd2BaseConsole(file=file).print(
1302+
*objects,
13611303
sep=sep,
13621304
end=end,
13631305
style=style,
@@ -1373,7 +1315,7 @@ def print_to(
13731315
# writing. If you would like your application to print a
13741316
# warning message, then set the broken_pipe_warning attribute
13751317
# to the message you want printed.
1376-
if self.broken_pipe_warning and console.file != sys.stderr:
1318+
if self.broken_pipe_warning and file != sys.stderr:
13771319
Cmd2GeneralConsole(file=sys.stderr).print(self.broken_pipe_warning)
13781320

13791321
def poutput(
@@ -1382,10 +1324,10 @@ def poutput(
13821324
sep: str = " ",
13831325
end: str = "\n",
13841326
style: StyleType | None = None,
1385-
soft_wrap: bool | None = None,
1386-
emoji: bool | None = None,
1387-
markup: bool | None = None,
1388-
highlight: bool | None = None,
1327+
soft_wrap: bool = True,
1328+
emoji: bool = False,
1329+
markup: bool = False,
1330+
highlight: bool = False,
13891331
rich_print_kwargs: RichPrintKwargs | None = None,
13901332
**kwargs: Any, # noqa: ARG002
13911333
) -> None:
@@ -1412,10 +1354,10 @@ def perror(
14121354
sep: str = " ",
14131355
end: str = "\n",
14141356
style: StyleType | None = Cmd2Style.ERROR,
1415-
soft_wrap: bool | None = None,
1416-
emoji: bool | None = None,
1417-
markup: bool | None = None,
1418-
highlight: bool | None = None,
1357+
soft_wrap: bool = True,
1358+
emoji: bool = False,
1359+
markup: bool = False,
1360+
highlight: bool = False,
14191361
rich_print_kwargs: RichPrintKwargs | None = None,
14201362
**kwargs: Any, # noqa: ARG002
14211363
) -> None:
@@ -1443,10 +1385,10 @@ def psuccess(
14431385
*objects: Any,
14441386
sep: str = " ",
14451387
end: str = "\n",
1446-
soft_wrap: bool | None = None,
1447-
emoji: bool | None = None,
1448-
markup: bool | None = None,
1449-
highlight: bool | None = None,
1388+
soft_wrap: bool = True,
1389+
emoji: bool = False,
1390+
markup: bool = False,
1391+
highlight: bool = False,
14501392
rich_print_kwargs: RichPrintKwargs | None = None,
14511393
**kwargs: Any, # noqa: ARG002
14521394
) -> None:
@@ -1471,10 +1413,10 @@ def pwarning(
14711413
*objects: Any,
14721414
sep: str = " ",
14731415
end: str = "\n",
1474-
soft_wrap: bool | None = None,
1475-
emoji: bool | None = None,
1476-
markup: bool | None = None,
1477-
highlight: bool | None = None,
1416+
soft_wrap: bool = True,
1417+
emoji: bool = False,
1418+
markup: bool = False,
1419+
highlight: bool = False,
14781420
rich_print_kwargs: RichPrintKwargs | None = None,
14791421
**kwargs: Any, # noqa: ARG002
14801422
) -> None:
@@ -1554,10 +1496,10 @@ def pfeedback(
15541496
sep: str = " ",
15551497
end: str = "\n",
15561498
style: StyleType | None = None,
1557-
soft_wrap: bool | None = None,
1558-
emoji: bool | None = None,
1559-
markup: bool | None = None,
1560-
highlight: bool | None = None,
1499+
soft_wrap: bool = True,
1500+
emoji: bool = False,
1501+
markup: bool = False,
1502+
highlight: bool = False,
15611503
rich_print_kwargs: RichPrintKwargs | None = None,
15621504
**kwargs: Any, # noqa: ARG002
15631505
) -> None:
@@ -1602,9 +1544,9 @@ def ppaged(
16021544
style: StyleType | None = None,
16031545
chop: bool = False,
16041546
soft_wrap: bool = True,
1605-
emoji: bool | None = None,
1606-
markup: bool | None = None,
1607-
highlight: bool | None = None,
1547+
emoji: bool = False,
1548+
markup: bool = False,
1549+
highlight: bool = False,
16081550
rich_print_kwargs: RichPrintKwargs | None = None,
16091551
**kwargs: Any, # noqa: ARG002
16101552
) -> None:
@@ -1646,10 +1588,9 @@ def ppaged(
16461588
soft_wrap = True
16471589

16481590
# Generate the bytes to send to the pager
1649-
console = Cmd2GeneralConsole(file=self.stdout)
1591+
console = Cmd2BaseConsole(file=self.stdout)
16501592
with console.capture() as capture:
1651-
self.print_to(
1652-
console,
1593+
console.print(
16531594
*objects,
16541595
sep=sep,
16551596
end=end,

0 commit comments

Comments
 (0)