Skip to content

cmd2.utils

Settings

cmd2.utils.Settable

Settable(name, val_type, description, settable_object, *, settable_attrib_name=None, onchange_cb=None, choices=None, choices_provider=None, completer=None)

Used to configure an attribute to be settable via the set command in the CLI

Settable Initializer

PARAMETER DESCRIPTION
name

name of the instance attribute being made settable

TYPE: str

val_type

callable used to cast the string value from the command line into its proper type and even validate its value. Setting this to bool provides tab completion for true/false and validation using to_bool(). The val_type function should raise an exception if it fails. This exception will be caught and printed by Cmd.do_set().

TYPE: Union[Type[Any], Callable[[Any], Any]]

description

string describing this setting

TYPE: str

settable_object

object to which the instance attribute belongs (e.g. self)

TYPE: object

settable_attrib_name

name which displays to the user in the output of the set command. Defaults to name if not specified.

TYPE: Optional[str] DEFAULT: None

onchange_cb

optional function or method to call when the value of this settable is altered by the set command. (e.g. onchange_cb=self.debug_changed) Cmd.do_set() passes the following 3 arguments to onchange_cb: param_name: str - name of the changed parameter old_value: Any - the value before being changed new_value: Any - the value after being changed The following optional settings provide tab completion for a parameter's values. They correspond to the same settings in argparse-based tab completion. A maximum of one of these should be provided.

TYPE: Optional[Callable[[str, _T, _T], Any]] DEFAULT: None

choices

iterable of accepted values

TYPE: Optional[Iterable[Any]] DEFAULT: None

choices_provider

function that provides choices for this argument

TYPE: Optional[ChoicesProviderFunc] DEFAULT: None

completer

tab completion function that provides choices for this argument

TYPE: Optional[CompleterFunc] DEFAULT: None

Source code in cmd2/utils.py
def __init__(
    self,
    name: str,
    val_type: Union[Type[Any], Callable[[Any], Any]],
    description: str,
    settable_object: object,
    *,
    settable_attrib_name: Optional[str] = None,
    onchange_cb: Optional[Callable[[str, _T, _T], Any]] = None,
    choices: Optional[Iterable[Any]] = None,
    choices_provider: Optional[ChoicesProviderFunc] = None,
    completer: Optional[CompleterFunc] = None,
) -> None:
    """
    Settable Initializer

    :param name: name of the instance attribute being made settable
    :param val_type: callable used to cast the string value from the command line into its proper type and
                     even validate its value. Setting this to bool provides tab completion for true/false and
                     validation using to_bool(). The val_type function should raise an exception if it fails.
                     This exception will be caught and printed by Cmd.do_set().
    :param description: string describing this setting
    :param settable_object: object to which the instance attribute belongs (e.g. self)
    :param settable_attrib_name: name which displays to the user in the output of the set command.
                                 Defaults to `name` if not specified.
    :param onchange_cb: optional function or method to call when the value of this settable is altered
                        by the set command. (e.g. onchange_cb=self.debug_changed)

                        Cmd.do_set() passes the following 3 arguments to onchange_cb:
                            param_name: str - name of the changed parameter
                            old_value: Any - the value before being changed
                            new_value: Any - the value after being changed

    The following optional settings provide tab completion for a parameter's values. They correspond to the
    same settings in argparse-based tab completion. A maximum of one of these should be provided.

    :param choices: iterable of accepted values
    :param choices_provider: function that provides choices for this argument
    :param completer: tab completion function that provides choices for this argument
    """
    if val_type is bool:

        def get_bool_choices(_) -> List[str]:  # type: ignore[no-untyped-def]
            """Used to tab complete lowercase boolean values"""
            return ['true', 'false']

        val_type = to_bool
        choices_provider = cast(ChoicesProviderFunc, get_bool_choices)

    self.name = name
    self.val_type = val_type
    self.description = description
    self.settable_obj = settable_object
    self.settable_attrib_name = settable_attrib_name if settable_attrib_name is not None else name
    self.onchange_cb = onchange_cb
    self.choices = choices
    self.choices_provider = choices_provider
    self.completer = completer

name instance-attribute

name = name

val_type instance-attribute

val_type = val_type

description instance-attribute

description = description

settable_obj instance-attribute

settable_obj = settable_object

settable_attrib_name instance-attribute

settable_attrib_name = settable_attrib_name if settable_attrib_name is not None else name

onchange_cb instance-attribute

onchange_cb = onchange_cb

choices instance-attribute

choices = choices

choices_provider instance-attribute

choices_provider = choices_provider

completer instance-attribute

completer = completer

get_value

get_value()

Get the value of the settable attribute.

Source code in cmd2/utils.py
def get_value(self) -> Any:
    """Get the value of the settable attribute."""
    return getattr(self.settable_obj, self.settable_attrib_name)

