Skip to content

# cmd2.rich_utils

## `` cmd2.rich_utils

Provides common utilities to support Rich in cmd2-based applications.

### `` ANSI_STYLE_SEQUENCE_RE `module-attribute`

    ANSI_STYLE_SEQUENCE_RE = compile('\\x1b\\[[0-9;?]*m')

### `` ALLOW_STYLE `module-attribute`

    ALLOW_STYLE = TERMINAL

### `` APP_THEME `module-attribute`

    APP_THEME = _create_default_theme()

### `` AllowStyle

Bases: `Enum`

Values for `cmd2.rich_utils.ALLOW_STYLE`.

#### `` ALWAYS `class-attribute` `instance-attribute`

    ALWAYS = 'Always'

#### `` NEVER `class-attribute` `instance-attribute`

    NEVER = 'Never'

#### `` TERMINAL `class-attribute` `instance-attribute`

    TERMINAL = 'Terminal'

### `` RichPrintKwargs

Bases: `TypedDict`

Keyword arguments that can be passed to rich.console.Console.print() via cmd2's print methods.

See Rich's Console.print() documentation for full details on these parameters. https://rich.readthedocs.io/en/stable/reference/console.html#rich.console.Console.print

Note: All fields are optional (total=False). If a key is not present in the dictionary, Rich's default behavior for that argument will apply.

#### `` justify `instance-attribute`

    justify

#### `` overflow `instance-attribute`

    overflow

#### `` no_wrap `instance-attribute`

    no_wrap

#### `` width `instance-attribute`

    width

#### `` height `instance-attribute`

    height

#### `` crop `instance-attribute`

    crop

#### `` new_line_start `instance-attribute`

    new_line_start

### `` Cmd2BaseConsole

    Cmd2BaseConsole(file=None, **kwargs)

Bases: `Console`

Base class for all cmd2 Rich consoles.

This class handles the core logic for managing Rich behavior based on cmd2's global settings, such as `ALLOW_STYLE` and `APP_THEME`.

Cmd2BaseConsole initializer.

PARAMETER | DESCRIPTION
--- | ---
`file` | optional file object where the console should write to. Defaults to sys.stdout. TYPE: `IO[str] | None` DEFAULT: `None`
`kwargs` | keyword arguments passed to the parent Console class. TYPE: `Any` DEFAULT: `{}`

RAISES | DESCRIPTION
--- | ---
`TypeError` | if disallowed keyword argument is passed in.

Source code in `cmd2/rich_utils.py`

    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171

    def __init__(
        self,
        file: IO[str] | None = None,
        **kwargs: Any,
    ) -> None:
        """Cmd2BaseConsole initializer.

        :param file: optional file object where the console should write to.
                     Defaults to sys.stdout.
        :param kwargs: keyword arguments passed to the parent Console class.
        :raises TypeError: if disallowed keyword argument is passed in.
        """
        # Don't allow force_terminal or force_interactive to be passed in, as their
        # behavior is controlled by the ALLOW_STYLE setting.
        if "force_terminal" in kwargs:
            raise TypeError(
                "Passing 'force_terminal' is not allowed. Its behavior is controlled by the 'ALLOW_STYLE' setting."
            )
        if "force_interactive" in kwargs:
            raise TypeError(
                "Passing 'force_interactive' is not allowed. Its behavior is controlled by the 'ALLOW_STYLE' setting."
            )

        # Don't allow a theme to be passed in, as it is controlled by the global APP_THEME.
        # Use cmd2.rich_utils.set_theme() to set the global theme or use a temporary
        # theme with console.use_theme().
        if "theme" in kwargs:
            raise TypeError(
                "Passing 'theme' is not allowed. Its behavior is controlled by the global APP_THEME and set_theme()."
            )

        force_terminal: bool | None = None
        force_interactive: bool | None = None

        if ALLOW_STYLE == AllowStyle.ALWAYS:
            force_terminal = True

            # Turn off interactive mode if dest is not actually a terminal which supports it
            tmp_console = Console(file=file)
            force_interactive = tmp_console.is_interactive
        elif ALLOW_STYLE == AllowStyle.NEVER:
            force_terminal = False

        super().__init__(
            file=file,
            force_terminal=force_terminal,
            force_interactive=force_interactive,
            theme=APP_THEME,
            **kwargs,
        )

