Skip to content

Added cmd2.Cmd.ppretty() pretty print output method #1433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- `cmd2` 2.6 supports Python 3.9+ (removed support for Python 3.8)
- Enhancements
- Add support for Python 3.14
- Added new `Cmd.ppretty()` method for pretty printing arbitrary Python data structures

## 2.5.11 (January 25, 2025)

Expand Down
12 changes: 12 additions & 0 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import glob
import inspect
import os
import pprint
import pydoc
import re
import sys
Expand Down Expand Up @@ -1338,6 +1339,17 @@ def ppaged(self, msg: Any, *, end: str = '\n', chop: bool = False) -> None:
else:
self.poutput(msg, end=end)

def ppretty(self, data: Any, *, indent: int = 2, width: int = 80, depth: Optional[int] = None, end: str = '\n') -> None:
"""Pretty print arbitrary Python data structures to self.stdout and appends a newline by default.

:param data: object to print
:param indent: the amount of indentation added for each nesting level
:param width: the desired maximum number of characters per line in the output, a best effort will be made for long data
:param depth: the number of nesting levels which may be printed, if data is too deep, the next level replaced by ...
:param end: string appended after the end of the message, default a newline
"""
self.print_to(self.stdout, pprint.pformat(data, indent, width, depth), end=end)

# ----- Methods related to tab completion -----

def _reset_completion_defaults(self) -> None:
Expand Down
11 changes: 11 additions & 0 deletions docs/features/generating_output.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,14 @@ These functions differ from Python's string justifying functions in that they su
When generating output in multiple columns, you often need to calculate the width of each item so you can pad it appropriately with spaces. However, there are categories of Unicode characters that occupy 2 cells, and other that occupy 0. To further complicate matters, you might have included ANSI escape sequences in the output to generate colors on the terminal.

The `cmd2.ansi.style_aware_wcswidth` function solves both of these problems. Pass it a string, and regardless of which Unicode characters and ANSI text style escape sequences it contains, it will tell you how many characters on the screen that string will consume when printed.

## Pretty Printing Data Structures

The `cmd2.Cmd.ppretty` method is similar to the Python [pprint](https://docs.python.org/3/library/pprint.html) function from the standard `pprint` module. `cmd2.Cmd.pprint` adds the same conveniences as `cmd2.Cmd.poutput`.

This method provides a capability to “pretty-print” arbitrary Python data structures in a form which can be used as input to the interpreter and is easy for humans
to read.

The formatted representation keeps objects on a single line if it can, and breaks them onto multiple lines if they don’t fit within the allowed width, adjustable by the width parameter defaulting to 80 characters.

Dictionaries are sorted by key before the display is computed.
1 change: 1 addition & 0 deletions docs/features/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Output generated by `cmd2` programs may contain ANSI escape sequences which inst
- **`cmd2.Cmd.pexcept`**
- **`cmd2.Cmd.pfeedback`**
- **`cmd2.Cmd.ppaged`**
- **`cmd2.Cmd.ppretty`**

This setting can be one of three values:

Expand Down
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ Here is the list of examples in alphabetical order by filename along with a brie
- Shows how to enable persistent history in your `cmd2` application
- [pirate.py](https://github.com/python-cmd2/cmd2/blob/master/examples/pirate.py)
- Demonstrates many features including colorized output, multiline commands, shorcuts, defaulting to shell, etc.
- [pretty_print.py](https://github.com/python-cmd2/cmd2/blob/master/examples/pretty_print.py)
- Demonstrates use of cmd2.Cmd.ppretty() for pretty-printing arbitrary Python data structures like dictionaries.
- [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py)
- Shows how cmd2's built-in `run_pyscript` command can provide advanced Python scripting of cmd2 applications
- [read_input.py](https://github.com/python-cmd2/cmd2/blob/master/examples/read_input.py)
Expand Down
29 changes: 29 additions & 0 deletions examples/pretty_print.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""A simple example demonstrating use of cmd2.Cmd.ppretty()."""

import cmd2

data = {
"name": "John Doe",
"age": 30,
"address": {"street": "123 Main St", "city": "Anytown", "state": "CA"},
"hobbies": ["reading", "hiking", "coding"],
}


class Cmd2App(cmd2.Cmd):
def __init__(self) -> None:
super().__init__()

def do_normal(self, _) -> None:
"""Display the data using the normal poutput method."""
self.poutput(data)

def do_pretty(self, _) -> None:
"""Display the data using the ppretty method."""
self.ppretty(data)


if __name__ == '__main__':
app = Cmd2App()
app.cmdloop()
18 changes: 18 additions & 0 deletions tests/test_cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,24 @@ def test_poutput_none(outsim_app) -> None:
assert out == expected


def test_ppretty_dict(outsim_app) -> None:
data = {
"name": "John Doe",
"age": 30,
"address": {"street": "123 Main St", "city": "Anytown", "state": "CA"},
"hobbies": ["reading", "hiking", "coding"],
}
outsim_app.ppretty(data)
out = outsim_app.stdout.getvalue()
expected = """
{ 'address': {'city': 'Anytown', 'state': 'CA', 'street': '123 Main St'},
'age': 30,
'hobbies': ['reading', 'hiking', 'coding'],
'name': 'John Doe'}
"""
assert out == expected.lstrip()


@with_ansi_style(ansi.AllowStyle.ALWAYS)
def test_poutput_ansi_always(outsim_app) -> None:
msg = 'Hello World'
Expand Down
Loading