set_value

set_value(value)

Set the settable attribute on the specified destination object.

PARAMETER DESCRIPTION
value

new value to set

TYPE: Any

Source code in cmd2/utils.py
def set_value(self, value: Any) -> None:
    """
    Set the settable attribute on the specified destination object.

    :param value: new value to set
    """
    # Run the value through its type function to handle any conversion or validation
    new_value = self.val_type(value)

    # Make sure new_value is a valid choice
    if self.choices is not None and new_value not in self.choices:
        choices_str = ', '.join(map(repr, self.choices))
        raise ValueError(f"invalid choice: {new_value!r} (choose from {choices_str})")

    # Try to update the settable's value
    orig_value = self.get_value()
    setattr(self.settable_obj, self.settable_attrib_name, new_value)

    # Check if we need to call an onchange callback
    if orig_value != new_value and self.onchange_cb:
        self.onchange_cb(self.name, orig_value, new_value)

Quote Handling

cmd2.utils.is_quoted

is_quoted(arg)

Checks if a string is quoted

PARAMETER DESCRIPTION
arg

the string being checked for quotes

TYPE: str

RETURNS DESCRIPTION
bool

True if a string is quoted

Source code in cmd2/utils.py
def is_quoted(arg: str) -> bool:
    """
    Checks if a string is quoted

    :param arg: the string being checked for quotes
    :return: True if a string is quoted
    """
    return len(arg) > 1 and arg[0] == arg[-1] and arg[0] in constants.QUOTES

cmd2.utils.quote_string

quote_string(arg)

Quote a string

Source code in cmd2/utils.py
def quote_string(arg: str) -> str:
    """Quote a string"""
    if '"' in arg:
        quote = "'"
    else:
        quote = '"'

    return quote + arg + quote

cmd2.utils.quote_string_if_needed

quote_string_if_needed(arg)

Quote a string if it contains spaces and isn't already quoted

Source code in cmd2/utils.py
def quote_string_if_needed(arg: str) -> str:
    """Quote a string if it contains spaces and isn't already quoted"""
    if is_quoted(arg) or ' ' not in arg:
        return arg

    return quote_string(arg)

cmd2.utils.strip_quotes

strip_quotes(arg)

Strip outer quotes from a string.

Applies to both single and double quotes.

PARAMETER DESCRIPTION
arg

string to strip outer quotes from

TYPE: str

RETURNS DESCRIPTION
str

same string with potentially outer quotes stripped

Source code in cmd2/utils.py
def strip_quotes(arg: str) -> str:
    """Strip outer quotes from a string.

     Applies to both single and double quotes.

    :param arg:  string to strip outer quotes from
    :return: same string with potentially outer quotes stripped
    """
    if is_quoted(arg):
        arg = arg[1:-1]
    return arg

IO Handling

cmd2.utils.StdSim

StdSim(inner_stream, *, echo=False, encoding='utf-8', errors='replace')

Class to simulate behavior of sys.stdout or sys.stderr. Stores contents in internal buffer and optionally echos to the inner stream it is simulating.

StdSim Initializer

PARAMETER DESCRIPTION
inner_stream

the wrapped stream. Should be a TextIO or StdSim instance.

TYPE: Union[TextIO, StdSim]

echo

if True, then all input will be echoed to inner_stream

TYPE: bool DEFAULT: False

encoding

codec for encoding/decoding strings (defaults to utf-8)

TYPE: str DEFAULT: 'utf-8'

errors

how to handle encoding/decoding errors (defaults to replace)

TYPE: str DEFAULT: 'replace'

Source code in cmd2/utils.py
def __init__(
    self,
    inner_stream: Union[TextIO, 'StdSim'],
    *,
    echo: bool = False,
    encoding: str = 'utf-8',
    errors: str = 'replace',
) -> None:
    """
    StdSim Initializer

    :param inner_stream: the wrapped stream. Should be a TextIO or StdSim instance.
    :param echo: if True, then all input will be echoed to inner_stream
    :param encoding: codec for encoding/decoding strings (defaults to utf-8)
    :param errors: how to handle encoding/decoding errors (defaults to replace)
    """
    self.inner_stream = inner_stream
    self.echo = echo
    self.encoding = encoding
    self.errors = errors
    self.pause_storage = False
    self.buffer = ByteBuf(self)

inner_stream instance-attribute

inner_stream = inner_stream

echo instance-attribute

echo = echo

encoding instance-attribute

encoding = encoding

errors instance-attribute

errors = errors

pause_storage instance-attribute

pause_storage = False

buffer instance-attribute

buffer = ByteBuf(self)

line_buffering property

line_buffering

Handle when the inner stream doesn't have a line_buffering attribute which is the case when running unit tests because pytest sets stdout to a pytest EncodedFile object.