#### `` on_broken_pipe

    on_broken_pipe()

Override which raises BrokenPipeError instead of SystemExit.

Source code in `cmd2/rich_utils.py`

    173
    174
    175
    176

    def on_broken_pipe(self) -> None:
        """Override which raises BrokenPipeError instead of SystemExit."""
        self.quiet = True
        raise BrokenPipeError

### `` Cmd2GeneralConsole

    Cmd2GeneralConsole(file=None)

Bases: `Cmd2BaseConsole`

Rich console for general-purpose printing.

Cmd2GeneralConsole initializer.

PARAMETER | DESCRIPTION
--- | ---
`file` | optional file object where the console should write to. Defaults to sys.stdout. TYPE: `IO[str] | None` DEFAULT: `None`

Source code in `cmd2/rich_utils.py`

    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197

    def __init__(self, file: IO[str] | None = None) -> None:
        """Cmd2GeneralConsole initializer.

        :param file: optional file object where the console should write to.
                     Defaults to sys.stdout.
        """
        # This console is configured for general-purpose printing. It enables soft wrap
        # and disables Rich's automatic detection for markup, emoji, and highlighting.
        # These defaults can be overridden in calls to the console's or cmd2's print methods.
        super().__init__(
            file=file,
            soft_wrap=True,
            markup=False,
            emoji=False,
            highlight=False,
        )

#### `` on_broken_pipe

    on_broken_pipe()

Override which raises BrokenPipeError instead of SystemExit.

Source code in `cmd2/rich_utils.py`

    173
    174
    175
    176

    def on_broken_pipe(self) -> None:
        """Override which raises BrokenPipeError instead of SystemExit."""
        self.quiet = True
        raise BrokenPipeError

### `` Cmd2RichArgparseConsole

    Cmd2RichArgparseConsole(file=None)

Bases: `Cmd2BaseConsole`

Rich console for rich-argparse output.

This class ensures long lines in help text are not truncated by avoiding soft_wrap, which conflicts with rich-argparse's explicit no_wrap and overflow settings.

Cmd2RichArgparseConsole initializer.

PARAMETER | DESCRIPTION
--- | ---
`file` | optional file object where the console should write to. Defaults to sys.stdout. TYPE: `IO[str] | None` DEFAULT: `None`

Source code in `cmd2/rich_utils.py`

    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223

    def __init__(self, file: IO[str] | None = None) -> None:
        """Cmd2RichArgparseConsole initializer.

        :param file: optional file object where the console should write to.
                     Defaults to sys.stdout.
        """
        # Since this console is used to print error messages which may not have
        # been pre-formatted by rich-argparse, disable Rich's automatic detection
        # for markup, emoji, and highlighting. rich-argparse does markup and
        # highlighting without involving the console so these won't affect its
        # internal functionality.
        super().__init__(
            file=file,
            markup=False,
            emoji=False,
            highlight=False,
        )

#### `` on_broken_pipe

    on_broken_pipe()

Override which raises BrokenPipeError instead of SystemExit.

Source code in `cmd2/rich_utils.py`

    173
    174
    175
    176

    def on_broken_pipe(self) -> None:
        """Override which raises BrokenPipeError instead of SystemExit."""
        self.quiet = True
        raise BrokenPipeError

### `` Cmd2ExceptionConsole

    Cmd2ExceptionConsole(file=None, **kwargs)

Bases: `Cmd2BaseConsole`

Rich console for printing exceptions.

Ensures that long exception messages word wrap for readability by keeping soft_wrap disabled.

Cmd2BaseConsole initializer.

PARAMETER | DESCRIPTION
--- | ---
`file` | optional file object where the console should write to. Defaults to sys.stdout. TYPE: `IO[str] | None` DEFAULT: `None`
`kwargs` | keyword arguments passed to the parent Console class. TYPE: `Any` DEFAULT: `{}`

RAISES | DESCRIPTION
--- | ---
`TypeError` | if disallowed keyword argument is passed in.

