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
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
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
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
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
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
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
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
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
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
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
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
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
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)