write

write(s)

Add str to internal bytes buffer and if echo is True, echo contents to inner stream

PARAMETER DESCRIPTION
s

String to write to the stream

TYPE: str

Source code in cmd2/utils.py
def write(self, s: str) -> None:
    """
    Add str to internal bytes buffer and if echo is True, echo contents to inner stream

    :param s: String to write to the stream
    """
    if not isinstance(s, str):
        raise TypeError(f'write() argument must be str, not {type(s)}')

    if not self.pause_storage:
        self.buffer.byte_buf += s.encode(encoding=self.encoding, errors=self.errors)
    if self.echo:
        self.inner_stream.write(s)

getvalue

getvalue()

Get the internal contents as a str

Source code in cmd2/utils.py
def getvalue(self) -> str:
    """Get the internal contents as a str"""
    return self.buffer.byte_buf.decode(encoding=self.encoding, errors=self.errors)

getbytes

getbytes()

Get the internal contents as bytes

Source code in cmd2/utils.py
def getbytes(self) -> bytes:
    """Get the internal contents as bytes"""
    return bytes(self.buffer.byte_buf)

read

read(size=-1)

Read from the internal contents as a str and then clear them out

PARAMETER DESCRIPTION
size

Number of bytes to read from the stream

TYPE: Optional[int] DEFAULT: -1

Source code in cmd2/utils.py
def read(self, size: Optional[int] = -1) -> str:
    """
    Read from the internal contents as a str and then clear them out

    :param size: Number of bytes to read from the stream
    """
    if size is None or size == -1:
        result = self.getvalue()
        self.clear()
    else:
        result = self.buffer.byte_buf[:size].decode(encoding=self.encoding, errors=self.errors)
        self.buffer.byte_buf = self.buffer.byte_buf[size:]

    return result

readbytes

readbytes()

Read from the internal contents as bytes and then clear them out

Source code in cmd2/utils.py
def readbytes(self) -> bytes:
    """Read from the internal contents as bytes and then clear them out"""
    result = self.getbytes()
    self.clear()
    return result

clear

clear()

Clear the internal contents

Source code in cmd2/utils.py
def clear(self) -> None:
    """Clear the internal contents"""
    self.buffer.byte_buf.clear()

isatty

isatty()

StdSim only considered an interactive stream if echo is True and inner_stream is a tty.

Source code in cmd2/utils.py
def isatty(self) -> bool:
    """StdSim only considered an interactive stream if `echo` is True and `inner_stream` is a tty."""
    if self.echo:
        return self.inner_stream.isatty()
    else:
        return False

cmd2.utils.ByteBuf

ByteBuf(std_sim_instance)

Used by StdSim to write binary data and stores the actual bytes written

Source code in cmd2/utils.py
def __init__(self, std_sim_instance: StdSim) -> None:
    self.byte_buf = bytearray()
    self.std_sim_instance = std_sim_instance

NEWLINES class-attribute instance-attribute

NEWLINES = [b'\n', b'\r']

byte_buf instance-attribute

byte_buf = bytearray()

std_sim_instance instance-attribute

std_sim_instance = std_sim_instance

write

write(b)

Add bytes to internal bytes buffer and if echo is True, echo contents to inner stream.

Source code in cmd2/utils.py
def write(self, b: bytes) -> None:
    """Add bytes to internal bytes buffer and if echo is True, echo contents to inner stream."""
    if not isinstance(b, bytes):
        raise TypeError(f'a bytes-like object is required, not {type(b)}')
    if not self.std_sim_instance.pause_storage:
        self.byte_buf += b
    if self.std_sim_instance.echo:
        self.std_sim_instance.inner_stream.buffer.write(b)

        # Since StdSim wraps TextIO streams, we will flush the stream if line buffering is on
        # and the bytes being written contain a new line character. This is helpful when StdSim
        # is being used to capture output of a shell command because it causes the output to print
        # to the screen more often than if we waited for the stream to flush its buffer.
        if self.std_sim_instance.line_buffering:
            if any(newline in b for newline in ByteBuf.NEWLINES):
                self.std_sim_instance.flush()

cmd2.utils.ProcReader

ProcReader(proc, stdout, stderr)

Used to capture stdout and stderr from a Popen process if any of those were set to subprocess.PIPE. If neither are pipes, then the process will run normally and no output will be captured.

ProcReader initializer

PARAMETER DESCRIPTION
proc

the Popen process being read from

TYPE: PopenTextIO

stdout

the stream to write captured stdout

TYPE: Union[StdSim, TextIO]

stderr

the stream to write captured stderr

TYPE: Union[StdSim, TextIO]