Source code in `cmd2/rich_utils.py`

    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171

    def __init__(
        self,
        file: IO[str] | None = None,
        **kwargs: Any,
    ) -> None:
        """Cmd2BaseConsole initializer.

        :param file: optional file object where the console should write to.
                     Defaults to sys.stdout.
        :param kwargs: keyword arguments passed to the parent Console class.
        :raises TypeError: if disallowed keyword argument is passed in.
        """
        # Don't allow force_terminal or force_interactive to be passed in, as their
        # behavior is controlled by the ALLOW_STYLE setting.
        if "force_terminal" in kwargs:
            raise TypeError(
                "Passing 'force_terminal' is not allowed. Its behavior is controlled by the 'ALLOW_STYLE' setting."
            )
        if "force_interactive" in kwargs:
            raise TypeError(
                "Passing 'force_interactive' is not allowed. Its behavior is controlled by the 'ALLOW_STYLE' setting."
            )

        # Don't allow a theme to be passed in, as it is controlled by the global APP_THEME.
        # Use cmd2.rich_utils.set_theme() to set the global theme or use a temporary
        # theme with console.use_theme().
        if "theme" in kwargs:
            raise TypeError(
                "Passing 'theme' is not allowed. Its behavior is controlled by the global APP_THEME and set_theme()."
            )

        force_terminal: bool | None = None
        force_interactive: bool | None = None

        if ALLOW_STYLE == AllowStyle.ALWAYS:
            force_terminal = True

            # Turn off interactive mode if dest is not actually a terminal which supports it
            tmp_console = Console(file=file)
            force_interactive = tmp_console.is_interactive
        elif ALLOW_STYLE == AllowStyle.NEVER:
            force_terminal = False

        super().__init__(
            file=file,
            force_terminal=force_terminal,
            force_interactive=force_interactive,
            theme=APP_THEME,
            **kwargs,
        )

#### `` on_broken_pipe

    on_broken_pipe()

Override which raises BrokenPipeError instead of SystemExit.

Source code in `cmd2/rich_utils.py`

    173
    174
    175
    176

    def on_broken_pipe(self) -> None:
        """Override which raises BrokenPipeError instead of SystemExit."""
        self.quiet = True
        raise BrokenPipeError

### `` set_theme

    set_theme(styles=None)

Set the Rich theme used by cmd2.

Call set_theme() with no arguments to reset to the default theme. This will clear any custom styles that were previously applied.

PARAMETER | DESCRIPTION
--- | ---
`styles` | optional mapping of style names to styles TYPE: `Mapping[str, StyleType] | None` DEFAULT: `None`

Source code in `cmd2/rich_utils.py`

    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89

    def set_theme(styles: Mapping[str, StyleType] | None = None) -> None:
        """Set the Rich theme used by cmd2.

        Call set_theme() with no arguments to reset to the default theme.
        This will clear any custom styles that were previously applied.

        :param styles: optional mapping of style names to styles
        """
        global APP_THEME  # noqa: PLW0603

        # Start with a fresh copy of the default styles.
        app_styles: dict[str, StyleType] = {}
        app_styles.update(_create_default_theme().styles)

        # Incorporate custom styles.
        if styles is not None:
            app_styles.update(styles)

        APP_THEME = Theme(app_styles)

        # Synchronize rich-argparse styles with the main application theme.
        for name in RichHelpFormatter.styles.keys() & APP_THEME.styles.keys():
            RichHelpFormatter.styles[name] = APP_THEME.styles[name]

### `` console_width

    console_width()

Return the width of the console.

Source code in `cmd2/rich_utils.py`

    233
    234
    235

    def console_width() -> int:
        """Return the width of the console."""
        return Console().width

### `` rich_text_to_string

    rich_text_to_string(text)

Convert a Rich Text object to a string.

This function's purpose is to render a Rich Text object, including any styles (e.g., color, bold), to a plain Python string with ANSI style sequences. It differs from `text.plain`, which strips all formatting.

PARAMETER | DESCRIPTION
--- | ---
`text` | the text object to convert TYPE: `Text`

RETURNS | DESCRIPTION
--- | ---
`str` | the resulting string with ANSI styles preserved.