Source code in cmd2/utils.py
def __init__(self, proc: PopenTextIO, stdout: Union[StdSim, TextIO], stderr: Union[StdSim, TextIO]) -> None:
    """
    ProcReader initializer
    :param proc: the Popen process being read from
    :param stdout: the stream to write captured stdout
    :param stderr: the stream to write captured stderr
    """
    self._proc = proc
    self._stdout = stdout
    self._stderr = stderr

    self._out_thread = threading.Thread(name='out_thread', target=self._reader_thread_func, kwargs={'read_stdout': True})

    self._err_thread = threading.Thread(name='err_thread', target=self._reader_thread_func, kwargs={'read_stdout': False})

    # Start the reader threads for pipes only
    if self._proc.stdout is not None:
        self._out_thread.start()
    if self._proc.stderr is not None:
        self._err_thread.start()

send_sigint

send_sigint()

Send a SIGINT to the process similar to if +C were pressed

Source code in cmd2/utils.py
def send_sigint(self) -> None:
    """Send a SIGINT to the process similar to if <Ctrl>+C were pressed"""
    import signal

    if sys.platform.startswith('win'):
        # cmd2 started the Windows process in a new process group. Therefore we must send
        # a CTRL_BREAK_EVENT since CTRL_C_EVENT signals cannot be generated for process groups.
        self._proc.send_signal(signal.CTRL_BREAK_EVENT)
    else:
        # Since cmd2 uses shell=True in its Popen calls, we need to send the SIGINT to
        # the whole process group to make sure it propagates further than the shell
        try:
            group_id = os.getpgid(self._proc.pid)
            os.killpg(group_id, signal.SIGINT)
        except ProcessLookupError:
            return

terminate

terminate()

Terminate the process

Source code in cmd2/utils.py
def terminate(self) -> None:
    """Terminate the process"""
    self._proc.terminate()

wait

wait()

Wait for the process to finish

Source code in cmd2/utils.py
def wait(self) -> None:
    """Wait for the process to finish"""
    if self._out_thread.is_alive():
        self._out_thread.join()
    if self._err_thread.is_alive():
        self._err_thread.join()

    # Handle case where the process ended before the last read could be done.
    # This will return None for the streams that weren't pipes.
    out, err = self._proc.communicate()

    if out:
        self._write_bytes(self._stdout, out)
    if err:
        self._write_bytes(self._stderr, err)

Tab Completion

cmd2.utils.CompletionMode

Bases: Enum

Enum for what type of tab completion to perform in cmd2.Cmd.read_input()

NONE class-attribute instance-attribute

NONE = 1

COMMANDS class-attribute instance-attribute

COMMANDS = 2

CUSTOM class-attribute instance-attribute

CUSTOM = 3

cmd2.utils.CustomCompletionSettings

CustomCompletionSettings(parser, *, preserve_quotes=False)

Used by cmd2.Cmd.complete() to tab complete strings other than command arguments

Initializer

PARAMETER DESCRIPTION
parser

arg parser defining format of string being tab completed

TYPE: ArgumentParser

preserve_quotes

if True, then quoted tokens will keep their quotes when processed by ArgparseCompleter. This is helpful in cases when you're tab completing flag-like tokens (e.g. -o, --option) and you don't want them to be treated as argparse flags when quoted. Set this to True if you plan on passing the string to argparse with the tokens still quoted.

TYPE: bool DEFAULT: False

Source code in cmd2/utils.py
def __init__(self, parser: argparse.ArgumentParser, *, preserve_quotes: bool = False) -> None:
    """
    Initializer

    :param parser: arg parser defining format of string being tab completed
    :param preserve_quotes: if True, then quoted tokens will keep their quotes when processed by
                            ArgparseCompleter. This is helpful in cases when you're tab completing
                            flag-like tokens (e.g. -o, --option) and you don't want them to be
                            treated as argparse flags when quoted. Set this to True if you plan
                            on passing the string to argparse with the tokens still quoted.
    """
    self.parser = parser
    self.preserve_quotes = preserve_quotes

parser instance-attribute

parser = parser

preserve_quotes instance-attribute

preserve_quotes = preserve_quotes

Text Alignment

cmd2.utils.TextAlignment

Bases: Enum

Horizontal text alignment

LEFT class-attribute instance-attribute

LEFT = 1

CENTER class-attribute instance-attribute

CENTER = 2

RIGHT class-attribute instance-attribute

RIGHT = 3

cmd2.utils.align_text

align_text(text, alignment, *, fill_char=' ', width=None, tab_width=4, truncate=False)

Align text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently.

There are convenience wrappers around this function: align_left(), align_center(), and align_right()

PARAMETER DESCRIPTION
text

text to align (can contain multiple lines)

TYPE: str

alignment

how to align the text

TYPE: TextAlignment

fill_char

character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)

TYPE: str DEFAULT: ' '

width

display width of the aligned text. Defaults to width of the terminal.

TYPE: Optional[int] DEFAULT: None

tab_width

any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

truncate

if True, then each line will be shortened to fit within the display width. The truncated portions are replaced by a '…' character. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
str

aligned text

RAISES DESCRIPTION
TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if text or fill_char contains an unprintable character

ValueError

if width is less than 1

Source code in cmd2/utils.py
def align_text(
    text: str,
    alignment: TextAlignment,
    *,
    fill_char: str = ' ',
    width: Optional[int] = None,
    tab_width: int = 4,
    truncate: bool = False,
) -> str:
    """
    Align text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    There are convenience wrappers around this function: align_left(), align_center(), and align_right()

    :param text: text to align (can contain multiple lines)
    :param alignment: how to align the text
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then each line will be shortened to fit within the display width. The truncated
                     portions are replaced by a '…' character. Defaults to False.
    :return: aligned text
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if text or fill_char contains an unprintable character
    :raises ValueError: if width is less than 1
    """
    import io
    import shutil

    from . import (
        ansi,
    )

    if width is None:
        # Prior to Python 3.11 this can return 0, so use a fallback if needed.
        width = shutil.get_terminal_size().columns or constants.DEFAULT_TERMINAL_WIDTH

    if width < 1:
        raise ValueError("width must be at least 1")

    # Convert tabs to spaces
    text = text.replace('\t', ' ' * tab_width)
    fill_char = fill_char.replace('\t', ' ')

    # Save fill_char with no styles for use later
    stripped_fill_char = ansi.strip_style(fill_char)
    if len(stripped_fill_char) != 1:
        raise TypeError("Fill character must be exactly one character long")

    fill_char_width = ansi.style_aware_wcswidth(fill_char)
    if fill_char_width == -1:
        raise (ValueError("Fill character is an unprintable character"))

    # Isolate the style chars before and after the fill character. We will use them when building sequences of
    # fill characters. Instead of repeating the style characters for each fill character, we'll wrap each sequence.
    fill_char_style_begin, fill_char_style_end = fill_char.split(stripped_fill_char)

    if text:
        lines = text.splitlines()
    else:
        lines = ['']

    text_buf = io.StringIO()

    # ANSI style sequences that may affect subsequent lines will be cancelled by the fill_char's style.
    # To avoid this, we save styles which are still in effect so we can restore them when beginning the next line.
    # This also allows lines to be used independently and still have their style. TableCreator does this.
    previous_styles: List[str] = []

    for index, line in enumerate(lines):
        if index > 0:
            text_buf.write('\n')

        if truncate:
            line = truncate_line(line, width)

        line_width = ansi.style_aware_wcswidth(line)
        if line_width == -1:
            raise (ValueError("Text to align contains an unprintable character"))

        # Get list of styles in this line
        line_styles = list(get_styles_dict(line).values())

        # Calculate how wide each side of filling needs to be
        if line_width >= width:
            # Don't return here even though the line needs no fill chars.
            # There may be styles sequences to restore.
            total_fill_width = 0
        else:
            total_fill_width = width - line_width

        if alignment == TextAlignment.LEFT:
            left_fill_width = 0
            right_fill_width = total_fill_width
        elif alignment == TextAlignment.CENTER:
            left_fill_width = total_fill_width // 2
            right_fill_width = total_fill_width - left_fill_width
        else:
            left_fill_width = total_fill_width
            right_fill_width = 0

        # Determine how many fill characters are needed to cover the width
        left_fill = (left_fill_width // fill_char_width) * stripped_fill_char
        right_fill = (right_fill_width // fill_char_width) * stripped_fill_char

        # In cases where the fill character display width didn't divide evenly into
        # the gap being filled, pad the remainder with space.
        left_fill += ' ' * (left_fill_width - ansi.style_aware_wcswidth(left_fill))
        right_fill += ' ' * (right_fill_width - ansi.style_aware_wcswidth(right_fill))

        # Don't allow styles in fill characters and text to affect one another
        if fill_char_style_begin or fill_char_style_end or previous_styles or line_styles:
            if left_fill:
                left_fill = ansi.TextStyle.RESET_ALL + fill_char_style_begin + left_fill + fill_char_style_end
            left_fill += ansi.TextStyle.RESET_ALL

            if right_fill:
                right_fill = ansi.TextStyle.RESET_ALL + fill_char_style_begin + right_fill + fill_char_style_end
            right_fill += ansi.TextStyle.RESET_ALL

        # Write the line and restore styles from previous lines which are still in effect
        text_buf.write(left_fill + ''.join(previous_styles) + line + right_fill)

        # Update list of styles that are still in effect for the next line
        previous_styles.extend(line_styles)
        previous_styles = _remove_overridden_styles(previous_styles)

    return text_buf.getvalue()

cmd2.utils.align_left

align_left(text, *, fill_char=' ', width=None, tab_width=4, truncate=False)

Left align text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently.

PARAMETER DESCRIPTION
text

text to left align (can contain multiple lines)

TYPE: str

fill_char

character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)

TYPE: str DEFAULT: ' '

width

display width of the aligned text. Defaults to width of the terminal.

TYPE: Optional[int] DEFAULT: None

tab_width

any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

truncate

if True, then text will be shortened to fit within the display width. The truncated portion is replaced by a '…' character. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
str

left-aligned text

RAISES DESCRIPTION
TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if text or fill_char contains an unprintable character

ValueError

if width is less than 1

Source code in cmd2/utils.py
def align_left(
    text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False
) -> str:
    """
    Left align text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    :param text: text to left align (can contain multiple lines)
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
                     replaced by a '…' character. Defaults to False.
    :return: left-aligned text
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if text or fill_char contains an unprintable character
    :raises ValueError: if width is less than 1
    """
    return align_text(text, TextAlignment.LEFT, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate)

cmd2.utils.align_right

align_right(text, *, fill_char=' ', width=None, tab_width=4, truncate=False)

Right align text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently.

PARAMETER DESCRIPTION
text

text to right align (can contain multiple lines)

TYPE: str

fill_char

character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)