Source code in `cmd2/rich_utils.py`

    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259

    def rich_text_to_string(text: Text) -> str:
        """Convert a Rich Text object to a string.

        This function's purpose is to render a Rich Text object, including any styles (e.g., color, bold),
        to a plain Python string with ANSI style sequences. It differs from `text.plain`, which strips
        all formatting.

        :param text: the text object to convert
        :return: the resulting string with ANSI styles preserved.
        """
        console = Console(
            force_terminal=True,
            soft_wrap=True,
            no_color=False,
            markup=False,
            emoji=False,
            highlight=False,
            theme=APP_THEME,
        )
        with console.capture() as capture:
            console.print(text, end="")
        return capture.get()

### `` indent

    indent(renderable, level)

Indent a Rich renderable.

When soft-wrapping is enabled, a Rich console is unable to properly print a Padding object of indented text, as it truncates long strings instead of wrapping them. This function provides a workaround for this issue, ensuring that indented text is printed correctly regardless of the soft-wrap setting.

For non-text objects, this function merely serves as a convenience wrapper around Padding.indent().

PARAMETER | DESCRIPTION
--- | ---
`renderable` | a Rich renderable to indent. TYPE: `RenderableType`
`level` | number of characters to indent. TYPE: `int`

RETURNS | DESCRIPTION
--- | ---
`Padding` | a Padding object containing the indented content.

Source code in `cmd2/rich_utils.py`

    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283

    def indent(renderable: RenderableType, level: int) -> Padding:
        """Indent a Rich renderable.

        When soft-wrapping is enabled, a Rich console is unable to properly print a
        Padding object of indented text, as it truncates long strings instead of wrapping
        them. This function provides a workaround for this issue, ensuring that indented
        text is printed correctly regardless of the soft-wrap setting.

        For non-text objects, this function merely serves as a convenience
        wrapper around Padding.indent().

        :param renderable: a Rich renderable to indent.
        :param level: number of characters to indent.
        :return: a Padding object containing the indented content.
        """
        if isinstance(renderable, (str, Text)):
            # Wrap text in a grid to handle the wrapping.
            text_grid = Table.grid(Column(overflow="fold"))
            text_grid.add_row(renderable)
            renderable = text_grid

        return Padding.indent(renderable, level)

### `` prepare_objects_for_rendering

    prepare_objects_for_rendering(*objects)

Prepare a tuple of objects for printing by Rich's Console.print().

This function processes objects to ensure they are rendered correctly by Rich. It inspects each object and, if its string representation contains ANSI style sequences, it converts the object to a Rich Text object. This ensures Rich can properly parse the non-printing codes for accurate display width calculation.

Objects that already implement the Rich console protocol or are expandable by its pretty printer are left untouched, as they can be handled directly by Rich's native renderers.

PARAMETER | DESCRIPTION
--- | ---
`objects` | objects to prepare TYPE: `Any` DEFAULT: `()`

RETURNS | DESCRIPTION
--- | ---
`tuple[Any, ...]` | a tuple containing the processed objects.

Source code in `cmd2/rich_utils.py`

    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317

    def prepare_objects_for_rendering(*objects: Any) -> tuple[Any, ...]:
        """Prepare a tuple of objects for printing by Rich's Console.print().

        This function processes objects to ensure they are rendered correctly by Rich.
        It inspects each object and, if its string representation contains ANSI style
        sequences, it converts the object to a Rich Text object. This ensures Rich can
        properly parse the non-printing codes for accurate display width calculation.

        Objects that already implement the Rich console protocol or are expandable
        by its pretty printer are left untouched, as they can be handled directly by
        Rich's native renderers.

        :param objects: objects to prepare
        :return: a tuple containing the processed objects.
        """
        object_list = list(objects)

        for i, obj in enumerate(object_list):
            # Resolve the object's final renderable form, including those
            # with a __rich__ method that might return a string.
            renderable = rich_cast(obj)

            # No preprocessing is needed for Rich-compatible or expandable objects.
            if isinstance(renderable, ConsoleRenderable) or is_expandable(renderable):
                continue

            # Check for ANSI style sequences in its string representation.
            renderable_as_str = str(renderable)
            if ANSI_STYLE_SEQUENCE_RE.search(renderable_as_str):
                object_list[i] = Text.from_ansi(renderable_as_str)

        return tuple(object_list)