TYPE: str DEFAULT: ' '

width

display width of the aligned text. Defaults to width of the terminal.

TYPE: Optional[int] DEFAULT: None

tab_width

any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

truncate

if True, then text will be shortened to fit within the display width. The truncated portion is replaced by a '…' character. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
str

right-aligned text

RAISES DESCRIPTION
TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if text or fill_char contains an unprintable character

ValueError

if width is less than 1

Source code in cmd2/utils.py
def align_right(
    text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False
) -> str:
    """
    Right align text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    :param text: text to right align (can contain multiple lines)
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
                     replaced by a '…' character. Defaults to False.
    :return: right-aligned text
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if text or fill_char contains an unprintable character
    :raises ValueError: if width is less than 1
    """
    return align_text(text, TextAlignment.RIGHT, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate)

cmd2.utils.align_center

align_center(text, *, fill_char=' ', width=None, tab_width=4, truncate=False)

Center text for display within a given width. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned independently.

PARAMETER DESCRIPTION
text

text to center (can contain multiple lines)

TYPE: str

fill_char

character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)

TYPE: str DEFAULT: ' '

width

display width of the aligned text. Defaults to width of the terminal.

TYPE: Optional[int] DEFAULT: None

tab_width

any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will be converted to one space.

TYPE: int DEFAULT: 4

truncate

if True, then text will be shortened to fit within the display width. The truncated portion is replaced by a '…' character. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
str

centered text

RAISES DESCRIPTION
TypeError

if fill_char is more than one character (not including ANSI style sequences)

ValueError

if text or fill_char contains an unprintable character

ValueError

if width is less than 1

Source code in cmd2/utils.py
def align_center(
    text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False
) -> str:
    """
    Center text for display within a given width. Supports characters with display widths greater than 1.
    ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
    independently.

    :param text: text to center (can contain multiple lines)
    :param fill_char: character that fills the alignment gap. Defaults to space. (Cannot be a line breaking character)
    :param width: display width of the aligned text. Defaults to width of the terminal.
    :param tab_width: any tabs in the text will be replaced with this many spaces. if fill_char is a tab, then it will
                      be converted to one space.
    :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
                     replaced by a '…' character. Defaults to False.
    :return: centered text
    :raises TypeError: if fill_char is more than one character (not including ANSI style sequences)
    :raises ValueError: if text or fill_char contains an unprintable character
    :raises ValueError: if width is less than 1
    """
    return align_text(text, TextAlignment.CENTER, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate)

cmd2.utils.truncate_line

truncate_line(line, max_width, *, tab_width=4)

Truncate a single line to fit within a given display width. Any portion of the string that is truncated is replaced by a '…' character. Supports characters with display widths greater than 1. ANSI style sequences do not count toward the display width.

If there are ANSI style sequences in the string after where truncation occurs, this function will append them to the returned string.

This is done to prevent issues caused in cases like: truncate_line(Fg.BLUE + hello + Fg.RESET, 3) In this case, "hello" would be truncated before Fg.RESET resets the color from blue. Appending the remaining style sequences makes sure the style is in the same state had the entire string been printed. align_text() relies on this behavior when preserving style over multiple lines.

PARAMETER DESCRIPTION
line

text to truncate

TYPE: str

max_width

the maximum display width the resulting string is allowed to have

TYPE: int

tab_width

any tabs in the text will be replaced with this many spaces

TYPE: int DEFAULT: 4

RETURNS DESCRIPTION
str

line that has a display width less than or equal to width

RAISES DESCRIPTION
ValueError

if text contains an unprintable character like a newline

ValueError

if max_width is less than 1

Source code in cmd2/utils.py
def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str:
    """
    Truncate a single line to fit within a given display width. Any portion of the string that is truncated
    is replaced by a '…' character. Supports characters with display widths greater than 1. ANSI style sequences
    do not count toward the display width.

    If there are ANSI style sequences in the string after where truncation occurs, this function will append them
    to the returned string.

    This is done to prevent issues caused in cases like: truncate_line(Fg.BLUE + hello + Fg.RESET, 3)
    In this case, "hello" would be truncated before Fg.RESET resets the color from blue. Appending the remaining style
    sequences makes sure the style is in the same state had the entire string been printed. align_text() relies on this
    behavior when preserving style over multiple lines.

    :param line: text to truncate
    :param max_width: the maximum display width the resulting string is allowed to have
    :param tab_width: any tabs in the text will be replaced with this many spaces
    :return: line that has a display width less than or equal to width
    :raises ValueError: if text contains an unprintable character like a newline
    :raises ValueError: if max_width is less than 1
    """
    import io

    from . import (
        ansi,
    )

    # Handle tabs
    line = line.replace('\t', ' ' * tab_width)

    if ansi.style_aware_wcswidth(line) == -1:
        raise (ValueError("text contains an unprintable character"))

    if max_width < 1:
        raise ValueError("max_width must be at least 1")

    if ansi.style_aware_wcswidth(line) <= max_width:
        return line

    # Find all style sequences in the line
    styles_dict = get_styles_dict(line)

    # Add characters one by one and preserve all style sequences
    done = False
    index = 0
    total_width = 0
    truncated_buf = io.StringIO()

    while not done:
        # Check if a style sequence is at this index. These don't count toward display width.
        if index in styles_dict:
            truncated_buf.write(styles_dict[index])
            style_len = len(styles_dict[index])
            styles_dict.pop(index)
            index += style_len
            continue

        char = line[index]
        char_width = ansi.style_aware_wcswidth(char)

        # This char will make the text too wide, add the ellipsis instead
        if char_width + total_width >= max_width:
            char = constants.HORIZONTAL_ELLIPSIS
            char_width = ansi.style_aware_wcswidth(char)
            done = True

        total_width += char_width
        truncated_buf.write(char)
        index += 1

    # Filter out overridden styles from the remaining ones
    remaining_styles = _remove_overridden_styles(list(styles_dict.values()))

    # Append the remaining styles to the truncated text
    truncated_buf.write(''.join(remaining_styles))

    return truncated_buf.getvalue()

Miscellaneous

cmd2.utils.to_bool

to_bool(val)

Converts anything to a boolean based on its value.

Strings like "True", "true", "False", and "false" return True, True, False, and False respectively. All other values are converted using bool()

PARAMETER DESCRIPTION
val

value being converted

TYPE: Any

RETURNS DESCRIPTION
bool

boolean value expressed in the passed in value

RAISES DESCRIPTION
ValueError

if the string does not contain a value corresponding to a boolean value

Source code in cmd2/utils.py
def to_bool(val: Any) -> bool:
    """Converts anything to a boolean based on its value.

    Strings like "True", "true", "False", and "false" return True, True, False, and False
    respectively. All other values are converted using bool()

    :param val: value being converted
    :return: boolean value expressed in the passed in value
    :raises ValueError: if the string does not contain a value corresponding to a boolean value
    """
    if isinstance(val, str):
        if val.capitalize() == str(True):
            return True
        elif val.capitalize() == str(False):
            return False
        raise ValueError("must be True or False (case-insensitive)")
    elif isinstance(val, bool):
        return val
    else:
        return bool(val)

cmd2.utils.categorize

categorize(func, category)

Categorize a function.

The help command output will group the passed function under the specified category heading

PARAMETER DESCRIPTION
func

function or list of functions to categorize

TYPE: Union[Callable[..., Any], Iterable[Callable[..., Any]]]

category

category to put it in Example: py import cmd2 class MyApp(cmd2.Cmd): def do_echo(self, arglist): self.poutput(' '.join(arglist) cmd2.utils.categorize(do_echo, "Text Processing") For an alternative approach to categorizing commands using a decorator, see cmd2.decorators.with_category

TYPE: str

Source code in cmd2/utils.py
def categorize(func: Union[Callable[..., Any], Iterable[Callable[..., Any]]], category: str) -> None:
    """Categorize a function.

    The help command output will group the passed function under the
    specified category heading

    :param func: function or list of functions to categorize
    :param category: category to put it in

    Example:

    ```py
    import cmd2
    class MyApp(cmd2.Cmd):
      def do_echo(self, arglist):
        self.poutput(' '.join(arglist)

      cmd2.utils.categorize(do_echo, "Text Processing")
    ```

    For an alternative approach to categorizing commands using a decorator, see [cmd2.decorators.with_category][]
    """
    if isinstance(func, Iterable):
        for item in func:
            setattr(item, constants.CMD_ATTR_HELP_CATEGORY, category)
    else:
        if inspect.ismethod(func):
            setattr(func.__func__, constants.CMD_ATTR_HELP_CATEGORY, category)  # type: ignore[attr-defined]
        else:
            setattr(func, constants.CMD_ATTR_HELP_CATEGORY, category)

cmd2.utils.remove_duplicates

remove_duplicates(list_to_prune)

Removes duplicates from a list while preserving order of the items.

PARAMETER DESCRIPTION
list_to_prune

the list being pruned of duplicates

TYPE: List[_T]

RETURNS DESCRIPTION
List[_T]

The pruned list

Source code in cmd2/utils.py
def remove_duplicates(list_to_prune: List[_T]) -> List[_T]:
    """Removes duplicates from a list while preserving order of the items.

    :param list_to_prune: the list being pruned of duplicates
    :return: The pruned list
    """
    temp_dict: collections.OrderedDict[_T, Any] = collections.OrderedDict()
    for item in list_to_prune:
        temp_dict[item] = None

    return list(temp_dict.keys())

cmd2.utils.alphabetical_sort

alphabetical_sort(list_to_sort)

Sorts a list of strings alphabetically.

For example: ['a1', 'A11', 'A2', 'a22', 'a3']

To sort a list in place, don't call this method, which makes a copy. Instead, do this:

my_list.sort(key=norm_fold)

PARAMETER DESCRIPTION
list_to_sort

the list being sorted

TYPE: Iterable[str]

RETURNS DESCRIPTION
List[str]

the sorted list

Source code in cmd2/utils.py
def alphabetical_sort(list_to_sort: Iterable[str]) -> List[str]:
    """Sorts a list of strings alphabetically.

    For example: ['a1', 'A11', 'A2', 'a22', 'a3']

    To sort a list in place, don't call this method, which makes a copy. Instead, do this:

    my_list.sort(key=norm_fold)

    :param list_to_sort: the list being sorted
    :return: the sorted list
    """
    return sorted(list_to_sort, key=norm_fold)

cmd2.utils.natural_sort

natural_sort(list_to_sort)

Sorts a list of strings case insensitively as well as numerically.

For example: ['a1', 'A2', 'a3', 'A11', 'a22']

To sort a list in place, don't call this method, which makes a copy. Instead, do this:

my_list.sort(key=natural_keys)

PARAMETER DESCRIPTION
list_to_sort

the list being sorted

TYPE: Iterable[str]

RETURNS DESCRIPTION
List[str]

the list sorted naturally

Source code in cmd2/utils.py
def natural_sort(list_to_sort: Iterable[str]) -> List[str]:
    """
    Sorts a list of strings case insensitively as well as numerically.

    For example: ['a1', 'A2', 'a3', 'A11', 'a22']

    To sort a list in place, don't call this method, which makes a copy. Instead, do this:

    my_list.sort(key=natural_keys)

    :param list_to_sort: the list being sorted
    :return: the list sorted naturally
    """
    return sorted(list_to_sort, key=natural_keys)

cmd2.utils.suggest_similar

suggest_similar(requested_command, options, similarity_function_to_use=None)

Given a requested command and an iterable of possible options returns the most similar (if any is similar)

PARAMETER DESCRIPTION
requested_command

The command entered by the user

TYPE: str

options

The list of available commands to search for the most similar

TYPE: Iterable[str]

similarity_function_to_use

An optional callable to use to compare commands

TYPE: Optional[Callable[[str, str], float]] DEFAULT: None

RETURNS DESCRIPTION
Optional[str]

The most similar command or None if no one is similar

Source code in cmd2/utils.py
def suggest_similar(
    requested_command: str, options: Iterable[str], similarity_function_to_use: Optional[Callable[[str, str], float]] = None
) -> Optional[str]:
    """
    Given a requested command and an iterable of possible options returns the most similar (if any is similar)

    :param requested_command: The command entered by the user
    :param options: The list of available commands to search for the most similar
    :param similarity_function_to_use: An optional callable to use to compare commands
    :return: The most similar command or None if no one is similar
    """

    proposed_command = None
    best_simil = MIN_SIMIL_TO_CONSIDER
    requested_command_to_compare = requested_command.lower()
    similarity_function_to_use = similarity_function_to_use or similarity_function
    for each in options:
        simil = similarity_function_to_use(each.lower(), requested_command_to_compare)
        if best_simil < simil:
            best_simil = simil
            proposed_command = each
    return proposed_command