Skip to content

cmd2.Cmd

cmd2.Cmd

Cmd(
    completekey=None,
    stdin=None,
    stdout=None,
    *,
    allow_cli_args=True,
    allow_clipboard=True,
    allow_redirection=True,
    auto_load_commands=False,
    auto_suggest=True,
    bottom_toolbar=False,
    command_sets=None,
    include_ipy=False,
    include_py=False,
    intro="",
    multiline_commands=None,
    persistent_history_file="",
    persistent_history_length=1000,
    shortcuts=None,
    silence_startup_script=False,
    startup_script="",
    suggest_similar_command=False,
    terminators=None,
)

An easy but powerful framework for writing line-oriented command interpreters.

Extends the Python Standard Library's cmd package by adding a lot of useful features to the out of the box configuration.

Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes.

Easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.

PARAMETER DESCRIPTION
completekey

name of a completion key, default to 'tab'. (If None or an empty string, 'tab' is used)

TYPE: str | None DEFAULT: None

stdin

alternate input file object, if not specified, sys.stdin is used

TYPE: TextIO | None DEFAULT: None

stdout

alternate output file object, if not specified, sys.stdout is used

TYPE: TextIO | None DEFAULT: None

allow_cli_args

if True, then cmd2.Cmd.init will process command line arguments as either commands to be run. This should be set to False if your application parses its own command line arguments.

TYPE: bool DEFAULT: True

allow_clipboard

If False, cmd2 will disable clipboard interactions

TYPE: bool DEFAULT: True

allow_redirection

If False, prevent output redirection and piping to shell commands. This parameter prevents redirection and piping, but does not alter parsing behavior. A user can still type redirection and piping tokens, and they will be parsed as such but they won't do anything.

TYPE: bool DEFAULT: True

auto_load_commands

If True, cmd2 will check for all subclasses of CommandSet that are currently loaded by Python and automatically instantiate and register all commands. If False, CommandSets must be manually installed with register_command_set.

TYPE: bool DEFAULT: False

auto_suggest

If True, cmd2 will provide fish shell style auto-suggestions based on history. User can press right-arrow key to accept the provided suggestion.

TYPE: bool DEFAULT: True

bottom_toolbar

if True, then a bottom toolbar will be displayed.

TYPE: bool DEFAULT: False

command_sets

Provide CommandSet instances to load during cmd2 initialization. This allows CommandSets with custom constructor parameters to be loaded. This also allows the a set of CommandSets to be provided when auto_load_commands is set to False

TYPE: Iterable[CommandSet[Any]] | None DEFAULT: None

include_ipy

should the "ipy" command be included for an embedded IPython shell

TYPE: bool DEFAULT: False

include_py

should the "py" command be included for an embedded Python shell

TYPE: bool DEFAULT: False

intro

introduction to display at startup

TYPE: RenderableType DEFAULT: ''

multiline_commands

Iterable of commands allowed to accept multi-line input

TYPE: Iterable[str] | None DEFAULT: None

persistent_history_file

file path to load a persistent cmd2 command history from

TYPE: str DEFAULT: ''

persistent_history_length

max number of history items to write to the persistent history file

TYPE: int DEFAULT: 1000

shortcuts

Mapping containing shortcuts for commands. If not supplied, then defaults to constants.DEFAULT_SHORTCUTS. If you do not want any shortcuts, pass None and an empty dictionary will be created.

TYPE: Mapping[str, str] | None DEFAULT: None

silence_startup_script

if True, then the startup script's output will be suppressed. Anything written to stderr will still display.

TYPE: bool DEFAULT: False

startup_script

file path to a script to execute at startup

TYPE: str DEFAULT: ''

suggest_similar_command

if True, then when a command is not found, cmd2.Cmd will look for similar commands and suggest them.

TYPE: bool DEFAULT: False

terminators

Iterable of characters that terminate a command. These are mainly intended for terminating multiline commands, but will also terminate single-line commands. If not supplied, the default is a semicolon. If your app only contains single-line commands and you want terminators to be treated as literals by the parser, then set this to None.

TYPE: Iterable[str] | None DEFAULT: None

Source code in cmd2/cmd2.py
def __init__(
    self,
    completekey: str | None = None,
    stdin: TextIO | None = None,
    stdout: TextIO | None = None,
    *,
    allow_cli_args: bool = True,
    allow_clipboard: bool = True,
    allow_redirection: bool = True,
    auto_load_commands: bool = False,
    auto_suggest: bool = True,
    bottom_toolbar: bool = False,
    command_sets: Iterable[CommandSet[Any]] | None = None,
    include_ipy: bool = False,
    include_py: bool = False,
    intro: RenderableType = "",
    multiline_commands: Iterable[str] | None = None,
    persistent_history_file: str = "",
    persistent_history_length: int = 1000,
    shortcuts: Mapping[str, str] | None = None,
    silence_startup_script: bool = False,
    startup_script: str = "",
    suggest_similar_command: bool = False,
    terminators: Iterable[str] | None = None,
) -> None:
    """Easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.

    :param completekey: name of a completion key, default to 'tab'. (If None or an empty string, 'tab' is used)
    :param stdin: alternate input file object, if not specified, sys.stdin is used
    :param stdout: alternate output file object, if not specified, sys.stdout is used
    :param allow_cli_args: if ``True``, then [cmd2.Cmd.__init__][] will process command
                           line arguments as either commands to be run. This should be
                           set to ``False`` if your application parses its own command line
                           arguments.
    :param allow_clipboard: If False, cmd2 will disable clipboard interactions
    :param allow_redirection: If ``False``, prevent output redirection and piping to shell
                              commands. This parameter prevents redirection and piping, but
                              does not alter parsing behavior. A user can still type
                              redirection and piping tokens, and they will be parsed as such
                              but they won't do anything.
    :param auto_load_commands: If True, cmd2 will check for all subclasses of `CommandSet`
                               that are currently loaded by Python and automatically
                               instantiate and register all commands. If False, CommandSets
                               must be manually installed with `register_command_set`.
    :param auto_suggest: If True, cmd2 will provide fish shell style auto-suggestions
                        based on history. User can press right-arrow key to accept the
                        provided suggestion.
    :param bottom_toolbar: if ``True``, then a bottom toolbar will be displayed.
    :param command_sets: Provide CommandSet instances to load during cmd2 initialization.
                         This allows CommandSets with custom constructor parameters to be
                         loaded.  This also allows the a set of CommandSets to be provided
                         when `auto_load_commands` is set to False
    :param include_ipy: should the "ipy" command be included for an embedded IPython shell
    :param include_py: should the "py" command be included for an embedded Python shell
    :param intro: introduction to display at startup
    :param multiline_commands: Iterable of commands allowed to accept multi-line input
    :param persistent_history_file: file path to load a persistent cmd2 command history from
    :param persistent_history_length: max number of history items to write
                                      to the persistent history file
    :param shortcuts: Mapping containing shortcuts for commands. If not supplied,
                      then defaults to constants.DEFAULT_SHORTCUTS. If you do not want
                      any shortcuts, pass None and an empty dictionary will be created.
    :param silence_startup_script: if ``True``, then the startup script's output will be
                                   suppressed. Anything written to stderr will still display.
    :param startup_script: file path to a script to execute at startup
    :param suggest_similar_command: if ``True``, then when a command is not found,
                                    [cmd2.Cmd][] will look for similar commands and suggest them.
    :param terminators: Iterable of characters that terminate a command. These are mainly
                        intended for terminating multiline commands, but will also
                        terminate single-line commands. If not supplied, the default
                        is a semicolon. If your app only contains single-line commands
                        and you want terminators to be treated as literals by the parser,
                        then set this to None.
    """
    # Check if py or ipy need to be disabled in this instance
    if not include_py:
        setattr(self, "do_py", None)  # noqa: B010
    if not include_ipy:
        setattr(self, "do_ipy", None)  # noqa: B010

    # initialize plugin system
    # needs to be done before we most of the other stuff below
    self._initialize_plugin_system()

    # Configure a few defaults
    self.prompt: str = self.DEFAULT_PROMPT
    self.intro = intro

    if not completekey:
        completekey = self.DEFAULT_COMPLETEKEY

    # What to use for standard input
    if stdin is not None:
        self.stdin = stdin
    else:
        self.stdin = sys.stdin

    # Standard output stream. The interactive UI remains attached to this initial
    # stream even when self.stdout is temporarily swapped during command output
    # redirection.
    if stdout is not None:
        self.stdout = stdout
    else:
        self.stdout = sys.stdout

    # Attributes which should NOT be dynamically settable via the set command at runtime
    self.allow_redirection = allow_redirection  # Security setting to prevent redirection of stdout

    # If True, cmd2 treats redirected input (pipes/files) as an interactive session.
    # It will display the prompt before reading each line to synchronize with
    # automation tools (like Pexpect) and will skip echoing the input to prevent
    # duplicate prompts in the output.
    self.interactive_pipe = False

    # Attributes which ARE dynamically settable via the set command at runtime
    self.debug = False
    self.echo = False
    self.editor = self.DEFAULT_EDITOR
    self.quiet = False  # Do not suppress nonessential output
    self.scripts_add_to_history = True  # Scripts and pyscripts add commands to history
    self.timing = False  # Prints elapsed time for each command

    # Default settings for Rich tracebacks created by format_exception().
    # This dictionary can contain any parameter accepted by the
    # rich.traceback.Traceback class. You can modify it to adjust
    # the detail and layout of tracebacks.
    self.traceback_kwargs: dict[str, Any] = {
        "width": 100,
        "code_width": None,  # Show all code characters
        "show_locals": False,
        "max_frames": 100,
        "word_wrap": True,  # Wrap long lines of code instead of truncate
        "indent_guides": True,
    }

    # Cached Rich consoles used by core print methods.
    self._console_cache = _ConsoleCache()

    # The maximum number of items to display in a completion table. If the number of completion
    # suggestions exceeds this number, then no table will appear.
    self.max_completion_table_items: int = 50

    # The maximum number of completion results to display in a single column (CompleteStyle.COLUMN).
    # If the number of results exceeds this, CompleteStyle.MULTI_COLUMN will be used.
    self.max_column_completion_results: int = 7

    # A dictionary mapping settable names to their Settable instance
    self._settables: dict[str, Settable] = {}
    self._always_prefix_settables: bool = False

    # CommandSet containers
    self._installed_command_sets: set[CommandSet[Any]] = set()
    self._cmd_to_command_sets: dict[str, CommandSet[Any]] = {}

    self.build_settables()

    # Use as prompt for multiline commands on the 2nd+ line of input
    self.continuation_prompt: str = "> "

    # Allow access to your application in embedded Python shells and pyscripts via self
    self.self_in_py = False

    # Commands to exclude from the help menu and completion
    self.hidden_commands = ["_eof", "_relative_run_script"]

    # Initialize history from a persistent history file (if present)
    self.persistent_history_file = ""
    self._persistent_history_length = persistent_history_length
    self._initialize_history(persistent_history_file)

    # Create the main PromptSession
    self.bottom_toolbar = bottom_toolbar
    self.main_session = self._create_main_session(auto_suggest, completekey)

    # The session currently holding focus (either the main REPL or a command's
    # custom prompt). Completion and UI logic should reference this variable
    # to ensure they modify the correct session state.
    self.active_session = self.main_session

    # Commands to exclude from the history command
    self.exclude_from_history = ["_eof", "history"]

    # Dictionary of macro names and their values
    self.macros: dict[str, Macro] = {}

    # Keeps track of typed command history in the Python shell
    self._py_history: list[str] = []

    # The name by which Python environments refer to the PyBridge to call app commands
    self.py_bridge_name = "app"

    # Defines app-specific variables/functions available in Python shells and pyscripts
    self.py_locals: dict[str, Any] = {}

    # True if running inside a Python shell or pyscript, False otherwise
    self._in_py = False

    self.statement_parser: StatementParser = StatementParser(
        terminators=terminators, multiline_commands=multiline_commands, shortcuts=shortcuts
    )

    # Stores results from the last command run to enable usage of results in Python shells and pyscripts
    self.last_result: Any = None

    # Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command
    self._script_dir: list[str] = []

    # Context manager used to protect critical sections in the main thread from stopping due to a KeyboardInterrupt
    self.sigint_protection: utils.ContextFlag = utils.ContextFlag()

    # If the current command created a process to pipe to, then this will be a ProcReader object.
    # Otherwise it will be None. It's used to know when a pipe process can be killed and/or waited upon.
    self._cur_pipe_proc_reader: utils.ProcReader | None = None

    # Used to keep track of whether we are redirecting or piping output
    self._redirecting = False

    # Set text which prints right before all of the help tables are listed.
    self.doc_leader = ""

    # The error that prints when no help information can be found
    self.help_error = "No help on {}"

    # The error that prints when a non-existent command is run
    self.default_error = "{} is not a recognized command, alias, or macro."

    # If non-empty, this string will be displayed if a broken pipe error occurs
    self.broken_pipe_warning = ""

    # Commands that will run at the beginning of the command loop
    self._startup_commands: list[str] = []

    # Store initial termios settings to restore after each command.
    # This is a faster way of accomplishing what "stty sane" does.
    self._initial_termios_settings = None
    if not sys.platform.startswith("win") and self.stdin.isatty():
        try:
            import io
            import termios

            self._initial_termios_settings = termios.tcgetattr(self.stdin.fileno())
        except (ImportError, io.UnsupportedOperation, termios.error):
            # This can happen if termios isn't available or stdin is a pseudo-TTY
            self._initial_termios_settings = None

    # If a startup script is provided and exists, then execute it in the startup commands
    if startup_script:
        startup_script = os.path.abspath(os.path.expanduser(startup_script))
        if os.path.exists(startup_script):
            script_cmd = f"run_script {su.quote(startup_script)}"
            if silence_startup_script:
                script_cmd += f" {constants.REDIRECTION_OVERWRITE} {os.devnull}"
            self._startup_commands.append(script_cmd)

    # Check for command line args
    if allow_cli_args:
        parser = argparse_utils.DEFAULT_ARGUMENT_PARSER()
        _callopts, callargs = parser.parse_known_args()

        # If commands were supplied at invocation, then add them to the command queue
        if callargs:
            self._startup_commands.extend(callargs)

    # Set the pager(s) for use when displaying output using a pager
    if sys.platform.startswith("win"):
        self.pager = self.pager_chop = "more"
    else:
        # Here is the meaning of the various flags we are using with the less command:
        # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped
        # -R causes ANSI "style" escape sequences to be output in raw form (i.e. colors are displayed)
        # -X disables sending the termcap initialization and deinitialization strings to the terminal
        # -F causes less to automatically exit if the entire file can be displayed on the first screen
        self.pager = "less -RXF"
        self.pager_chop = "less -SRXF"

    # This boolean flag stores whether cmd2 will allow clipboard related features
    self.allow_clipboard = allow_clipboard

    # This determines the value returned by cmdloop() when exiting the application
    self.exit_code = 0

    # Commands disabled during specific application states
    # Key: Command name | Value: DisabledCommand object
    # NOTE: Use disable_command() and enable_command() to modify this dictionary.
    self.disabled_commands: dict[str, DisabledCommand] = {}

    # Categories of commands to be disabled
    # Key: Category name | Value: Message to display
    # NOTE: Use disable_category() and enable_category() to modify this dictionary.
    self.disabled_categories: dict[str, str] = {}

    # Command parsers for this Cmd instance.
    self.command_parsers: CommandParsers = CommandParsers(self)

    # Members related to printing asynchronous alerts
    self._alert_queue: deque[AsyncAlert] = deque()
    self._alert_condition = threading.Condition()
    self._alert_allowed = False
    self._alert_shutdown = False
    self._alert_thread: threading.Thread | None = None
    self._alert_prompt_timestamp: float = 0.0  # Uses time.monotonic()

    # Add functions decorated to be subcommands
    self._register_subcommands(self)

    ############################################################################################################
    # The following code block loads CommandSets, verifies command names, and registers subcommands.
    # This block should appear after all attributes have been created since the registration code
    # depends on them and it's possible a module's on_register() method may need to access some.
    ############################################################################################################
    # Load modular commands
    if command_sets:
        for command_set in command_sets:
            self.register_command_set(command_set)

    if auto_load_commands:
        self._autoload_commands()

    # Verify commands don't have invalid names (like starting with a shortcut)
    for cur_cmd in self.get_all_commands():
        valid, errmsg = self.statement_parser.is_valid_command(cur_cmd)
        if not valid:
            raise ValueError(f"Invalid command name '{cur_cmd}': {errmsg}")

    self.suggest_similar_command = suggest_similar_command
    self.default_suggestion_message = "Did you mean {}?"

    # the current command being executed
    self.current_command: Statement | None = None

DEFAULT_COMPLETEKEY class-attribute

DEFAULT_COMPLETEKEY = 'tab'

DEFAULT_EDITOR class-attribute

DEFAULT_EDITOR = find_editor()

DEFAULT_PROMPT class-attribute

DEFAULT_PROMPT = '(Cmd) '

DEFAULT_CATEGORY class-attribute

DEFAULT_CATEGORY = 'Cmd2 Commands'

MISC_HEADER class-attribute

MISC_HEADER = 'Miscellaneous Help Topics'

prompt instance-attribute

prompt = DEFAULT_PROMPT

intro instance-attribute

intro = intro

stdin instance-attribute

stdin = stdin

stdout instance-attribute

stdout = stdout

allow_redirection instance-attribute

allow_redirection = allow_redirection

interactive_pipe instance-attribute

interactive_pipe = False

debug instance-attribute

debug = False

echo instance-attribute

echo = False

editor instance-attribute

editor = DEFAULT_EDITOR

quiet instance-attribute

quiet = False

scripts_add_to_history instance-attribute

scripts_add_to_history = True

timing instance-attribute

timing = False

traceback_kwargs instance-attribute

traceback_kwargs = {
    "width": 100,
    "code_width": None,
    "show_locals": False,
    "max_frames": 100,
    "word_wrap": True,
    "indent_guides": True,
}

max_completion_table_items instance-attribute

max_completion_table_items = 50

max_column_completion_results instance-attribute

max_column_completion_results = 7

continuation_prompt instance-attribute

continuation_prompt = '> '

self_in_py instance-attribute

self_in_py = False

hidden_commands instance-attribute

hidden_commands = ['_eof', '_relative_run_script']

persistent_history_file instance-attribute

persistent_history_file = ''

bottom_toolbar instance-attribute

bottom_toolbar = bottom_toolbar

main_session instance-attribute

main_session = _create_main_session(
    auto_suggest, completekey
)

active_session instance-attribute

active_session = main_session

exclude_from_history instance-attribute

exclude_from_history = ['_eof', 'history']

macros instance-attribute

macros = {}

py_bridge_name instance-attribute

py_bridge_name = 'app'

py_locals instance-attribute

py_locals = {}

statement_parser instance-attribute

statement_parser = StatementParser(
    terminators=terminators,
    multiline_commands=multiline_commands,
    shortcuts=shortcuts,
)

last_result instance-attribute

last_result = None

sigint_protection instance-attribute

sigint_protection = ContextFlag()

doc_leader instance-attribute

doc_leader = ''

help_error instance-attribute

help_error = 'No help on {}'

default_error instance-attribute

default_error = (
    "{} is not a recognized command, alias, or macro."
)

broken_pipe_warning instance-attribute

broken_pipe_warning = ''

pager instance-attribute

pager = 'more'

pager_chop instance-attribute

pager_chop = 'more'

allow_clipboard instance-attribute

allow_clipboard = allow_clipboard

exit_code instance-attribute

exit_code = 0

disabled_commands instance-attribute

disabled_commands = {}

disabled_categories instance-attribute

disabled_categories = {}

command_parsers instance-attribute

command_parsers = CommandParsers(self)

suggest_similar_command instance-attribute

suggest_similar_command = suggest_similar_command

default_suggestion_message instance-attribute

default_suggestion_message = 'Did you mean {}?'

current_command instance-attribute

current_command = None

always_prefix_settables property writable

always_prefix_settables

Flags whether CommandSet settable values should always be prefixed.

RETURNS DESCRIPTION
bool

True if CommandSet settable values will always be prefixed. False if not.

settables property

settables

Get all available user-settable attributes. This includes settables defined in installed CommandSets.

RETURNS DESCRIPTION
Mapping[str, Settable]

Mapping from attribute-name to Settable of all user-settable attributes from

allow_style property writable

allow_style

Property needed to support do_set when it reads allow_style.

traceback_show_locals property writable

traceback_show_locals

Property needed to support do_set when it reads traceback_show_locals.

traceback_width property writable

traceback_width

Property needed to support do_set when it reads traceback_width.

visible_prompt property

visible_prompt

Read-only property to get the visible prompt with any ANSI style sequences stripped.

Useful for test frameworks doing comparisons without having to worry about color/style.

RETURNS DESCRIPTION
str

the stripped prompt

aliases property

aliases

Read-only property to access the aliases stored in the StatementParser.

CommandDataType class-attribute instance-attribute

CommandDataType = TypeVar('CommandDataType')

find_commandsets

find_commandsets(commandset_type, *, subclass_match=False)

Find all CommandSets that match the provided CommandSet type.

By default, locates a CommandSet that is an exact type match but may optionally return all CommandSets that are sub-classes of the provided type

PARAMETER DESCRIPTION
commandset_type

CommandSet sub-class type to search for

TYPE: type[CommandSet[Any]]

subclass_match

If True, return all sub-classes of provided type, otherwise only search for exact match

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
list[CommandSet[Any]]

Matching CommandSets

Source code in cmd2/cmd2.py
def find_commandsets(
    self, commandset_type: type[CommandSet[Any]], *, subclass_match: bool = False
) -> list[CommandSet[Any]]:
    """Find all CommandSets that match the provided CommandSet type.

    By default, locates a CommandSet that is an exact type match but may optionally return all CommandSets that
    are sub-classes of the provided type
    :param commandset_type: CommandSet sub-class type to search for
    :param subclass_match: If True, return all sub-classes of provided type, otherwise only search for exact match
    :return: Matching CommandSets
    """
    return [
        cmdset
        for cmdset in self._installed_command_sets
        if type(cmdset) == commandset_type or (subclass_match and isinstance(cmdset, commandset_type))  # noqa: E721
    ]

find_commandset_for_command

find_commandset_for_command(command_name)

Find the CommandSet that registered the command name.

PARAMETER DESCRIPTION
command_name

command name to search

TYPE: str

RETURNS DESCRIPTION
CommandSet[Any] | None

CommandSet that provided the command

Source code in cmd2/cmd2.py
def find_commandset_for_command(self, command_name: str) -> CommandSet[Any] | None:
    """Find the CommandSet that registered the command name.

    :param command_name: command name to search
    :return: CommandSet that provided the command
    """
    return self._cmd_to_command_sets.get(command_name)

register_command_set

register_command_set(cmdset)

Installs a CommandSet, loading all commands defined in the CommandSet.

PARAMETER DESCRIPTION
cmdset

CommandSet to load

TYPE: CommandSet[Any]

Source code in cmd2/cmd2.py
def register_command_set(self, cmdset: CommandSet[Any]) -> None:
    """Installs a CommandSet, loading all commands defined in the CommandSet.

    :param cmdset: CommandSet to load
    """
    existing_commandset_types = [type(command_set) for command_set in self._installed_command_sets]
    if type(cmdset) in existing_commandset_types:
        raise CommandSetRegistrationError("CommandSet " + type(cmdset).__name__ + " is already installed")

    all_settables = self.settables
    if self.always_prefix_settables:
        if not cmdset.settable_prefix.strip():
            raise CommandSetRegistrationError("CommandSet settable prefix must not be empty")
        for key in cmdset.settables:
            prefixed_name = f"{cmdset.settable_prefix}.{key}"
            if prefixed_name in all_settables:
                raise CommandSetRegistrationError(f"Duplicate settable: {key}")

    else:
        for key in cmdset.settables:
            if key in all_settables:
                raise CommandSetRegistrationError(f"Duplicate settable {key} is already registered")

    cmdset.on_register(self)
    methods = cast(
        list[tuple[str, Callable[..., Any]]],
        inspect.getmembers(
            cmdset,
            predicate=lambda meth: (  # type: ignore[arg-type]
                isinstance(meth, Callable)  # type: ignore[arg-type]
                and hasattr(meth, "__name__")
                and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
            ),
        ),
    )

    installed_attributes = []
    try:
        for cmd_func_name, command_method in methods:
            command = cmd_func_name[len(COMMAND_FUNC_PREFIX) :]

            self._install_command_function(cmd_func_name, command_method, type(cmdset).__name__)
            installed_attributes.append(cmd_func_name)

            completer_func_name = COMPLETER_FUNC_PREFIX + command
            cmd_completer = getattr(cmdset, completer_func_name, None)
            if cmd_completer is not None:
                self._install_completer_function(command, cmd_completer)
                installed_attributes.append(completer_func_name)

            help_func_name = HELP_FUNC_PREFIX + command
            cmd_help = getattr(cmdset, help_func_name, None)
            if cmd_help is not None:
                self._install_help_function(command, cmd_help)
                installed_attributes.append(help_func_name)

            self._cmd_to_command_sets[command] = cmdset

            # If this command is in a disabled category, then disable it
            command_category = self._get_command_category(command_method)
            if command_category in self.disabled_categories:
                message_to_print = self.disabled_categories[command_category]
                self.disable_command(command, message_to_print)

        self._installed_command_sets.add(cmdset)

        self._register_subcommands(cmdset)
        cmdset.on_registered()
    except Exception:
        cmdset.on_unregister()
        for attrib in installed_attributes:
            delattr(self, attrib)
        if cmdset in self._installed_command_sets:
            self._installed_command_sets.remove(cmdset)
        if cmdset in self._cmd_to_command_sets.values():
            self._cmd_to_command_sets = {key: val for key, val in self._cmd_to_command_sets.items() if val is not cmdset}
        cmdset.on_unregistered()
        raise

unregister_command_set

unregister_command_set(cmdset)

Uninstalls a CommandSet and unloads all associated commands.

PARAMETER DESCRIPTION
cmdset

CommandSet to uninstall

TYPE: CommandSet[Any]

Source code in cmd2/cmd2.py
def unregister_command_set(self, cmdset: CommandSet[Any]) -> None:
    """Uninstalls a CommandSet and unloads all associated commands.

    :param cmdset: CommandSet to uninstall
    """
    if cmdset in self._installed_command_sets:
        self._check_uninstallable(cmdset)
        cmdset.on_unregister()
        self._unregister_subcommands(cmdset)

        methods: list[tuple[str, Callable[..., Any]]] = inspect.getmembers(
            cmdset,
            predicate=lambda meth: (  # type: ignore[arg-type]
                isinstance(meth, Callable)  # type: ignore[arg-type]
                and hasattr(meth, "__name__")
                and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
            ),
        )

        for cmd_func_name, command_method in methods:
            command = cmd_func_name[len(COMMAND_FUNC_PREFIX) :]

            # Enable the command before uninstalling it to make sure we remove both
            # the real functions and the ones used by the DisabledCommand object.
            if command in self.disabled_commands:
                self.enable_command(command)

            if command in self._cmd_to_command_sets:
                del self._cmd_to_command_sets[command]

            # Only remove the parser if this is the actual
            # command since command synonyms don't own it.
            if cmd_func_name == command_method.__name__:
                self.command_parsers.remove(command_method)

            if hasattr(self, COMPLETER_FUNC_PREFIX + command):
                delattr(self, COMPLETER_FUNC_PREFIX + command)
            if hasattr(self, HELP_FUNC_PREFIX + command):
                delattr(self, HELP_FUNC_PREFIX + command)

            delattr(self, cmd_func_name)

        cmdset.on_unregistered()
        self._installed_command_sets.remove(cmdset)

get_root_parser_and_subcmd_path

get_root_parser_and_subcmd_path(command)

Tokenize a command string and resolve the associated root parser and relative subcommand path.

This helper handles the initial resolution of a command string (e.g., 'foo bar baz') by identifying 'foo' as the root command, retrieving its associated parser, and returning any remaining tokens (['bar', 'baz']) as a path relative to that parser for further traversal.

PARAMETER DESCRIPTION
command

full space-delimited command path leading to a parser (e.g. 'foo' or 'foo bar')

TYPE: str

RETURNS DESCRIPTION
tuple[Cmd2ArgumentParser, list[str]]

a tuple containing the Cmd2ArgumentParser for the root command and a list of strings representing the relative path to the desired hosting parser.

RAISES DESCRIPTION
ValueError

if the command is empty, the root command is not found, or the root command does not use an argparse parser.

Source code in cmd2/cmd2.py
def get_root_parser_and_subcmd_path(self, command: str) -> tuple[Cmd2ArgumentParser, list[str]]:
    """Tokenize a command string and resolve the associated root parser and relative subcommand path.

    This helper handles the initial resolution of a command string (e.g., 'foo bar baz') by
    identifying 'foo' as the root command, retrieving its associated parser, and returning
    any remaining tokens (['bar', 'baz']) as a path relative to that parser for further traversal.

    :param command: full space-delimited command path leading to a parser (e.g. 'foo' or 'foo bar')
    :return: a tuple containing the Cmd2ArgumentParser for the root command and a list of
             strings representing the relative path to the desired hosting parser.
    :raises ValueError: if the command is empty, the root command is not found, or
                        the root command does not use an argparse parser.
    """
    tokens = command.split()
    if not tokens:
        raise ValueError("Command path cannot be empty")

    root_command = tokens[0]
    subcommand_path = tokens[1:]

    # Search for the base command function and verify it has a parser defined
    command_func = self.get_command_func(root_command)
    if command_func is None:
        raise ValueError(f"Root command '{root_command}' does not exist")

    root_parser = self.command_parsers.get(command_func)
    if root_parser is None:
        raise ValueError(f"Command '{root_command}' does not use argparse")

    return root_parser, subcommand_path

attach_subcommand

attach_subcommand(record)

Attach a parser as a subcommand to a command at the specified path.

PARAMETER DESCRIPTION
record

SubcommandRecord object describing the subcommand

TYPE: SubcommandRecord

RAISES DESCRIPTION
TypeError

if record.parser is not an instance of Cmd2ArgumentParser (or subclass)

ValueError

if the command path is invalid, doesn't support subcommands, or the subcommand already exists

Source code in cmd2/cmd2.py
def attach_subcommand(self, record: SubcommandRecord) -> None:
    """Attach a parser as a subcommand to a command at the specified path.

    :param record: SubcommandRecord object describing the subcommand
    :raises TypeError: if record.parser is not an instance of Cmd2ArgumentParser (or subclass)
    :raises ValueError: if the command path is invalid, doesn't support subcommands, or the
                        subcommand already exists
    """
    root_parser, subcommand_path = self.get_root_parser_and_subcmd_path(record.command)
    root_parser.attach_subcommand(record, subcommand_path=subcommand_path)

detach_subcommand

detach_subcommand(command, subcommand)

Detach a subcommand from a command at the specified path.

PARAMETER DESCRIPTION
command

full command path (space-delimited) leading to the parser hosting the subcommand to be detached (e.g. 'foo bar')

TYPE: str

subcommand

name of the subcommand to detach

TYPE: str

RETURNS DESCRIPTION
SubcommandRecord

a SubcommandRecord object describing the detached subcommand

RAISES DESCRIPTION
ValueError

if the command path is invalid or the subcommand doesn't exist

Source code in cmd2/cmd2.py
def detach_subcommand(self, command: str, subcommand: str) -> SubcommandRecord:
    """Detach a subcommand from a command at the specified path.

    :param command: full command path (space-delimited) leading to the parser hosting the
                    subcommand to be detached (e.g. 'foo bar')
    :param subcommand: name of the subcommand to detach
    :return: a SubcommandRecord object describing the detached subcommand
    :raises ValueError: if the command path is invalid or the subcommand doesn't exist
    """
    root_parser, subcommand_path = self.get_root_parser_and_subcmd_path(command)
    return root_parser.detach_subcommand(subcommand_path, subcommand)

detach_all_subcommands

detach_all_subcommands(command)

Detach all subcommands from a command at the specified path.

PARAMETER DESCRIPTION
command

full command path (space-delimited) leading to the parser hosting the subcommands to be detached (e.g. 'foo bar')

TYPE: str

RETURNS DESCRIPTION
list[SubcommandRecord]

a list of SubcommandRecord objects describing the detached subcommands

RAISES DESCRIPTION
ValueError

if the command path is invalid or the command doesn't support subcommands

Source code in cmd2/cmd2.py
def detach_all_subcommands(self, command: str) -> list[SubcommandRecord]:
    """Detach all subcommands from a command at the specified path.

    :param command: full command path (space-delimited) leading to the parser hosting the
                    subcommands to be detached (e.g. 'foo bar')
    :return: a list of SubcommandRecord objects describing the detached subcommands
    :raises ValueError: if the command path is invalid or the command doesn't support subcommands
    """
    root_parser, subcommand_path = self.get_root_parser_and_subcmd_path(command)
    return root_parser.detach_all_subcommands(subcommand_path)

add_settable

add_settable(settable)

Add a settable parameter to self.settables.

PARAMETER DESCRIPTION
settable

Settable object being added

TYPE: Settable

Source code in cmd2/cmd2.py
def add_settable(self, settable: Settable) -> None:
    """Add a settable parameter to ``self.settables``.

    :param settable: Settable object being added
    """
    if not self.always_prefix_settables and settable.name in self.settables and settable.name not in self._settables:
        raise KeyError(f"Duplicate settable: {settable.name}")
    self._settables[settable.name] = settable

remove_settable

remove_settable(name)

Remove a settable parameter from self.settables.

PARAMETER DESCRIPTION
name

name of the settable being removed

TYPE: str

RAISES DESCRIPTION
KeyError

if the Settable matches this name

Source code in cmd2/cmd2.py
def remove_settable(self, name: str) -> None:
    """Remove a settable parameter from ``self.settables``.

    :param name: name of the settable being removed
    :raises KeyError: if the Settable matches this name
    """
    try:
        del self._settables[name]
    except KeyError as exc:
        raise KeyError(name + " is not a settable parameter") from exc

build_settables

build_settables()

Create the dictionary of user-settable parameters.

Source code in cmd2/cmd2.py
def build_settables(self) -> None:
    """Create the dictionary of user-settable parameters."""

    def get_allow_style_choices(_cli_self: Cmd) -> Choices:
        """Complete allow_style values."""
        styles = [val.name.lower() for val in ru.AllowStyle]
        return Choices.from_values(styles)

    def allow_style_type(value: str) -> ru.AllowStyle:
        """Convert a string value into an ru.AllowStyle."""
        try:
            return ru.AllowStyle[value.upper()]
        except KeyError as ex:
            raise ValueError(
                f"must be {ru.AllowStyle.ALWAYS}, {ru.AllowStyle.NEVER}, or {ru.AllowStyle.TERMINAL} (case-insensitive)"
            ) from ex

    settable_description = Text.assemble(
        "Allow styled text in output (Options: ",
        (str(ru.AllowStyle.ALWAYS), Style(bold=True)),
        ", ",
        (str(ru.AllowStyle.NEVER), Style(bold=True)),
        ", ",
        (str(ru.AllowStyle.TERMINAL), Style(bold=True)),
        ")",
    )
    self.add_settable(
        Settable(
            "allow_style",
            allow_style_type,
            ru.rich_text_to_string(settable_description),
            self,
            choices_provider=get_allow_style_choices,
        )
    )
    self.add_settable(Settable("debug", bool, "Show full traceback on exception", self))
    self.add_settable(Settable("echo", bool, "Echo command issued into output", self))

    editor_description = Text.assemble(
        "Program used by ",
        ("'edit'", Style(bold=True)),
        " command",
    )
    self.add_settable(
        Settable(
            "editor",
            str,
            ru.rich_text_to_string(editor_description),
            self,
        )
    )

    self.add_settable(
        Settable(
            "max_completion_table_items",
            int,
            "Max results allowed to display a table",
            self,
        )
    )
    self.add_settable(
        Settable(
            "max_column_completion_results",
            int,
            "Max results to display in a single column",
            self,
        )
    )
    self.add_settable(Settable("quiet", bool, "Don't print nonessential feedback", self))
    self.add_settable(Settable("scripts_add_to_history", bool, "Scripts and pyscripts add commands to history", self))
    self.add_settable(Settable("timing", bool, "Report execution times", self))
    self.add_settable(Settable("traceback_show_locals", bool, "Display local variables in tracebacks", self))

    traceback_width_description = Text.assemble(
        "Maximum display width for tracebacks. Set to ",
        ("None", Style(bold=True)),
        " (case-insensitive) to fill entire terminal width.",
    )
    self.add_settable(
        Settable(
            "traceback_width",
            utils.optional_int,
            ru.rich_text_to_string(traceback_width_description),
            self,
        )
    )

print_to

print_to(
    file,
    *objects,
    sep=" ",
    end="\n",
    style=None,
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Print objects to a given file stream.

This method is configured for general-purpose printing. By default, it enables soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting. These defaults can be overridden by passing explicit keyword arguments.

PARAMETER DESCRIPTION
file

file stream being written to

TYPE: IO[str]

objects

objects to print

TYPE: Any DEFAULT: ()

sep

string to write between printed text. Defaults to " ".

TYPE: str DEFAULT: ' '

end

string to write at end of printed text. Defaults to a newline.

TYPE: str DEFAULT: '\n'

style

optional style to apply to output

TYPE: StyleType | None DEFAULT: None

soft_wrap

Enable soft wrap mode. Defaults to True. If True, text that doesn't fit will run on to the following line, just like the built-in print() function. This is useful for raw text and logs. If False, Rich wraps text to fit the terminal width. Set this to False when printing structured Renderables like Tables, Panels, or Columns to ensure they render as expected. For example, when soft_wrap is True Panels truncate text which is wider than the terminal.

TYPE: bool DEFAULT: True

justify

justify method ("left", "center", "right", "full"). Defaults to None.

TYPE: JustifyMethod | None DEFAULT: None

emoji

If True, Rich will replace emoji codes (e.g., 😃) with their corresponding Unicode characters. Defaults to False.

TYPE: bool DEFAULT: False

markup

If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold]) as styled output. Defaults to False.

TYPE: bool DEFAULT: False

highlight

If True, Rich will automatically apply highlighting to elements within strings, such as common Python data types like numbers, booleans, or None. This is particularly useful when pretty printing objects like lists and dictionaries to display them in color. Defaults to False.

TYPE: bool DEFAULT: False

rich_print_kwargs

optional additional keyword arguments to pass to console.print().

TYPE: Mapping[str, Any] | None DEFAULT: None

kwargs

Arbitrary keyword arguments. This allows subclasses to extend the signature of this method and still call super() without encountering unexpected keyword argument errors. These arguments are not passed to console.print().

See the Rich documentation for more details on emoji codes, markup tags, and highlighting.

TYPE: Any DEFAULT: {}

Source code in cmd2/cmd2.py
def print_to(
    self,
    file: IO[str],
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    style: StyleType | None = None,
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Print objects to a given file stream.

    This method is configured for general-purpose printing. By default, it enables
    soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting.
    These defaults can be overridden by passing explicit keyword arguments.

    :param file: file stream being written to
    :param objects: objects to print
    :param sep: string to write between printed text. Defaults to " ".
    :param end: string to write at end of printed text. Defaults to a newline.
    :param style: optional style to apply to output
    :param soft_wrap: Enable soft wrap mode. Defaults to True.
                      If True, text that doesn't fit will run on to the following line,
                      just like the built-in print() function. This is useful for raw text
                      and logs.
                      If False, Rich wraps text to fit the terminal width.
                      Set this to False when printing structured Renderables like
                      Tables, Panels, or Columns to ensure they render as expected.
                      For example, when soft_wrap is True Panels truncate text
                      which is wider than the terminal.
    :param justify: justify method ("left", "center", "right", "full"). Defaults to None.
    :param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
                  corresponding Unicode characters. Defaults to False.
    :param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold])
                   as styled output. Defaults to False.
    :param highlight: If True, Rich will automatically apply highlighting to elements within
                      strings, such as common Python data types like numbers, booleans, or None.
                      This is particularly useful when pretty printing objects like lists and
                      dictionaries to display them in color. Defaults to False.
    :param rich_print_kwargs: optional additional keyword arguments to pass to console.print().
    :param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
                   method and still call `super()` without encountering unexpected keyword argument errors.
                   These arguments are not passed to console.print().

    See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
    """
    try:
        self._get_core_print_console(
            file=file,
            emoji=emoji,
            markup=markup,
            highlight=highlight,
        ).print(
            *objects,
            sep=sep,
            end=end,
            style=style,
            justify=justify,
            soft_wrap=soft_wrap,
            **(rich_print_kwargs if rich_print_kwargs is not None else {}),
        )
    except BrokenPipeError:
        # This occurs if a command's output is being piped to another
        # process which closes the pipe before the command is finished
        # writing. If you would like your application to print a
        # warning message, then set the broken_pipe_warning attribute
        # to the message you want printed.
        if self.broken_pipe_warning and file != sys.stderr:
            Cmd2GeneralConsole(file=sys.stderr).print(self.broken_pipe_warning)

poutput

poutput(
    *objects,
    sep=" ",
    end="\n",
    style=None,
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Print objects to self.stdout.

For details on the parameters, refer to the print_to method documentation.

Source code in cmd2/cmd2.py
def poutput(
    self,
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    style: StyleType | None = None,
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Print objects to self.stdout.

    For details on the parameters, refer to the `print_to` method documentation.
    """
    self.print_to(
        self.stdout,
        *objects,
        sep=sep,
        end=end,
        style=style,
        soft_wrap=soft_wrap,
        justify=justify,
        emoji=emoji,
        markup=markup,
        highlight=highlight,
        rich_print_kwargs=rich_print_kwargs,
    )

perror

perror(
    *objects,
    sep=" ",
    end="\n",
    style=ERROR,
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Print objects to sys.stderr.

PARAMETER DESCRIPTION
style

optional style to apply to output. Defaults to Cmd2Style.ERROR.

For details on the other parameters, refer to the print_to method documentation.

TYPE: StyleType | None DEFAULT: ERROR

Source code in cmd2/cmd2.py
def perror(
    self,
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    style: StyleType | None = Cmd2Style.ERROR,
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Print objects to sys.stderr.

    :param style: optional style to apply to output. Defaults to Cmd2Style.ERROR.

    For details on the other parameters, refer to the `print_to` method documentation.
    """
    self.print_to(
        sys.stderr,
        *objects,
        sep=sep,
        end=end,
        style=style,
        soft_wrap=soft_wrap,
        justify=justify,
        emoji=emoji,
        markup=markup,
        highlight=highlight,
        rich_print_kwargs=rich_print_kwargs,
    )

psuccess

psuccess(
    *objects,
    sep=" ",
    end="\n",
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Wrap poutput, but apply Cmd2Style.SUCCESS.

For details on the parameters, refer to the print_to method documentation.

Source code in cmd2/cmd2.py
def psuccess(
    self,
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Wrap poutput, but apply Cmd2Style.SUCCESS.

    For details on the parameters, refer to the `print_to` method documentation.
    """
    self.poutput(
        *objects,
        sep=sep,
        end=end,
        style=Cmd2Style.SUCCESS,
        soft_wrap=soft_wrap,
        justify=justify,
        emoji=emoji,
        markup=markup,
        highlight=highlight,
        rich_print_kwargs=rich_print_kwargs,
    )

pwarning

pwarning(
    *objects,
    sep=" ",
    end="\n",
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Wrap perror, but apply Cmd2Style.WARNING.

For details on the parameters, refer to the print_to method documentation.

Source code in cmd2/cmd2.py
def pwarning(
    self,
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Wrap perror, but apply Cmd2Style.WARNING.

    For details on the parameters, refer to the `print_to` method documentation.
    """
    self.perror(
        *objects,
        sep=sep,
        end=end,
        style=Cmd2Style.WARNING,
        soft_wrap=soft_wrap,
        justify=justify,
        emoji=emoji,
        markup=markup,
        highlight=highlight,
        rich_print_kwargs=rich_print_kwargs,
    )

format_exception

format_exception(exception)

Format an exception for printing.

If debug is true, a full traceback is included, if one exists.

PARAMETER DESCRIPTION
exception

the exception to be printed.

TYPE: BaseException

RETURNS DESCRIPTION
str

a formatted exception string

Source code in cmd2/cmd2.py
def format_exception(self, exception: BaseException) -> str:
    """Format an exception for printing.

    If `debug` is true, a full traceback is included, if one exists.

    :param exception: the exception to be printed.
    :return: a formatted exception string
    """
    console = Cmd2ExceptionConsole(file=sys.stderr)
    with console.capture() as capture:
        # Only print a traceback if we're in debug mode and one exists.
        if self.debug and sys.exc_info() != (None, None, None):
            traceback = Traceback(**self.traceback_kwargs)
            console.print(traceback, end="")

        else:
            # Print the exception in the same style Rich uses after a traceback.
            exception_str = str(exception)

            if exception_str:
                highlighter = ReprHighlighter()

                final_msg = Text.assemble(
                    (f"{type(exception).__name__}: ", "traceback.exc_type"),
                    highlighter(exception_str),
                )
            else:
                final_msg = Text(f"{type(exception).__name__}", style="traceback.exc_type")

            # If not in debug mode and the 'debug' setting is available,
            # inform the user how to enable full tracebacks.
            if not self.debug and "debug" in self.settables:
                help_msg = Text.assemble(
                    "\n\n",
                    ("To enable full traceback, run the following command: ", Cmd2Style.WARNING),
                    ("set debug true", Cmd2Style.COMMAND_LINE),
                )
                final_msg.append(help_msg)

            console.print(final_msg)

    return capture.get()

pexcept

pexcept(exception, **kwargs)

Print an exception to sys.stderr.

If debug is true, a full traceback is also printed, if one exists.

PARAMETER DESCRIPTION
exception

the exception to be printed.

TYPE: BaseException

kwargs

Arbitrary keyword arguments. This allows subclasses to extend the signature of this method and still call super() without encountering unexpected keyword argument errors.

TYPE: Any DEFAULT: {}

Source code in cmd2/cmd2.py
def pexcept(
    self,
    exception: BaseException,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Print an exception to sys.stderr.

    If `debug` is true, a full traceback is also printed, if one exists.

    :param exception: the exception to be printed.
    :param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
                   method and still call `super()` without encountering unexpected keyword argument errors.
    """
    formatted_exception = self.format_exception(exception)
    self.print_to(sys.stderr, formatted_exception)

pfeedback

pfeedback(
    *objects,
    sep=" ",
    end="\n",
    style=None,
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Print nonessential feedback where the output can be silenced with the quiet setting.

For details on the parameters, refer to the print_to method documentation.

Source code in cmd2/cmd2.py
def pfeedback(
    self,
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    style: StyleType | None = None,
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Print nonessential feedback where the output can be silenced with the `quiet` setting.

    For details on the parameters, refer to the `print_to` method documentation.
    """
    if not self.quiet:
        self.poutput(
            *objects,
            sep=sep,
            end=end,
            style=style,
            soft_wrap=soft_wrap,
            justify=justify,
            emoji=emoji,
            markup=markup,
            highlight=highlight,
            rich_print_kwargs=rich_print_kwargs,
        )

ppaged

ppaged(
    *objects,
    sep=" ",
    end="\n",
    style=None,
    chop=False,
    soft_wrap=True,
    justify=None,
    emoji=False,
    markup=False,
    highlight=False,
    rich_print_kwargs=None,
    **kwargs,
)

Print output using a pager.

A pager is used when the terminal is interactive and may exit immediately if the output fits on the screen. A pager is not used inside a script (Python or text) or when output is redirected or piped, and in these cases, output is sent to poutput.

PARAMETER DESCRIPTION
chop

True -> causes lines longer than the screen width to be chopped (truncated) rather than wrapped - truncated text is still accessible by scrolling with the right & left arrow keys - chopping is ideal for displaying wide tabular data as is done in utilities like pgcli False -> causes lines longer than the screen width to wrap to the next line - wrapping is ideal when you want to keep users from having to use horizontal scrolling WARNING: On Windows, the text always wraps regardless of what the chop argument is set to

TYPE: bool DEFAULT: False

soft_wrap

Enable soft wrap mode. If True, lines of text will not be word-wrapped or cropped to fit the terminal width. Defaults to True.

              Note: If chop is True and a pager is used, soft_wrap is automatically set to True to
              prevent wrapping and allow for horizontal scrolling.

For details on the other parameters, refer to the print_to method documentation.

TYPE: bool DEFAULT: True

Source code in cmd2/cmd2.py
def ppaged(
    self,
    *objects: Any,
    sep: str = " ",
    end: str = "\n",
    style: StyleType | None = None,
    chop: bool = False,
    soft_wrap: bool = True,
    justify: JustifyMethod | None = None,
    emoji: bool = False,
    markup: bool = False,
    highlight: bool = False,
    rich_print_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,  # noqa: ARG002
) -> None:
    """Print output using a pager.

    A pager is used when the terminal is interactive and may exit immediately if the output
    fits on the screen. A pager is not used inside a script (Python or text) or when output is
    redirected or piped, and in these cases, output is sent to `poutput`.

    :param chop: True -> causes lines longer than the screen width to be chopped (truncated) rather than wrapped
                          - truncated text is still accessible by scrolling with the right & left arrow keys
                          - chopping is ideal for displaying wide tabular data as is done in utilities like pgcli
                 False -> causes lines longer than the screen width to wrap to the next line
                          - wrapping is ideal when you want to keep users from having to use horizontal scrolling
                 WARNING: On Windows, the text always wraps regardless of what the chop argument is set to
    :param soft_wrap: Enable soft wrap mode. If True, lines of text will not be word-wrapped or cropped to
                      fit the terminal width. Defaults to True.

                      Note: If chop is True and a pager is used, soft_wrap is automatically set to True to
                      prevent wrapping and allow for horizontal scrolling.

    For details on the other parameters, refer to the `print_to` method documentation.
    """
    # Detect if we are running within an interactive terminal.
    # Don't try to use the pager when being run by a continuous integration system like Jenkins + pexpect.
    functional_terminal = (
        self.stdin.isatty()
        and self.stdout.isatty()
        and (sys.platform.startswith("win") or os.environ.get("TERM") is not None)
    )

    # A pager application blocks, so only run one if not redirecting or running a script (either text or Python).
    can_block = not (self._redirecting or self.in_pyscript() or self.in_script())

    # Check if we are outputting to a pager.
    if functional_terminal and can_block:
        # Chopping overrides soft_wrap
        if chop:
            soft_wrap = True

        # Generate the bytes to send to the pager
        console = self._get_core_print_console(
            file=self.stdout,
            emoji=emoji,
            markup=markup,
            highlight=highlight,
        )
        with console.capture() as capture:
            console.print(
                *objects,
                sep=sep,
                end=end,
                style=style,
                justify=justify,
                soft_wrap=soft_wrap,
                **(rich_print_kwargs if rich_print_kwargs is not None else {}),
            )
        output_bytes = capture.get().encode("utf-8", "replace")

        # Prevent KeyboardInterrupts while in the pager. The pager application will
        # still receive the SIGINT since it is in the same process group as us.
        with self.sigint_protection:
            import subprocess

            pipe_proc = subprocess.Popen(  # noqa: S602
                self.pager_chop if chop else self.pager,
                shell=True,
                stdin=subprocess.PIPE,
                stdout=self.stdout,
            )
            pipe_proc.communicate(output_bytes)

            # If the pager was killed (e.g. SIGKILL), the terminal might be in a bad state.
            # Attempt to restore terminal settings and foreground process group.
            if self._initial_termios_settings is not None and self.stdin.isatty():  # type: ignore[unreachable]
                try:  # type: ignore[unreachable]
                    import signal
                    import termios

                    # Ensure we are in the foreground process group
                    if hasattr(os, "tcsetpgrp") and hasattr(os, "getpgrp"):
                        # Ignore SIGTTOU to avoid getting stopped when calling tcsetpgrp from background
                        old_handler = signal.signal(signal.SIGTTOU, signal.SIG_IGN)
                        try:
                            os.tcsetpgrp(self.stdin.fileno(), os.getpgrp())
                        finally:
                            signal.signal(signal.SIGTTOU, old_handler)

                    # Restore terminal attributes
                    if self._initial_termios_settings is not None:
                        termios.tcsetattr(self.stdin.fileno(), termios.TCSANOW, self._initial_termios_settings)

                except (OSError, termios.error):
                    pass

    else:
        self.poutput(
            *objects,
            sep=sep,
            end=end,
            style=style,
            soft_wrap=soft_wrap,
            justify=justify,
            emoji=emoji,
            markup=markup,
            highlight=highlight,
            rich_print_kwargs=rich_print_kwargs,
        )

ppretty

ppretty(
    obj,
    *,
    file=None,
    indent_size=4,
    indent_guides=True,
    max_length=None,
    max_string=None,
    max_depth=None,
    expand_all=False,
    end="\n",
)

Pretty print an object.

This is a cmd2-compatible replacement for rich.pretty.pprint().

PARAMETER DESCRIPTION
obj

object to pretty print

TYPE: Any

file

file stream being written to or None for self.stdout. Defaults to None.

TYPE: IO[str] | None DEFAULT: None

indent_size

number of spaces in indent. Defaults to 4.

TYPE: int DEFAULT: 4

indent_guides

enable indentation guides. Defaults to True.

TYPE: bool DEFAULT: True

max_length

maximum length of containers before abbreviating, or None for no abbreviation. Defaults to None.

TYPE: int | None DEFAULT: None

max_string

maximum length of strings before truncating, or None to disable. Defaults to None.

TYPE: int | None DEFAULT: None

max_depth

maximum depth for nested data structures, or None for unlimited depth. Defaults to None.

TYPE: int | None DEFAULT: None

expand_all

Expand all containers. Defaults to False.

TYPE: bool DEFAULT: False

end

string to write at end of printed text. Defaults to a newline.

TYPE: str DEFAULT: '\n'

Source code in cmd2/cmd2.py
def ppretty(
    self,
    obj: Any,
    *,
    file: IO[str] | None = None,
    indent_size: int = 4,
    indent_guides: bool = True,
    max_length: int | None = None,
    max_string: int | None = None,
    max_depth: int | None = None,
    expand_all: bool = False,
    end: str = "\n",
) -> None:
    """Pretty print an object.

    This is a cmd2-compatible replacement for rich.pretty.pprint().

    :param obj: object to pretty print
    :param file: file stream being written to or None for self.stdout.
                 Defaults to None.
    :param indent_size: number of spaces in indent. Defaults to 4.
    :param indent_guides: enable indentation guides. Defaults to True.
    :param max_length: maximum length of containers before abbreviating, or None for no abbreviation.
                       Defaults to None.
    :param max_string: maximum length of strings before truncating, or None to disable. Defaults to None.
    :param max_depth: maximum depth for nested data structures, or None for unlimited depth. Defaults to None.
    :param expand_all: Expand all containers. Defaults to False.
    :param end: string to write at end of printed text. Defaults to a newline.
    """
    # The overflow and soft_wrap values match those in rich.pretty.pprint().
    # This ensures long strings are neither truncated with ellipses nor broken
    # up by injected newlines.
    pretty_obj = Pretty(
        obj,
        indent_size=indent_size,
        indent_guides=indent_guides,
        max_length=max_length,
        max_string=max_string,
        max_depth=max_depth,
        expand_all=expand_all,
        overflow="ignore",
    )

    self.print_to(
        file or self.stdout,
        pretty_obj,
        soft_wrap=True,
        end=end,
    )

get_bottom_toolbar

get_bottom_toolbar()

Get the bottom toolbar content.

If self.bottom_toolbar is False, returns None.

Otherwise returns tokens for prompt-toolkit to populate in the bottom toolbar.

NOTE: This content can extend over multiple lines. However we would recommend keeping it to a single line or two lines maximum.

Source code in cmd2/cmd2.py
def get_bottom_toolbar(self) -> list[str | tuple[str, str]] | None:
    """Get the bottom toolbar content.

    If self.bottom_toolbar is False, returns None.

    Otherwise returns tokens for prompt-toolkit to populate in the bottom toolbar.

    NOTE: This content can extend over multiple lines.  However we would recommend
    keeping it to a single line or two lines maximum.
    """
    if self.bottom_toolbar:
        import datetime
        import shutil

        # Get the current time in ISO format with 0.01s precision
        dt = datetime.datetime.now(datetime.timezone.utc).astimezone()
        now = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-4] + dt.strftime("%z")
        left_text = sys.argv[0]

        # Get terminal width to calculate padding for right-alignment
        cols, _ = shutil.get_terminal_size()
        padding_size = cols - len(left_text) - len(now) - 1
        if padding_size < 1:
            padding_size = 1
        padding = " " * padding_size

        # Return formatted text for prompt-toolkit
        return [
            ("ansigreen", left_text),
            ("", padding),
            ("ansicyan", now),
        ]
    return None

get_rprompt

get_rprompt()

Provide text to populate prompt-toolkit right prompt with.

Override this if you want a right-prompt displaying contetual information useful for your application. This could be information like current Git branch, time, current working directory, etc that is displayed without cluttering the main input area.

RETURNS DESCRIPTION
str | FormattedText | None

any type of formatted text to display as the right prompt

Source code in cmd2/cmd2.py
def get_rprompt(self) -> str | FormattedText | None:
    """Provide text to populate prompt-toolkit right prompt with.

    Override this if you want a right-prompt displaying contetual information useful for your application.
    This could be information like current Git branch, time, current working directory, etc that is displayed
    without cluttering the main input area.

    :return: any type of formatted text to display as the right prompt
    """
    return None

tokens_for_completion

tokens_for_completion(line, begidx, endidx)

Get all tokens through the one being completed, used by completion functions.

PARAMETER DESCRIPTION
line

the current input line with leading whitespace removed

TYPE: str

begidx

the beginning index of the prefix text

TYPE: int

endidx

the ending index of the prefix text

TYPE: int

RETURNS DESCRIPTION
tuple[list[str], list[str]]

A 2 item tuple where the items are On Success - tokens: list of unquoted tokens - this is generally the list needed for completion functions - raw_tokens: list of tokens with any quotes preserved = this can be used to know if a token was quoted or is missing a closing quote Both lists are guaranteed to have at least 1 item. The last item in both lists is the token being tab completed On Failure - Two empty lists

Source code in cmd2/cmd2.py
def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> tuple[list[str], list[str]]:
    """Get all tokens through the one being completed, used by completion functions.

    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :return: A 2 item tuple where the items are
             **On Success**
             - tokens: list of unquoted tokens - this is generally the list needed for completion functions
             - raw_tokens: list of tokens with any quotes preserved = this can be used to know if a token was quoted
             or is missing a closing quote
             Both lists are guaranteed to have at least 1 item. The last item in both lists is the token being tab
             completed
             **On Failure**
             - Two empty lists
    """
    unclosed_quote = ""
    quotes_to_try = [*constants.QUOTES]

    tmp_line = line[:endidx]
    tmp_endidx = endidx

    # Parse the line into tokens
    while True:
        try:
            initial_tokens = shlex_split(tmp_line[:tmp_endidx])

            # If the cursor is at an empty token outside of a quoted string,
            # then that is the token being completed. Add it to the list.
            if not unclosed_quote and begidx == tmp_endidx:
                initial_tokens.append("")
            break
        except ValueError as ex:
            # Make sure the exception was due to an unclosed quote and
            # we haven't exhausted the closing quotes to try
            if str(ex) == "No closing quotation" and quotes_to_try:
                # Add a closing quote and try to parse again
                unclosed_quote = quotes_to_try[0]
                quotes_to_try = quotes_to_try[1:]

                tmp_line = line[:endidx]
                tmp_line += unclosed_quote
                tmp_endidx = endidx + 1
            else:  # pragma: no cover
                # The parsing error is not caused by unclosed quotes.
                # Return empty lists since this means the line is malformed.
                return [], []

    # Further split tokens on punctuation characters
    raw_tokens = self.statement_parser.split_on_punctuation(initial_tokens)

    # Save the unquoted tokens
    tokens = [su.strip_quotes(cur_token) for cur_token in raw_tokens]

    # If the token being completed had an unclosed quote, we need
    # to remove the closing quote that was added in order for it
    # to match what was on the command line.
    if unclosed_quote:
        raw_tokens[-1] = raw_tokens[-1][:-1]

    return tokens, raw_tokens

basic_complete

basic_complete(
    text, line, begidx, endidx, match_against, *, sort=True
)

Perform completion without considering line contents or cursor position.

Strings are matched directly while CompletionItems are matched against their 'text' member.

PARAMETER DESCRIPTION
text

the string prefix we are attempting to match (all matches must begin with it)

TYPE: str

line

the current input line with leading whitespace removed

TYPE: str

begidx

the beginning index of the prefix text

TYPE: int

endidx

the ending index of the prefix text

TYPE: int

match_against

the items being matched against

TYPE: Iterable[str | CompletionItem]

sort

if True, then results will be sorted. If False, then items will be in the same order they appeared in match_against.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
Completions

a Completions object

Source code in cmd2/cmd2.py
def basic_complete(
    self,
    text: str,
    line: str,  # noqa: ARG002
    begidx: int,  # noqa: ARG002
    endidx: int,  # noqa: ARG002
    match_against: Iterable[str | CompletionItem],
    *,
    sort: bool = True,
) -> Completions:
    """Perform completion without considering line contents or cursor position.

    Strings are matched directly while CompletionItems are matched against their 'text' member.

    :param text: the string prefix we are attempting to match (all matches must begin with it)
    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :param match_against: the items being matched against
    :param sort: if True, then results will be sorted. If False, then items will
                 be in the same order they appeared in match_against.
    :return: a Completions object
    """
    matches: list[CompletionItem] = []

    for item in match_against:
        candidate = item.text if isinstance(item, CompletionItem) else item
        if candidate.startswith(text):
            matches.append(item if isinstance(item, CompletionItem) else CompletionItem(item))

    return Completions(items=matches, is_sorted=not sort)

delimiter_complete

delimiter_complete(
    text, line, begidx, endidx, match_against, delimiter
)

Perform completion against a list but each match is split on a delimiter.

Only the portion of the match being completed is shown as the completion suggestions. This is useful if you match against strings that are hierarchical in nature and have a common delimiter.

An easy way to illustrate this concept is path completion since paths are just directories/files delimited by a slash. If you are completing items in /home/user you don't get the following as suggestions:

/home/user/file.txt /home/user/program.c /home/user/maps/ /home/user/cmd2.py

Instead you are shown:

file.txt program.c maps/ cmd2.py

For a large set of data, this can be visually more pleasing and easier to search.

Another example would be strings formatted with the following syntax: company::department::name In this case the delimiter would be :: and the user could easily narrow down what they are looking for if they were only shown suggestions in the category they are at in the string.

PARAMETER DESCRIPTION
text

the string prefix we are attempting to match (all matches must begin with it)

TYPE: str

line

the current input line with leading whitespace removed

TYPE: str

begidx

the beginning index of the prefix text

TYPE: int

endidx

the ending index of the prefix text

TYPE: int

match_against

the list being matched against

TYPE: Iterable[str]

delimiter

what delimits each portion of the matches (ex: paths are delimited by a slash)

TYPE: str

RETURNS DESCRIPTION
Completions

a Completions object

Source code in cmd2/cmd2.py
def delimiter_complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
    match_against: Iterable[str],
    delimiter: str,
) -> Completions:
    """Perform completion against a list but each match is split on a delimiter.

    Only the portion of the match being completed is shown as the completion suggestions.
    This is useful if you match against strings that are hierarchical in nature and have a
    common delimiter.

    An easy way to illustrate this concept is path completion since paths are just directories/files
    delimited by a slash. If you are completing items in /home/user you don't get the following
    as suggestions:

    /home/user/file.txt     /home/user/program.c
    /home/user/maps/        /home/user/cmd2.py

    Instead you are shown:

    file.txt                program.c
    maps/                   cmd2.py

    For a large set of data, this can be visually more pleasing and easier to search.

    Another example would be strings formatted with the following syntax: company::department::name
    In this case the delimiter would be :: and the user could easily narrow down what they are looking
    for if they were only shown suggestions in the category they are at in the string.

    :param text: the string prefix we are attempting to match (all matches must begin with it)
    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :param match_against: the list being matched against
    :param delimiter: what delimits each portion of the matches (ex: paths are delimited by a slash)
    :return: a Completions object
    """
    basic_completions = self.basic_complete(text, line, begidx, endidx, match_against)
    if not basic_completions:
        return Completions()

    match_strings = basic_completions.to_strings()

    # Calculate what portion of the match we are completing
    common_prefix = su.common_prefix(match_strings)
    prefix_tokens = common_prefix.split(delimiter)
    display_token_index = len(prefix_tokens) - 1

    # Remove from each match everything after where the user is completing.
    # This approach can result in duplicates so we will filter those out.
    unique_results: dict[str, str] = {}

    allow_finalization = True
    for cur_match in match_strings:
        match_tokens = cur_match.split(delimiter)

        full_value = delimiter.join(match_tokens[: display_token_index + 1])
        display_val = match_tokens[display_token_index]

        # If there are more tokens, then we aren't done completing a full item
        if len(match_tokens) > display_token_index + 1:
            full_value += delimiter
            display_val += delimiter
            allow_finalization = False

        if full_value not in unique_results:
            unique_results[full_value] = display_val

    items = [
        CompletionItem(
            value=value,
            display=display,
        )
        for value, display in unique_results.items()
    ]

    return Completions(items, allow_finalization=allow_finalization)

path_complete

path_complete(
    text, line, begidx, endidx, *, path_filter=None
)

Perform completion of local file system paths.

PARAMETER DESCRIPTION
text

the string prefix we are attempting to match (all matches must begin with it)

TYPE: str

line

the current input line with leading whitespace removed

TYPE: str

begidx

the beginning index of the prefix text

TYPE: int

endidx

the ending index of the prefix text

TYPE: int

path_filter

optional filter function that determines if a path belongs in the results this function takes a path as its argument and returns True if the path should be kept in the results

TYPE: Callable[[str], bool] | None DEFAULT: None

RETURNS DESCRIPTION
Completions

a Completions object

Source code in cmd2/cmd2.py
def path_complete(
    self,
    text: str,
    line: str,
    begidx: int,  # noqa: ARG002
    endidx: int,
    *,
    path_filter: Callable[[str], bool] | None = None,
) -> Completions:
    """Perform completion of local file system paths.

    :param text: the string prefix we are attempting to match (all matches must begin with it)
    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :param path_filter: optional filter function that determines if a path belongs in the results
                        this function takes a path as its argument and returns True if the path should
                        be kept in the results
    :return: a Completions object
    """
    # Determine if a trailing separator should be appended to directory completions
    add_trailing_sep_if_dir = False
    if endidx == len(line) or (endidx < len(line) and line[endidx] != os.path.sep):
        add_trailing_sep_if_dir = True

    # Used to replace cwd in the final results
    cwd = os.getcwd()
    cwd_added = False

    # Used to replace expanded user path in final result
    orig_tilde_path = ""
    expanded_tilde_path = ""

    # If the search text is blank, then search in the CWD for *
    if not text:
        search_str = os.path.join(os.getcwd(), "*")
        cwd_added = True
    else:
        # Purposely don't match any path containing wildcards
        wildcards = ["*", "?"]
        for wildcard in wildcards:
            if wildcard in text:
                return Completions()

        # Start the search string
        search_str = text + "*"

        # Handle tilde expansion and completion
        if text.startswith("~"):
            sep_index = text.find(os.path.sep, 1)

            # If there is no slash, then the user is still completing the user after the tilde
            if sep_index == -1:
                return self._complete_users(text, add_trailing_sep_if_dir)

            # Otherwise expand the user dir
            search_str = os.path.expanduser(search_str)

            # Get what we need to restore the original tilde path later
            orig_tilde_path = text[:sep_index]
            expanded_tilde_path = os.path.expanduser(orig_tilde_path)

        # If the search text does not have a directory, then use the cwd
        elif not os.path.dirname(text):
            search_str = os.path.join(os.getcwd(), search_str)
            cwd_added = True

    # Find all matching path completions
    matches = glob.glob(search_str)

    # Filter out results that don't belong
    if path_filter is not None:
        matches = [c for c in matches if path_filter(c)]

    if not matches:
        return Completions()

    # If we have a single match and it's a directory, then don't append a space or closing quote
    allow_finalization = not (len(matches) == 1 and os.path.isdir(matches[0]))

    # Build display_matches and add a slash to directories
    display_matches: list[str] = []
    for index, cur_match in enumerate(matches):
        # Display only the basename of this path in the completion suggestions
        display_matches.append(os.path.basename(cur_match))

        # Add a separator after directories if the next character isn't already a separator
        if os.path.isdir(cur_match) and add_trailing_sep_if_dir:
            matches[index] += os.path.sep
            display_matches[index] += os.path.sep

    # Remove cwd if it was added to match the text prompt-toolkit expects
    if cwd_added:
        to_replace = cwd if cwd == os.path.sep else cwd + os.path.sep
        matches = [cur_path.replace(to_replace, "", 1) for cur_path in matches]

    # Restore the tilde string if we expanded one to match the text prompt-toolkit expects
    if expanded_tilde_path:
        matches = [cur_path.replace(expanded_tilde_path, orig_tilde_path, 1) for cur_path in matches]

    items = [
        CompletionItem(
            value=match,
            display=display,
        )
        for match, display in zip(matches, display_matches, strict=True)
    ]

    return Completions(items=items, allow_finalization=allow_finalization)

shell_cmd_complete

shell_cmd_complete(
    text, line, begidx, endidx, *, complete_blank=False
)

Perform completion of executables either in a user's path or a given path.

PARAMETER DESCRIPTION
text

the string prefix we are attempting to match (all matches must begin with it)

TYPE: str

line

the current input line with leading whitespace removed

TYPE: str

begidx

the beginning index of the prefix text

TYPE: int

endidx

the ending index of the prefix text

TYPE: int

complete_blank

If True, then a blank will complete all shell commands in a user's path. If False, then no completion is performed. Defaults to False to match Bash shell behavior.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Completions

a Completions object

Source code in cmd2/cmd2.py
def shell_cmd_complete(
    self, text: str, line: str, begidx: int, endidx: int, *, complete_blank: bool = False
) -> Completions:
    """Perform completion of executables either in a user's path or a given path.

    :param text: the string prefix we are attempting to match (all matches must begin with it)
    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :param complete_blank: If True, then a blank will complete all shell commands in a user's path. If False, then
                           no completion is performed. Defaults to False to match Bash shell behavior.
    :return: a Completions object
    """
    # Don't complete anything if no shell command has been started
    if not complete_blank and not text:
        return Completions()

    # If there are no path characters in the search text, then do shell command completion in the user's path
    if not text.startswith("~") and os.path.sep not in text:
        items = [CompletionItem(exe) for exe in utils.get_exes_in_path(text)]
        return Completions(items=items)

    # Otherwise look for executables in the given path
    return self.path_complete(
        text, line, begidx, endidx, path_filter=lambda path: os.path.isdir(path) or os.access(path, os.X_OK)
    )

complete

complete(text, line, begidx, endidx, custom_settings=None)

Handle completion for an input line.

PARAMETER DESCRIPTION
text

the current word that user is typing

TYPE: str

line

current input line

TYPE: str

begidx

beginning index of text

TYPE: int

endidx

ending index of text

TYPE: int

custom_settings

used when not completing the main command line

TYPE: CustomCompletionSettings | None DEFAULT: None

RETURNS DESCRIPTION
Completions

a Completions object

Source code in cmd2/cmd2.py
def complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
    custom_settings: utils.CustomCompletionSettings | None = None,
) -> Completions:
    """Handle completion for an input line.

    :param text: the current word that user is typing
    :param line: current input line
    :param begidx: beginning index of text
    :param endidx: ending index of text
    :param custom_settings: used when not completing the main command line
    :return: a Completions object
    """
    try:
        # lstrip the original line
        orig_line = line
        line = orig_line.lstrip()
        num_stripped = len(orig_line) - len(line)

        # Calculate new indexes for the stripped line. If the cursor is at a position before the end of a
        # line of spaces, then the following math could result in negative indexes. Enforce a max of 0.
        begidx = max(begidx - num_stripped, 0)
        endidx = max(endidx - num_stripped, 0)

        # Shortcuts are not word break characters when completing. Therefore, shortcuts become part
        # of the text variable if there isn't a word break, like a space, after it. We need to remove it
        # from text and update the indexes. This only applies if we are at the beginning of the command line.
        shortcut_to_restore = ""
        if begidx == 0 and custom_settings is None:
            for shortcut, _ in self.statement_parser.shortcuts:
                if text.startswith(shortcut):
                    # Save the shortcut to restore later
                    shortcut_to_restore = shortcut

                    # Adjust text and where it begins
                    text = text[len(shortcut_to_restore) :]
                    begidx += len(shortcut_to_restore)
                    break
            else:
                # No shortcut was found. Complete the command token.
                parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(add_help=False)
                parser.add_argument(
                    "command",
                    metavar="COMMAND",
                    help="command, alias, or macro name",
                    choices=self._get_commands_aliases_and_macros_choices(),
                )
                custom_settings = utils.CustomCompletionSettings(parser)

        completions = self._perform_completion(text, line, begidx, endidx, custom_settings)

        # Check if we need to restore a shortcut in the completion text
        # so it doesn't get erased from the command line.
        if completions and shortcut_to_restore:
            new_items = [
                dataclasses.replace(
                    item,
                    text=shortcut_to_restore + item.text,
                )
                for item in completions
            ]

            # Update items and set _quote_from_offset so that any auto-inserted
            # opening quote is placed after the shortcut.
            completions = dataclasses.replace(
                completions,
                items=new_items,
                _search_text_offset=len(shortcut_to_restore),
            )

        # Swap between COLUMN and MULTI_COLUMN style based on the number of matches.
        if len(completions) > self.max_column_completion_results:
            self.active_session.complete_style = CompleteStyle.MULTI_COLUMN
        else:
            self.active_session.complete_style = CompleteStyle.COLUMN

        return completions  # noqa: TRY300

    except CompletionError as ex:
        error_msg = str(ex)
        formatted_error = ""

        # Don't display anything if the error is blank (e.g. _NoResultsError for an argument which suppresses hints)
        if error_msg:
            # _NoResultsError completion hints already include a trailing "\n".
            end = "" if isinstance(ex, argparse_completer._NoResultsError) else "\n"

            console = Cmd2GeneralConsole(file=self.stdout)
            with console.capture() as capture:
                console.print(
                    error_msg,
                    style=Cmd2Style.ERROR if ex.apply_style else "",
                    end=end,
                )
            formatted_error = capture.get()
        return Completions(error=formatted_error)
    except Exception as ex:  # noqa: BLE001
        formatted_exception = self.format_exception(ex)
        return Completions(error=formatted_exception)

in_script

in_script()

Return whether a text script is running.

Source code in cmd2/cmd2.py
def in_script(self) -> bool:
    """Return whether a text script is running."""
    return self._current_script_dir is not None

in_pyscript

in_pyscript()

Return whether running inside a Python shell or pyscript.

Source code in cmd2/cmd2.py
def in_pyscript(self) -> bool:
    """Return whether running inside a Python shell or pyscript."""
    return self._in_py

get_names

get_names()

Return an alphabetized list of names comprising the attributes of the cmd2 class instance.

Source code in cmd2/cmd2.py
def get_names(self) -> list[str]:
    """Return an alphabetized list of names comprising the attributes of the cmd2 class instance."""
    return dir(self)

get_all_commands

get_all_commands()

Return a list of all commands.

Source code in cmd2/cmd2.py
def get_all_commands(self) -> list[str]:
    """Return a list of all commands."""
    return [
        name[len(constants.COMMAND_FUNC_PREFIX) :]
        for name in self.get_names()
        if name.startswith(constants.COMMAND_FUNC_PREFIX) and callable(getattr(self, name))
    ]

get_visible_commands

get_visible_commands()

Return a list of commands that have not been hidden or disabled.

Source code in cmd2/cmd2.py
def get_visible_commands(self) -> list[str]:
    """Return a list of commands that have not been hidden or disabled."""
    return [
        command
        for command in self.get_all_commands()
        if command not in self.hidden_commands and command not in self.disabled_commands
    ]

get_help_topics

get_help_topics()

Return a list of help topics.

Source code in cmd2/cmd2.py
def get_help_topics(self) -> list[str]:
    """Return a list of help topics."""
    all_topics = [
        name[len(constants.HELP_FUNC_PREFIX) :]
        for name in self.get_names()
        if name.startswith(constants.HELP_FUNC_PREFIX) and callable(getattr(self, name))
    ]

    # Filter out hidden and disabled commands
    return [topic for topic in all_topics if topic not in self.hidden_commands and topic not in self.disabled_commands]

sigint_handler

sigint_handler(signum, frame)

Signal handler for SIGINTs which typically come from Ctrl-C events.

If you need custom SIGINT behavior, then override this method.

PARAMETER DESCRIPTION
signum

signal number

TYPE: int

frame

the current stack frame or None

TYPE: FrameType | None

Source code in cmd2/cmd2.py
def sigint_handler(
    self,
    signum: int,  # noqa: ARG002
    frame: FrameType | None,  # noqa: ARG002
) -> None:
    """Signal handler for SIGINTs which typically come from Ctrl-C events.

    If you need custom SIGINT behavior, then override this method.

    :param signum: signal number
    :param frame: the current stack frame or None
    """
    if self._cur_pipe_proc_reader is not None:
        # Pass the SIGINT to the current pipe process
        self._cur_pipe_proc_reader.send_sigint()

    # Check if we are allowed to re-raise the KeyboardInterrupt
    if not self.sigint_protection:
        raise_interrupt = True
        if self.current_command is not None:
            command_set = self.find_commandset_for_command(self.current_command.command)
            if command_set is not None:
                raise_interrupt = not command_set.sigint_handler()
        if raise_interrupt:
            self._raise_keyboard_interrupt()

termination_signal_handler

termination_signal_handler(signum, _)

Signal handler for SIGHUP and SIGTERM. Only runs on Linux and Mac.

SIGHUP - received when terminal window is closed SIGTERM - received when this app has been requested to terminate

The basic purpose of this method is to call sys.exit() so our exit handler will run and save the persistent history file. If you need more complex behavior like killing threads and performing cleanup, then override this method.

PARAMETER DESCRIPTION
signum

signal number

TYPE: int

_

the current stack frame or None

TYPE: FrameType | None

Source code in cmd2/cmd2.py
def termination_signal_handler(self, signum: int, _: FrameType | None) -> None:
    """Signal handler for SIGHUP and SIGTERM. Only runs on Linux and Mac.

    SIGHUP - received when terminal window is closed
    SIGTERM - received when this app has been requested to terminate

    The basic purpose of this method is to call sys.exit() so our exit handler will run
    and save the persistent history file. If you need more complex behavior like killing
    threads and performing cleanup, then override this method.

    :param signum: signal number
    :param _: the current stack frame or None
    """
    # POSIX systems add 128 to signal numbers for the exit code
    sys.exit(128 + signum)

pre_prompt

pre_prompt()

Ran just before the prompt is displayed (and after the event loop has started).

This is the ideal location to update self.prompt or any other state that should be current when the prompt appears.

Source code in cmd2/cmd2.py
def pre_prompt(self) -> None:
    """Ran just before the prompt is displayed (and after the event loop has started).

    This is the ideal location to update `self.prompt` or any other state that should
    be current when the prompt appears.
    """

precmd

precmd(statement)

Ran just before the command is executed by cmd2.Cmd.onecmd and after adding it to history (cmd Hook method).

PARAMETER DESCRIPTION
statement

subclass of str which also contains the parsed input

TYPE: Statement | str

RETURNS DESCRIPTION
Statement

a potentially modified version of the input Statement object

See cmd2.Cmd.register_postparsing_hook and cmd2.Cmd.register_precmd_hook for more robust ways to run hooks before the command is executed. See Hooks for more information.

Source code in cmd2/cmd2.py
def precmd(self, statement: Statement | str) -> Statement:
    """Ran just before the command is executed by [cmd2.Cmd.onecmd][] and after adding it to history (cmd Hook method).

    :param statement: subclass of str which also contains the parsed input
    :return: a potentially modified version of the input Statement object

    See [cmd2.Cmd.register_postparsing_hook][] and [cmd2.Cmd.register_precmd_hook][] for more robust ways
    to run hooks before the command is executed. See [Hooks](../features/hooks.md) for more information.
    """
    return Statement(statement) if not isinstance(statement, Statement) else statement

postcmd

postcmd(stop, statement)

Ran just after a command is executed by cmd2.Cmd.onecmd (cmd inherited Hook method).

PARAMETER DESCRIPTION
stop

return True to request the command loop terminate

TYPE: bool

statement

subclass of str which also contains the parsed input

See cmd2.Cmd.register_postcmd_hook and cmd2.Cmd.register_cmdfinalization_hook for more robust ways to run hooks after the command is executed. See Hooks for more information.

TYPE: Statement | str

Source code in cmd2/cmd2.py
def postcmd(self, stop: bool, statement: Statement | str) -> bool:  # noqa: ARG002
    """Ran just after a command is executed by [cmd2.Cmd.onecmd][] (cmd inherited Hook method).

    :param stop: return `True` to request the command loop terminate
    :param statement: subclass of str which also contains the parsed input

    See [cmd2.Cmd.register_postcmd_hook][] and [cmd2.Cmd.register_cmdfinalization_hook][] for more robust ways
    to run hooks after the command is executed. See [Hooks](../features/hooks.md) for more information.
    """
    return stop

preloop

preloop()

Ran once when the cmd2.Cmd.cmdloop method is called (cmd inherited Hook method).

This method is a stub that does nothing and exists to be overridden by subclasses.

See cmd2.Cmd.register_preloop_hook for a more robust wayto run hooks before the command loop begins. See Hooks for more information.

Source code in cmd2/cmd2.py
def preloop(self) -> None:
    """Ran once when the [cmd2.Cmd.cmdloop][] method is called (cmd inherited Hook method).

    This method is a stub that does nothing and exists to be overridden by subclasses.

    See [cmd2.Cmd.register_preloop_hook][] for a more robust wayto run hooks before the command loop begins.
    See [Hooks](../features/hooks.md) for more information.
    """

postloop

postloop()

Ran once when the cmd2.Cmd.cmdloop method is about to return (cmd inherited Hook Method).

This method is a stub that does nothing and exists to be overridden by subclasses.

See cmd2.Cmd.register_postloop_hook for a more robust way to run hooks after the command loop completes. See Hooks for more information.

Source code in cmd2/cmd2.py
def postloop(self) -> None:
    """Ran once when the [cmd2.Cmd.cmdloop][] method is about to return (cmd inherited Hook Method).

    This method is a stub that does nothing and exists to be overridden by subclasses.

    See [cmd2.Cmd.register_postloop_hook][] for a more robust way to run hooks after the command loop completes.
    See [Hooks](../features/hooks.md) for more information.
    """

onecmd_plus_hooks

onecmd_plus_hooks(
    line,
    *,
    add_to_history=True,
    raise_keyboard_interrupt=False,
    py_bridge_call=False,
)

Top-level function called by cmdloop() to handle parsing a line and running the command and all of its hooks.

PARAMETER DESCRIPTION
line

command line to run

TYPE: str

add_to_history

If True, then add this command to history. Defaults to True.

TYPE: bool DEFAULT: True

raise_keyboard_interrupt

if True, then KeyboardInterrupt exceptions will be raised if stop isn't already True. This is used when running commands in a loop to be able to stop the whole loop and not just the current command. Defaults to False.

TYPE: bool DEFAULT: False

py_bridge_call

This should only ever be set to True by PyBridge to signify the beginning of an app() call from Python. It is used to enable/disable the storage of the command's stdout.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
bool

True if running of commands should stop

Source code in cmd2/cmd2.py
def onecmd_plus_hooks(
    self,
    line: str,
    *,
    add_to_history: bool = True,
    raise_keyboard_interrupt: bool = False,
    py_bridge_call: bool = False,
) -> bool:
    """Top-level function called by cmdloop() to handle parsing a line and running the command and all of its hooks.

    :param line: command line to run
    :param add_to_history: If True, then add this command to history. Defaults to True.
    :param raise_keyboard_interrupt: if True, then KeyboardInterrupt exceptions will be raised if stop isn't already
                                     True. This is used when running commands in a loop to be able to stop the whole
                                     loop and not just the current command. Defaults to False.
    :param py_bridge_call: This should only ever be set to True by PyBridge to signify the beginning
                           of an app() call from Python. It is used to enable/disable the storage of the
                           command's stdout.
    :return: True if running of commands should stop
    """
    import datetime

    stop = False
    statement = None

    try:
        # Convert the line into a Statement
        statement = self._input_line_to_statement(line)

        # call the postparsing hooks
        postparsing_data = plugin.PostparsingData(False, statement)
        for postparsing_func in self._postparsing_hooks:
            postparsing_data = postparsing_func(postparsing_data)
            if postparsing_data.stop:
                break

        # unpack the postparsing_data object
        statement = postparsing_data.statement
        stop = postparsing_data.stop
        if stop:
            # we should not run the command, but
            # we need to run the finalization hooks
            raise EmptyStatement  # noqa: TRY301

        redir_saved_state: utils.RedirectionSavedState | None = None

        try:
            # Get sigint protection while we set up redirection
            with self.sigint_protection:
                if py_bridge_call:
                    # Start saving command's stdout at this point
                    self.stdout.pause_storage = False  # type: ignore[attr-defined]

                redir_saved_state = self._redirect_output(statement)

            timestart = datetime.datetime.now(tz=datetime.timezone.utc)

            # precommand hooks
            precmd_data = plugin.PrecommandData(statement)
            for precmd_func in self._precmd_hooks:
                precmd_data = precmd_func(precmd_data)
            statement = precmd_data.statement

            # call precmd() for compatibility with cmd.Cmd
            statement = self.precmd(statement)

            # go run the command function
            stop = self.onecmd(statement, add_to_history=add_to_history)

            # postcommand hooks
            postcmd_data = plugin.PostcommandData(stop, statement)
            for postcmd_func in self._postcmd_hooks:
                postcmd_data = postcmd_func(postcmd_data)

            # retrieve the final value of stop, ignoring any statement modification from the hooks
            stop = postcmd_data.stop

            # call postcmd() for compatibility with cmd.Cmd
            stop = self.postcmd(stop, statement)

            if self.timing:
                self.perror(f"Elapsed: {datetime.datetime.now(tz=datetime.timezone.utc) - timestart}", style=None)
        finally:
            # Get sigint protection while we restore stuff
            with self.sigint_protection:
                if redir_saved_state is not None:
                    self._restore_output(statement, redir_saved_state)

                if py_bridge_call:
                    # Stop saving command's stdout before command finalization hooks run
                    self.stdout.pause_storage = True  # type: ignore[attr-defined]
    except (SkipPostcommandHooks, EmptyStatement):
        # Don't do anything, but do allow command finalization hooks to run
        pass
    except Cmd2ShlexError as ex:
        self.perror(f"Invalid syntax: {ex}")
    except RedirectionError as ex:
        self.perror(ex)
    except KeyboardInterrupt:
        if raise_keyboard_interrupt and not stop:
            raise
    except SystemExit as ex:
        if isinstance(ex.code, int):
            self.exit_code = ex.code
        stop = True
    except PassThroughException as ex:
        raise ex.wrapped_ex from None
    except Exception as ex:  # noqa: BLE001
        self.pexcept(ex)
    finally:
        try:
            stop = self._run_cmdfinalization_hooks(stop, statement)
        except KeyboardInterrupt:
            if raise_keyboard_interrupt and not stop:
                raise
        except SystemExit as ex:
            if isinstance(ex.code, int):
                self.exit_code = ex.code
            stop = True
        except PassThroughException as ex:
            raise ex.wrapped_ex from None
        except Exception as ex:  # noqa: BLE001
            self.pexcept(ex)

    return stop

runcmds_plus_hooks

runcmds_plus_hooks(
    cmds,
    *,
    add_to_history=True,
    stop_on_keyboard_interrupt=False,
)

Run commands in an automated fashion from sources like text scripts or history replays.

The prompt and command line for each command will be printed if echo is True.

PARAMETER DESCRIPTION
cmds

commands to run

TYPE: Iterable[HistoryItem] | Iterable[str]

add_to_history

If True, then add these commands to history. Defaults to True.

TYPE: bool DEFAULT: True

stop_on_keyboard_interrupt

if True, then stop running contents of cmds if Ctrl-C is pressed instead of moving to the next command in the list. This is used when the commands are part of a group, like a text script, which should stop upon Ctrl-C. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
bool

True if running of commands should stop

Source code in cmd2/cmd2.py
def runcmds_plus_hooks(
    self,
    cmds: Iterable[HistoryItem] | Iterable[str],
    *,
    add_to_history: bool = True,
    stop_on_keyboard_interrupt: bool = False,
) -> bool:
    """Run commands in an automated fashion from sources like text scripts or history replays.

    The prompt and command line for each command will be printed if echo is True.

    :param cmds: commands to run
    :param add_to_history: If True, then add these commands to history. Defaults to True.
    :param stop_on_keyboard_interrupt: if True, then stop running contents of cmds if Ctrl-C is pressed instead of moving
                                       to the next command in the list. This is used when the commands are part of a
                                       group, like a text script, which should stop upon Ctrl-C. Defaults to False.
    :return: True if running of commands should stop
    """
    for line in cmds:
        if isinstance(line, HistoryItem):
            line = line.raw  # noqa: PLW2901

        if self.echo:
            self.poutput(f"{self.prompt}{line}")

        try:
            if self.onecmd_plus_hooks(
                line, add_to_history=add_to_history, raise_keyboard_interrupt=stop_on_keyboard_interrupt
            ):
                return True
        except KeyboardInterrupt as ex:
            if stop_on_keyboard_interrupt:
                self.perror(ex)
                break

    return False

get_command_func

get_command_func(command)

Get the bound command function for a command.

PARAMETER DESCRIPTION
command

the name of the command

TYPE: str

RETURNS DESCRIPTION
BoundCommandFunc | None

the bound function implementing the command, or None if not found

Source code in cmd2/cmd2.py
def get_command_func(self, command: str) -> BoundCommandFunc | None:
    """Get the bound command function for a command.

    :param command: the name of the command
    :return: the bound function implementing the command, or None if not found
    """
    command_func_name = constants.COMMAND_FUNC_PREFIX + command
    command_func = getattr(self, command_func_name, None)
    return cast(BoundCommandFunc, command_func) if callable(command_func) else None

onecmd

onecmd(statement, *, add_to_history=True)

Execute the actual do_* method for a command.

If the command provided doesn't exist, then it executes default() instead.

PARAMETER DESCRIPTION
statement

intended to be a Statement instance parsed command from the input stream, alternative acceptance of a str is present only for backward compatibility with cmd

TYPE: Statement | str

add_to_history

If True, then add this command to history. Defaults to True.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
bool

a flag indicating whether the interpretation of commands should stop

Source code in cmd2/cmd2.py
def onecmd(self, statement: Statement | str, *, add_to_history: bool = True) -> bool:
    """Execute the actual do_* method for a command.

    If the command provided doesn't exist, then it executes default() instead.

    :param statement: intended to be a Statement instance parsed command from the input stream, alternative
                      acceptance of a str is present only for backward compatibility with cmd
    :param add_to_history: If True, then add this command to history. Defaults to True.
    :return: a flag indicating whether the interpretation of commands should stop
    """
    # For backwards compatibility with cmd, allow a str to be passed in
    if not isinstance(statement, Statement):
        statement = self._input_line_to_statement(statement)

    command_func = self.get_command_func(statement.command)
    if command_func:
        # Check to see if this command should be stored in history
        if (
            statement.command not in self.exclude_from_history
            and statement.command not in self.disabled_commands
            and add_to_history
        ):
            self.history.append(statement)

        try:
            self.current_command = statement
            stop = command_func(statement)
        finally:
            self.current_command = None

    else:
        stop = self.default(statement)

    return stop if stop is not None else False

default

default(statement)

Execute when the command given isn't a recognized command implemented by a do_* method.

PARAMETER DESCRIPTION
statement

Statement object with parsed input

TYPE: Statement

Source code in cmd2/cmd2.py
def default(self, statement: Statement) -> bool | None:
    """Execute when the command given isn't a recognized command implemented by a do_* method.

    :param statement: Statement object with parsed input
    """
    err_msg = self.default_error.format(statement.command)
    if self.suggest_similar_command and (suggested_command := self._suggest_similar_command(statement.command)):
        err_msg += f"\n{self.default_suggestion_message.format(suggested_command)}"

    self.perror(err_msg, style=None)
    return None

completedefault

completedefault(*_ignored)

Call to complete an input line when no command-specific complete_*() method is available.

This method is only called for non-argparse-based commands.

By default, it returns a Completions object with no matches.

Source code in cmd2/cmd2.py
def completedefault(self, *_ignored: Sequence[str]) -> Completions:
    """Call to complete an input line when no command-specific complete_*() method is available.

    This method is only called for non-argparse-based commands.

    By default, it returns a Completions object with no matches.
    """
    return Completions()

read_input

read_input(
    prompt="",
    *,
    history=None,
    preserve_quotes=False,
    choices=None,
    choices_provider=None,
    completer=None,
    parser=None,
)

Read a line of input with optional completion and history.

PARAMETER DESCRIPTION
prompt

prompt to display to user

TYPE: str DEFAULT: ''

history

optional Sequence of strings to use for up-arrow history. The passed in history will not be edited. It is the caller's responsibility to add the returned input to history if desired. Defaults to None.

TYPE: Sequence[str] | None DEFAULT: None

preserve_quotes

if True, then quoted tokens will keep their quotes when processed by ArgparseCompleter. This is helpful in cases when you're 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.

A maximum of one of these should be provided:

TYPE: bool DEFAULT: False

choices

iterable of accepted values for single argument

TYPE: Iterable[Any] | None DEFAULT: None

choices_provider

function that provides choices for single argument

TYPE: UnboundChoicesProvider[CmdOrSetT] | None DEFAULT: None

completer

completion function that provides choices for single argument

TYPE: UnboundCompleter[CmdOrSetT] | None DEFAULT: None

parser

an argument parser which supports the completion of multiple arguments

TYPE: Cmd2ArgumentParser | None DEFAULT: None

RETURNS DESCRIPTION
str

the line read from stdin with all trailing new lines removed

RAISES DESCRIPTION
EOFError

if the input stream is closed or the user signals EOF (e.g., Ctrl+D)

Exception

any other exceptions raised by prompt()

Source code in cmd2/cmd2.py
def read_input(
    self,
    prompt: str = "",
    *,
    history: Sequence[str] | None = None,
    preserve_quotes: bool = False,
    choices: Iterable[Any] | None = None,
    choices_provider: UnboundChoicesProvider[CmdOrSetT] | None = None,
    completer: UnboundCompleter[CmdOrSetT] | None = None,
    parser: Cmd2ArgumentParser | None = None,
) -> str:
    """Read a line of input with optional completion and history.

    :param prompt: prompt to display to user
    :param history: optional Sequence of strings to use for up-arrow history. The passed in history
                    will not be edited. It is the caller's responsibility to add the returned input
                    to history if desired. Defaults to None.
    :param preserve_quotes: if True, then quoted tokens will keep their quotes when processed by
                            ArgparseCompleter. This is helpful in cases when you're 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.

    A maximum of one of these should be provided:
    :param choices: iterable of accepted values for single argument
    :param choices_provider: function that provides choices for single argument
    :param completer: completion function that provides choices for single argument
    :param parser: an argument parser which supports the completion of multiple arguments
    :return: the line read from stdin with all trailing new lines removed
    :raises EOFError: if the input stream is closed or the user signals EOF (e.g., Ctrl+D)
    :raises Exception: any other exceptions raised by prompt()
    """
    completer_to_use = self._resolve_completer(
        preserve_quotes=preserve_quotes,
        choices=choices,
        choices_provider=choices_provider,
        completer=completer,
        parser=parser,
    )

    temp_session: PromptSession[str] = PromptSession(
        auto_suggest=self.main_session.auto_suggest,
        color_depth=self.main_session.color_depth,
        complete_style=self.main_session.complete_style,
        complete_in_thread=self.main_session.complete_in_thread,
        complete_while_typing=self.main_session.complete_while_typing,
        completer=completer_to_use,
        history=InMemoryHistory(history) if history is not None else InMemoryHistory(),
        key_bindings=self.main_session.key_bindings,
        input=self.main_session.input,
        output=self.main_session.output,
        style=self.main_session.style,
    )

    return self._read_raw_input(prompt, temp_session)

read_secret

read_secret(prompt='')

Read a secret from stdin without displaying the value on the screen.

PARAMETER DESCRIPTION
prompt

prompt to display to user

TYPE: str DEFAULT: ''

RETURNS DESCRIPTION
str

the secret read from stdin with all trailing new lines removed

RAISES DESCRIPTION
EOFError

if the input stream is closed or the user signals EOF (e.g., Ctrl+D)

Exception

any other exceptions raised by prompt()

Source code in cmd2/cmd2.py
def read_secret(
    self,
    prompt: str = "",
) -> str:
    """Read a secret from stdin without displaying the value on the screen.

    :param prompt: prompt to display to user
    :return: the secret read from stdin with all trailing new lines removed
    :raises EOFError: if the input stream is closed or the user signals EOF (e.g., Ctrl+D)
    :raises Exception: any other exceptions raised by prompt()
    """
    temp_session: PromptSession[str] = PromptSession(
        color_depth=self.main_session.color_depth,
        input=self.main_session.input,
        output=self.main_session.output,
        style=self.main_session.style,
    )

    return self._read_raw_input(prompt, temp_session, is_password=True)

do_alias

do_alias(args)

Manage aliases.

Source code in cmd2/cmd2.py
@with_argparser(_build_alias_parser, preserve_quotes=True)
def do_alias(self, args: argparse.Namespace) -> None:
    """Manage aliases."""
    # Call function for whatever subcommand was selected
    args.cmd2_subcommand_func(args)

macro_arg_complete

macro_arg_complete(text, line, begidx, endidx)

Completes arguments to a macro.

Its default behavior is to call path_complete, but you can override this as needed.

PARAMETER DESCRIPTION
text

the string prefix we are attempting to match (all matches must begin with it)

TYPE: str

line

the current input line with leading whitespace removed

TYPE: str

begidx

the beginning index of the prefix text

TYPE: int

endidx

the ending index of the prefix text

TYPE: int

RETURNS DESCRIPTION
Completions

a Completions object

Source code in cmd2/cmd2.py
def macro_arg_complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
) -> Completions:
    """Completes arguments to a macro.

    Its default behavior is to call path_complete, but you can override this as needed.

    :param text: the string prefix we are attempting to match (all matches must begin with it)
    :param line: the current input line with leading whitespace removed
    :param begidx: the beginning index of the prefix text
    :param endidx: the ending index of the prefix text
    :return: a Completions object
    """
    return self.path_complete(text, line, begidx, endidx)

do_macro

do_macro(args)

Manage macros.

Source code in cmd2/cmd2.py
@with_argparser(_build_macro_parser, preserve_quotes=True)
def do_macro(self, args: argparse.Namespace) -> None:
    """Manage macros."""
    # Call function for whatever subcommand was selected
    args.cmd2_subcommand_func(args)

complete_help_command

complete_help_command(text, line, begidx, endidx)

Completes the command argument of help.

Source code in cmd2/cmd2.py
def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> Completions:
    """Completes the command argument of help."""
    # Complete token against topics and visible commands
    topics = set(self.get_help_topics())
    visible_commands = set(self.get_visible_commands())
    strs_to_match = list(topics | visible_commands)
    return self.basic_complete(text, line, begidx, endidx, strs_to_match)

complete_help_subcommands

complete_help_subcommands(
    text, line, begidx, endidx, arg_tokens
)

Completes the subcommands argument of help.

Source code in cmd2/cmd2.py
def complete_help_subcommands(
    self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Mapping[str, Sequence[str]]
) -> Completions:
    """Completes the subcommands argument of help."""
    # Make sure we have a command whose subcommands we will complete
    command = arg_tokens["command"][0]
    if not command:
        return Completions()

    # Check if this command uses argparse
    if (command_func := self.get_command_func(command)) is None or (
        parser := self.command_parsers.get(command_func)
    ) is None:
        return Completions()

    completer = parser.completer_class(parser, self)
    return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens["subcommands"])

do_help

do_help(args)

List available commands or provide detailed help for a specific command.

Source code in cmd2/cmd2.py
@with_argparser(_build_help_parser)
def do_help(self, args: argparse.Namespace) -> None:
    """List available commands or provide detailed help for a specific command."""
    self.last_result = True

    if not args.command or args.verbose:
        cmds_cats, help_topics = self._build_command_info()

        if self.doc_leader:
            self.poutput()
            self.poutput(Text(self.doc_leader, style=Cmd2Style.HELP_LEADER))
        self.poutput()

        # Used to provide verbose table separation for better readability.
        previous_table_printed = False

        # Print commands grouped by category
        sorted_categories = sorted(cmds_cats.keys(), key=utils.DEFAULT_STR_SORT_KEY)
        all_cmds = {category: cmds_cats[category] for category in sorted_categories}

        for category, commands in all_cmds.items():
            if previous_table_printed:
                self.poutput()

            self._print_documented_command_topics(category, commands, args.verbose)
            previous_table_printed = bool(commands) and args.verbose

        if previous_table_printed and help_topics:
            self.poutput()

        # Print help topics table
        self.print_topics(self.MISC_HEADER, help_topics, 15, 80)

    else:
        # Getting help for a specific command
        disabled = args.command in self.disabled_commands
        help_func = getattr(self, constants.HELP_FUNC_PREFIX + args.command, None)

        # If the command is disabled, then call the help function which was
        # overwritten by disable_command() to print the disabled message.
        if disabled:
            if help_func is not None:
                help_func()
            else:
                # Handle potential case where command is disabled by manually editing
                # self.disabled_commands instead of using disable_command().
                self._report_disabled_command_usage(message_to_print=f"{args.command} is currently disabled.")
            return

        command_func = self.get_command_func(args.command)
        parser = None if command_func is None else self.command_parsers.get(command_func)

        # If the command function uses argparse, then use argparse's help
        if command_func is not None and parser is not None:
            completer = parser.completer_class(parser, self)
            completer.print_help(args.subcommands, self.stdout)

        # If the command has a custom help function, then call it
        elif help_func is not None:
            help_func()

        # If the command function has a docstring, then print it
        elif command_func is not None and command_func.__doc__ is not None:
            self.poutput(pydoc.getdoc(command_func))

        # If there is no help information then print an error
        else:
            err_msg = self.help_error.format(args.command)
            self.perror(err_msg, style=None)
            self.last_result = False

print_topics

print_topics(header, cmds, cmdlen, maxcol)

Print groups of commands and topics in columns and an optional header.

Override of cmd's print_topics() to use Rich.

PARAMETER DESCRIPTION
header

string to print above commands being printed

TYPE: str

cmds

Sequence of topics to print

TYPE: Sequence[str] | None

cmdlen

unused, even by cmd's version

TYPE: int

maxcol

max number of display columns to fit into

TYPE: int

Source code in cmd2/cmd2.py
def print_topics(self, header: str, cmds: Sequence[str] | None, cmdlen: int, maxcol: int) -> None:  # noqa: ARG002
    """Print groups of commands and topics in columns and an optional header.

    Override of cmd's print_topics() to use Rich.

    :param header: string to print above commands being printed
    :param cmds: Sequence of topics to print
    :param cmdlen: unused, even by cmd's version
    :param maxcol: max number of display columns to fit into
    """
    if not cmds:
        return

    if header:
        self.poutput(
            self._create_help_grid(header),
            soft_wrap=False,
        )

    # Subtract 1 from maxcol to account for a one-space right margin.
    maxcol = min(maxcol, ru.console_width()) - 1
    self.columnize(cmds, maxcol)
    self.poutput()

render_columns

render_columns(str_list, display_width=80)

Render a list of single-line strings as a compact set of columns.

This method correctly handles strings containing ANSI style sequences and full-width characters (like those used in CJK languages). Each column is only as wide as necessary and columns are separated by two spaces.

PARAMETER DESCRIPTION
str_list

Sequence of single-line strings to display

TYPE: Sequence[str] | None

display_width

max number of display columns to fit into

TYPE: int DEFAULT: 80

RETURNS DESCRIPTION
str

a string containing the columnized output

Source code in cmd2/cmd2.py
def render_columns(self, str_list: Sequence[str] | None, display_width: int = 80) -> str:
    """Render a list of single-line strings as a compact set of columns.

    This method correctly handles strings containing ANSI style sequences and
    full-width characters (like those used in CJK languages). Each column is
    only as wide as necessary and columns are separated by two spaces.

    :param str_list: Sequence of single-line strings to display
    :param display_width: max number of display columns to fit into
    :return: a string containing the columnized output
    """
    if not str_list:
        return ""

    size = len(str_list)
    if size == 1:
        return str_list[0]

    rows: list[str] = []

    # Try every row count from 1 upwards
    for nrows in range(1, len(str_list)):
        ncols = (size + nrows - 1) // nrows
        colwidths = []
        totwidth = -2
        for col in range(ncols):
            colwidth = 0
            for row in range(nrows):
                i = row + nrows * col
                if i >= size:
                    break
                x = str_list[i]
                colwidth = max(colwidth, su.str_width(x))
            colwidths.append(colwidth)
            totwidth += colwidth + 2
            if totwidth > display_width:
                break
        if totwidth <= display_width:
            break
    else:
        # The output is wider than display_width. Print 1 column with each string on its own row.
        nrows = len(str_list)
        ncols = 1
        max_width = max(su.str_width(s) for s in str_list)
        colwidths = [max_width]
    for row in range(nrows):
        texts = []
        for col in range(ncols):
            i = row + nrows * col
            x = "" if i >= size else str_list[i]
            texts.append(x)
        while texts and not texts[-1]:
            del texts[-1]
        for col in range(len(texts)):
            texts[col] = su.align_left(texts[col], width=colwidths[col])
        rows.append("  ".join(texts))

    return "\n".join(rows)

columnize

columnize(str_list, display_width=80)

Display a list of single-line strings as a compact set of columns.

Override of cmd's columnize() that uses the render_columns() method. The method correctly handles strings with ANSI style sequences and full-width characters (like those used in CJK languages).

PARAMETER DESCRIPTION
str_list

Sequence of single-line strings to display

TYPE: Sequence[str] | None

display_width

max number of display columns to fit into

TYPE: int DEFAULT: 80

Source code in cmd2/cmd2.py
def columnize(self, str_list: Sequence[str] | None, display_width: int = 80) -> None:
    """Display a list of single-line strings as a compact set of columns.

    Override of cmd's columnize() that uses the render_columns() method.
    The method correctly handles strings with ANSI style sequences and
    full-width characters (like those used in CJK languages).

    :param str_list: Sequence of single-line strings to display
    :param display_width: max number of display columns to fit into
    """
    columnized_strs = self.render_columns(str_list, display_width)
    self.poutput(columnized_strs)

do_shortcuts

do_shortcuts(_)

List available shortcuts.

Source code in cmd2/cmd2.py
@with_argparser(_build_shortcuts_parser)
def do_shortcuts(self, _: argparse.Namespace) -> None:
    """List available shortcuts."""
    # Sort the shortcut tuples by name
    sorted_shortcuts = sorted(self.statement_parser.shortcuts, key=lambda x: utils.DEFAULT_STR_SORT_KEY(x[0]))
    result = "\n".join(f"{sc[0]}: {sc[1]}" for sc in sorted_shortcuts)
    self.poutput(f"Shortcuts for other commands:\n{result}")
    self.last_result = True

do__eof

do__eof(_)

Quit with no arguments, called when Ctrl-D is pressed.

This can be overridden if quit should be called differently.

Source code in cmd2/cmd2.py
@with_argparser(_build__eof_parser)
def do__eof(self, _: argparse.Namespace) -> bool | None:
    """Quit with no arguments, called when Ctrl-D is pressed.

    This can be overridden if quit should be called differently.
    """
    self.poutput()

    # self.last_result will be set by do_quit()
    return self.do_quit("")

do_quit

do_quit(_)

Exit this application.

Source code in cmd2/cmd2.py
@with_argparser(_build_quit_parser)
def do_quit(self, _: argparse.Namespace) -> bool | None:
    """Exit this application."""
    # Return True to stop the command loop
    self.last_result = True
    return True

select

select(opts, prompt='Your choice? ')

Present a menu to the user.

Modeled after the bash shell's SELECT. Returns the item chosen.

Argument opts can be:

| a single string -> will be split into one-word options | a list of strings -> will be offered as options | a list of tuples -> interpreted as (value, text), so that the return value can differ from the text advertised to the user

Source code in cmd2/cmd2.py
def select(self, opts: str | Iterable[str] | Iterable[tuple[Any, str | None]], prompt: str = "Your choice? ") -> Any:
    """Present a menu to the user.

    Modeled after the bash shell's SELECT.  Returns the item chosen.

    Argument ``opts`` can be:

      | a single string -> will be split into one-word options
      | a list of strings -> will be offered as options
      | a list of tuples -> interpreted as (value, text), so
                            that the return value can differ from
                            the text advertised to the user
    """
    local_opts: Iterable[str] | Iterable[tuple[Any, str | None]]
    if isinstance(opts, str):
        local_opts = cast(list[tuple[Any, str | None]], list(zip(opts.split(), opts.split(), strict=False)))
    else:
        local_opts = opts
    fulloptions: list[tuple[Any, str]] = []
    for opt in local_opts:
        if isinstance(opt, str):
            fulloptions.append((opt, opt))
        else:
            try:
                val = opt[0]
                text = str(opt[1]) if len(opt) > 1 and opt[1] is not None else str(val)
                fulloptions.append((val, text))
            except (IndexError, TypeError):
                fulloptions.append((opt[0], str(opt[0])))

    if self._is_tty_session(self.main_session):
        try:
            while True:
                with create_app_session(input=self.main_session.input, output=self.main_session.output):
                    result = choice(message=prompt, options=fulloptions)
                if result is not None:
                    return result
        except KeyboardInterrupt:
            self.poutput("^C")
            raise

    # Non-interactive fallback
    for idx, (_, text) in enumerate(fulloptions):
        self.poutput("  %2d. %s" % (idx + 1, text))  # noqa: UP031

    while True:
        try:
            response = self.read_input(prompt)
        except EOFError:
            response = ""
            self.poutput()
        except KeyboardInterrupt:
            self.poutput("^C")
            raise

        if not response:
            continue

        try:
            choice_idx = int(response)
            if choice_idx < 1:
                raise IndexError  # noqa: TRY301
            return fulloptions[choice_idx - 1][0]
        except (ValueError, IndexError):
            self.poutput(f"'{response}' isn't a valid choice. Pick a number between 1 and {len(fulloptions)}:")

complete_set_value

complete_set_value(text, line, begidx, endidx, arg_tokens)

Completes the value argument of set.

Source code in cmd2/cmd2.py
def complete_set_value(
    self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Mapping[str, Sequence[str]]
) -> Completions:
    """Completes the value argument of set."""
    param = arg_tokens["param"][0]
    try:
        settable = self.settables[param]
    except KeyError as ex:
        raise CompletionError(param + " is not a settable parameter") from ex

    # Create a parser with a value field based on this settable
    settable_parser = self._build_base_set_parser()

    # Settables with choices list the values of those choices instead of the arg name
    # in help text and this shows in completion hints. Set metavar to avoid this.
    arg_name = "value"
    settable_parser.add_argument(
        arg_name,
        metavar=arg_name,
        help=settable.description,
        choices=settable.choices,
        choices_provider=settable.choices_provider,
        completer=settable.completer,
    )

    completer = settable_parser.completer_class(settable_parser, self)

    # Use raw_tokens since quotes have been preserved
    _, raw_tokens = self.tokens_for_completion(line, begidx, endidx)
    return completer.complete(text, line, begidx, endidx, raw_tokens[1:])

do_set

do_set(args)

Set a settable parameter or show current settings of parameters.

Source code in cmd2/cmd2.py
@with_argparser(_build_set_parser, preserve_quotes=True)
def do_set(self, args: argparse.Namespace) -> None:
    """Set a settable parameter or show current settings of parameters."""
    self.last_result = False

    if not self.settables:
        self.pwarning("There are no settable parameters")
        return

    if args.param:
        try:
            settable = self.settables[args.param]
        except KeyError:
            self.perror(f"Parameter '{args.param}' not supported (type 'set' for list of parameters).")
            return

        if args.value:
            # Try to update the settable's value
            try:
                settable.value = su.strip_quotes(args.value)
            except ValueError as ex:
                self.perror(f"Error setting {args.param}: {ex}")
            else:
                # Create the feedback message using Rich Text for color
                feedback_msg = Text.assemble(
                    f"{args.param} ─> ",
                    (f"{settable.value!r}", Cmd2Style.SUCCESS),
                )
                self.pfeedback(feedback_msg)

                self.last_result = True
            return

        # Show one settable
        to_show: list[str] = [args.param]
    else:
        # Show all settables
        to_show = list(self.settables.keys())

    settable_table = Cmd2SimpleTable(
        Column("Name", no_wrap=True),
        Column("Value", overflow="fold"),
        Column("Description", overflow="fold"),
    )

    # Build the table and populate self.last_result
    self.last_result = {}  # dict[settable_name, settable_value]

    for param in sorted(to_show, key=utils.DEFAULT_STR_SORT_KEY):
        settable = self.settables[param]
        settable_table.add_row(
            param,
            str(settable.value),
            Text.from_ansi(settable.description),
        )
        self.last_result[param] = settable.value

    self.poutput()
    self.poutput(settable_table, soft_wrap=False)
    self.poutput()

do_shell

do_shell(args)

Execute a command as if at the OS prompt.

Source code in cmd2/cmd2.py
@with_argparser(_build_shell_parser, preserve_quotes=True)
def do_shell(self, args: argparse.Namespace) -> None:
    """Execute a command as if at the OS prompt."""
    import signal
    import subprocess

    kwargs: dict[str, Any] = {}

    # Set OS-specific parameters
    if sys.platform.startswith("win"):
        # Windows returns STATUS_CONTROL_C_EXIT when application stopped by Ctrl-C
        ctrl_c_ret_code = 0xC000013A
    else:
        # On POSIX, Popen() returns -SIGINT when application stopped by Ctrl-C
        ctrl_c_ret_code = signal.SIGINT.value * -1

        # On POSIX with shell=True, Popen() defaults to /bin/sh as the shell.
        # sh reports an incorrect return code for some applications when Ctrl-C is pressed within that
        # application (e.g. less). Since sh received the SIGINT, it sets the return code to reflect being
        # closed by SIGINT even though less did not exit upon a Ctrl-C press. In the same situation, other
        # shells like bash and zsh report the actual return code of less. Therefore, we will try to run the
        # user's preferred shell which most likely will be something other than sh. This also allows the user
        # to run builtin commands of their preferred shell.
        shell = os.environ.get("SHELL")
        if shell:
            kwargs["executable"] = shell

    # Create a list of arguments to shell
    tokens = [args.command, *args.command_args]

    # Expand ~ where needed
    utils.expand_user_in_tokens(tokens)
    expanded_command = " ".join(tokens)

    # Prevent KeyboardInterrupts while in the shell process. The shell process will
    # still receive the SIGINT since it is in the same process group as us.
    with self.sigint_protection:
        # For any stream that is a StdSim, we will use a pipe so we can capture its output
        proc = subprocess.Popen(  # noqa: S602
            expanded_command,
            stdout=subprocess.PIPE if isinstance(self.stdout, utils.StdSim) else self.stdout,  # type: ignore[unreachable]
            stderr=subprocess.PIPE if isinstance(sys.stderr, utils.StdSim) else sys.stderr,
            shell=True,
            **kwargs,
        )

        proc_reader = utils.ProcReader(proc, self.stdout, sys.stderr)
        proc_reader.wait()

        # Save the return code of the application for use in a pyscript
        self.last_result = proc.returncode

        # If the process was stopped by Ctrl-C, then inform the caller by raising a KeyboardInterrupt.
        # This is to support things like stop_on_keyboard_interrupt in runcmds_plus_hooks().
        if proc.returncode == ctrl_c_ret_code:
            self._raise_keyboard_interrupt()

do_py

do_py(_)

Run an interactive Python shell.

RETURNS DESCRIPTION
bool | None

True if running of commands should stop.

Source code in cmd2/cmd2.py
@with_argparser(_build_py_parser)
def do_py(self, _: argparse.Namespace) -> bool | None:
    """Run an interactive Python shell.

    :return: True if running of commands should stop.
    """
    # self.last_result will be set by _run_python()
    return self._run_python()

do_run_pyscript

do_run_pyscript(args)

Run Python script within this application's environment.

RETURNS DESCRIPTION
bool | None

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(_build_run_pyscript_parser)
def do_run_pyscript(self, args: argparse.Namespace) -> bool | None:
    """Run Python script within this application's environment.

    :return: True if running of commands should stop
    """
    self.last_result = False

    # Expand ~ before placing this path in sys.argv just as a shell would
    args.script_path = os.path.expanduser(args.script_path)

    # Add some protection against accidentally running a non-Python file. The happens when users
    # mix up run_script and run_pyscript.
    if not args.script_path.endswith(".py"):
        self.pwarning(f"'{args.script_path}' does not have a .py extension")
        selection = self.select("Yes No", "Continue to try to run it as a Python script? ")
        if selection != "Yes":
            return None

    # Save current command line arguments
    orig_args = sys.argv

    try:
        # Overwrite sys.argv to allow the script to take command line arguments
        sys.argv = [args.script_path, *args.script_arguments]

        # self.last_result will be set by _run_python()
        py_return = self._run_python(pyscript=args.script_path)
    finally:
        # Restore command line arguments to original state
        sys.argv = orig_args

    return py_return

do_ipy

do_ipy(_)

Run an interactive IPython shell.

RETURNS DESCRIPTION
bool | None

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(_build_ipython_parser)
def do_ipy(self, _: argparse.Namespace) -> bool | None:  # pragma: no cover
    """Run an interactive IPython shell.

    :return: True if running of commands should stop
    """
    self.last_result = False

    # Detect whether IPython is installed
    try:
        import traitlets.config.loader as traitlets_loader

        # Allow users to install ipython from a cmd2 prompt when needed and still have ipy command work
        try:
            _dummy = start_ipython  # noqa: F823
        except NameError:
            from IPython import start_ipython

        from IPython.terminal.interactiveshell import TerminalInteractiveShell
        from IPython.terminal.ipapp import TerminalIPythonApp
    except ImportError:
        self.perror("IPython package is not installed")
        return None

    from .py_bridge import PyBridge

    if self.in_pyscript():
        self.perror("Recursively entering interactive Python shells is not allowed")
        return None

    self.last_result = True

    try:
        self._in_py = True
        py_bridge = PyBridge(self)

        # Make a copy of self.py_locals for the locals dictionary in the IPython environment we are creating.
        # This is to prevent ipy from editing it. (e.g. locals().clear()). Only make a shallow copy since
        # it's OK for py_locals to contain objects which are editable in ipy.
        local_vars = self.py_locals.copy()
        local_vars[self.py_bridge_name] = py_bridge
        if self.self_in_py:
            local_vars["self"] = self

        # Configure IPython
        config = traitlets_loader.Config()
        config.InteractiveShell.banner2 = (
            "Entering an IPython shell. Type exit, quit, or Ctrl-D to exit.\n"
            f'Run CLI commands with: {self.py_bridge_name}("command ...")\n'
        )

        # Start IPython
        start_ipython(config=config, argv=[], user_ns=local_vars)  # type: ignore[no-untyped-call]
        self.poutput("Now exiting IPython shell...")

        # The IPython application is a singleton and won't be recreated next time
        # this function runs. That's a problem since the contents of local_vars
        # may need to be changed. Therefore, we must destroy all instances of the
        # relevant classes.
        TerminalIPythonApp.clear_instance()
        TerminalInteractiveShell.clear_instance()

        return py_bridge.stop
    finally:
        self._in_py = False

do_history

do_history(args)

View, run, edit, save, or clear previously entered commands.

RETURNS DESCRIPTION
bool | None

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(_build_history_parser)
def do_history(self, args: argparse.Namespace) -> bool | None:
    """View, run, edit, save, or clear previously entered commands.

    :return: True if running of commands should stop
    """
    self.last_result = False

    # -v must be used alone with no other options
    if args.verbose:  # noqa: SIM102
        if args.clear or args.edit or args.output_file or args.run or args.expanded or args.script:
            self.poutput("-v cannot be used with any other options")
            return None

    # -s and -x can only be used if none of these options are present: [-c -r -e -o -t]
    if (args.script or args.expanded) and (args.clear or args.edit or args.output_file or args.run):
        self.poutput("-s and -x cannot be used with -c, -r, -e, or -o")
        return None

    if args.clear:
        self.last_result = True

        # Clear command and prompt-toolkit history
        self.history.clear()
        cast(Cmd2History, self.main_session.history).clear()

        if self.persistent_history_file:
            try:
                os.remove(self.persistent_history_file)
            except FileNotFoundError:
                pass
            except OSError as ex:
                self.perror(f"Error removing history file '{self.persistent_history_file}': {ex}")
                self.last_result = False
                return None

        return None

    # If an argument was supplied, then retrieve partial contents of the history, otherwise retrieve it all
    history = self._get_history(args)

    if args.run:
        if not args.arg:
            self.perror("Cowardly refusing to run all previously entered commands.")
            self.perror("If this is what you want to do, specify '1:' as the range of history.")
        else:
            stop = self.runcmds_plus_hooks(list(history.values()))
            self.last_result = True
            return stop
    elif args.edit:
        fd, fname = tempfile.mkstemp(suffix=".txt", text=True)
        fobj: TextIO
        with os.fdopen(fd, "w") as fobj:
            for command in history.values():
                if command.statement.multiline_command:
                    fobj.write(f"{command.expanded}\n")
                else:
                    fobj.write(f"{command.raw}\n")
        try:
            self.run_editor(fname)

            # self.last_result will be set by do_run_script()
            return self.do_run_script(su.quote(fname))
        finally:
            os.remove(fname)
    elif args.output_file:
        full_path = os.path.abspath(os.path.expanduser(args.output_file))
        try:
            with open(full_path, "w") as fobj:
                for item in history.values():
                    if item.statement.multiline_command:
                        fobj.write(f"{item.expanded}\n")
                    else:
                        fobj.write(f"{item.raw}\n")
            plural = "" if len(history) == 1 else "s"
        except OSError as ex:
            self.perror(f"Error saving history file '{full_path}': {ex}")
        else:
            self.pfeedback(f"{len(history)} command{plural} saved to {full_path}")
            self.last_result = True
    else:
        # Display the history items retrieved
        for idx, hi in history.items():
            self.poutput(hi.pr(idx, script=args.script, expanded=args.expanded, verbose=args.verbose))
        self.last_result = history
    return None

do_edit

do_edit(args)

Run a text editor and optionally open a file with it.

Source code in cmd2/cmd2.py
@with_argparser(_build_edit_parser)
def do_edit(self, args: argparse.Namespace) -> None:
    """Run a text editor and optionally open a file with it."""
    # self.last_result will be set by do_shell() which is called by run_editor()
    self.run_editor(args.file_path)

run_editor

run_editor(file_path=None)

Run a text editor and optionally open a file with it.

PARAMETER DESCRIPTION
file_path

optional path of the file to edit. Defaults to None.

TYPE: str | None DEFAULT: None

RAISES DESCRIPTION
ValueError

if self.editor is not set

Source code in cmd2/cmd2.py
def run_editor(self, file_path: str | None = None) -> None:
    """Run a text editor and optionally open a file with it.

    :param file_path: optional path of the file to edit. Defaults to None.
    :raises ValueError: if self.editor is not set
    """
    if not self.editor:
        raise ValueError("Please use 'set editor' to specify your text editing program of choice.")

    command = su.quote(os.path.expanduser(self.editor))
    if file_path:
        command += " " + su.quote(os.path.expanduser(file_path))

    self.do_shell(command)

do_run_script

do_run_script(args)

Run text script.

RETURNS DESCRIPTION
bool | None

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(_build_run_script_parser)
def do_run_script(self, args: argparse.Namespace) -> bool | None:
    """Run text script.

    :return: True if running of commands should stop
    """
    self.last_result = False
    expanded_path = os.path.abspath(os.path.expanduser(args.script_path))

    # Add some protection against accidentally running a Python file. The happens when users
    # mix up run_script and run_pyscript.
    if expanded_path.endswith(".py"):
        self.pwarning(f"'{expanded_path}' appears to be a Python file")
        selection = self.select("Yes No", "Continue to try to run it as a text script? ")
        if selection != "Yes":
            return None

    try:
        # An empty file is not an error, so just return
        if os.path.getsize(expanded_path) == 0:
            self.last_result = True
            return None

        # Make sure the file is ASCII or UTF-8 encoded text
        if not utils.is_text_file(expanded_path):
            self.perror(f"'{expanded_path}' is not an ASCII or UTF-8 encoded text file")
            return None

        # Read all lines of the script
        with open(expanded_path, encoding="utf-8") as target:
            script_commands = target.read().splitlines()
    except OSError as ex:
        self.perror(f"Problem accessing script from '{expanded_path}': {ex}")
        return None

    orig_script_dir_count = len(self._script_dir)

    try:
        self._script_dir.append(os.path.dirname(expanded_path))
        stop = self.runcmds_plus_hooks(
            script_commands,
            add_to_history=self.scripts_add_to_history,
            stop_on_keyboard_interrupt=True,
        )
        self.last_result = True
        return stop
    finally:
        with self.sigint_protection:
            # Check if a script dir was added before an exception occurred
            if orig_script_dir_count != len(self._script_dir):
                self._script_dir.pop()

do__relative_run_script

do__relative_run_script(args)

Run text script.

This command is intended to be used from within a text script.

RETURNS DESCRIPTION
bool | None

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(_build__relative_run_script_parser)
def do__relative_run_script(self, args: argparse.Namespace) -> bool | None:
    """Run text script.

    This command is intended to be used from within a text script.

    :return: True if running of commands should stop
    """
    script_path = args.script_path
    # NOTE: Relative path is an absolute path, it is just relative to the current script directory
    relative_path = os.path.join(self._current_script_dir or "", script_path)

    # self.last_result will be set by do_run_script()
    return self.do_run_script(su.quote(relative_path))

add_alert

add_alert(*, msg=None, prompt=None)

Queue an asynchronous alert to be displayed when the prompt is active.

Examples: add_alert(msg="System error!") # Print message only add_alert(prompt="user@host> ") # Update prompt only add_alert(msg="Done", prompt="> ") # Update both

PARAMETER DESCRIPTION
msg

an optional message to be printed above the prompt.

TYPE: str | None DEFAULT: None

prompt

an optional string to dynamically replace the current prompt.

TYPE: str | None DEFAULT: None

Source code in cmd2/cmd2.py
def add_alert(self, *, msg: str | None = None, prompt: str | None = None) -> None:
    """Queue an asynchronous alert to be displayed when the prompt is active.

    Examples:
        add_alert(msg="System error!")        # Print message only
        add_alert(prompt="user@host> ")       # Update prompt only
        add_alert(msg="Done", prompt="> ")    # Update both

    :param msg: an optional message to be printed above the prompt.
    :param prompt: an optional string to dynamically replace the current prompt.

    """
    if msg is None and prompt is None:
        return

    with self._alert_condition:
        alert = AsyncAlert(msg=msg, prompt=prompt)
        self._alert_queue.append(alert)
        self._alert_condition.notify_all()

set_window_title staticmethod

set_window_title(title)

Set the terminal window title.

PARAMETER DESCRIPTION
title

the new window title

TYPE: str

Source code in cmd2/cmd2.py
@staticmethod
def set_window_title(title: str) -> None:  # pragma: no cover
    """Set the terminal window title.

    :param title: the new window title
    """
    set_title(title)

enable_command

enable_command(command)

Enable a command by restoring its functions.

PARAMETER DESCRIPTION
command

the command being enabled

TYPE: str

Source code in cmd2/cmd2.py
def enable_command(self, command: str) -> None:
    """Enable a command by restoring its functions.

    :param command: the command being enabled
    """
    # If the command is already enabled, then return
    if command not in self.disabled_commands:
        return

    command_func_name = constants.COMMAND_FUNC_PREFIX + command
    help_func_name = constants.HELP_FUNC_PREFIX + command
    completer_func_name = constants.COMPLETER_FUNC_PREFIX + command

    # Restore the command function to its original value
    dc = self.disabled_commands[command]
    setattr(self, command_func_name, dc.command_func)

    # Restore the help function to its original value
    if dc.help_func is None:
        delattr(self, help_func_name)
    else:
        setattr(self, help_func_name, dc.help_func)

    # Restore the completer function to its original value
    if dc.completer_func is None:
        delattr(self, completer_func_name)
    else:
        setattr(self, completer_func_name, dc.completer_func)

    # Remove the disabled command entry
    del self.disabled_commands[command]

enable_category

enable_category(category)

Enable an entire category of commands.

PARAMETER DESCRIPTION
category

the category to enable

TYPE: str

Source code in cmd2/cmd2.py
def enable_category(self, category: str) -> None:
    """Enable an entire category of commands.

    :param category: the category to enable
    """
    # If the category is already enabled, then return
    if category not in self.disabled_categories:
        return

    for command in list(self.disabled_commands):
        command_func = self.disabled_commands[command].command_func
        if self._get_command_category(command_func) == category:
            self.enable_command(command)

    del self.disabled_categories[category]

disable_command

disable_command(command, message_to_print)

Disable a command and replace its functions with disabled versions.

PARAMETER DESCRIPTION
command

the command being disabled

TYPE: str

message_to_print

what to print when this command is run or help is called on it while disabled

The variable cmd2.COMMAND_NAME can be used as a placeholder for the name of the command being disabled. ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled"

TYPE: str

Source code in cmd2/cmd2.py
def disable_command(self, command: str, message_to_print: str) -> None:
    """Disable a command and replace its functions with disabled versions.

    :param command: the command being disabled
    :param message_to_print: what to print when this command is run or help is called on it while disabled

                             The variable cmd2.COMMAND_NAME can be used as a placeholder for the name of the
                             command being disabled.
                             ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled"
    """
    # If the command is already disabled, then return
    if command in self.disabled_commands:
        return

    # Make sure this is an actual command
    command_func = self.get_command_func(command)
    if command_func is None:
        raise AttributeError(f"'{command}' does not refer to a command")

    command_func_name = constants.COMMAND_FUNC_PREFIX + command

    help_func_name = constants.HELP_FUNC_PREFIX + command
    help_func = getattr(self, help_func_name, None)

    completer_func_name = constants.COMPLETER_FUNC_PREFIX + command
    completer_func = getattr(self, completer_func_name, None)

    # Add the disabled command record
    self.disabled_commands[command] = DisabledCommand(
        command_func=command_func,
        help_func=help_func,
        completer_func=completer_func,
    )

    # Replace command and help functions to report the disabled message
    message_to_print = message_to_print.replace(constants.COMMAND_NAME, command)
    new_cmd_func = functools.partial(
        self._report_disabled_command_usage,
        message_to_print=message_to_print,
    )

    # Ensure the replacement function identifies as the original for introspection
    functools.update_wrapper(new_cmd_func, command_func)
    setattr(self, command_func_name, new_cmd_func)

    new_help_func = functools.partial(
        self._report_disabled_command_usage,
        message_to_print=message_to_print,
    )
    if help_func is not None:
        functools.update_wrapper(new_help_func, help_func)
    setattr(self, help_func_name, new_help_func)

    # Replace completer with a function that returns nothing
    new_completer_func = functools.partial(self._disabled_completer)
    if completer_func is not None:
        functools.update_wrapper(new_completer_func, completer_func)
    setattr(self, completer_func_name, new_completer_func)

disable_category

disable_category(category, message_to_print)

Disable an entire category of commands.

PARAMETER DESCRIPTION
category

the category to disable

TYPE: str

message_to_print

what to print when anything in this category is run or help is called on it while disabled. The variable cmd2.COMMAND_NAME can be used as a placeholder for the name of the command being disabled. ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled"

TYPE: str

Source code in cmd2/cmd2.py
def disable_category(self, category: str, message_to_print: str) -> None:
    """Disable an entire category of commands.

    :param category: the category to disable
    :param message_to_print: what to print when anything in this category is run or help is called on it
                             while disabled. The variable cmd2.COMMAND_NAME can be used as a placeholder for the name
                             of the command being disabled.
                             ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled"
    """
    # If the category is already disabled, then return
    if category in self.disabled_categories:
        return

    all_commands = self.get_all_commands()

    for command in all_commands:
        command_func = cast(BoundCommandFunc, self.get_command_func(command))
        if self._get_command_category(command_func) == category:
            self.disable_command(command, message_to_print)

    self.disabled_categories[category] = message_to_print

cmdloop

cmdloop(intro='')

Deal with extra features provided by cmd2, this is an outer wrapper around _cmdloop().

_cmdloop() provides the main loop. This provides the following extra features provided by cmd2: - intro banner - exit code

PARAMETER DESCRIPTION
intro

if provided this overrides self.intro and serves as the intro banner printed once at start

TYPE: RenderableType DEFAULT: ''

RETURNS DESCRIPTION
int

exit code

Source code in cmd2/cmd2.py
def cmdloop(self, intro: RenderableType = "") -> int:
    """Deal with extra features provided by cmd2, this is an outer wrapper around _cmdloop().

    _cmdloop() provides the main loop.  This provides the following extra features provided by cmd2:
    - intro banner
    - exit code

    :param intro: if provided this overrides self.intro and serves as the intro banner printed once at start
    :return: exit code
    """
    # cmdloop() expects to be run in the main thread to support extensive use of KeyboardInterrupts throughout the
    # other built-in functions. You are free to override cmdloop, but much of cmd2's features will be limited.
    if threading.current_thread() is not threading.main_thread():
        raise RuntimeError("cmdloop must be run in the main thread")

    # Register signal handlers
    import signal

    original_sigint_handler = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, self.sigint_handler)

    if not sys.platform.startswith("win"):
        original_sighup_handler = signal.getsignal(signal.SIGHUP)
        signal.signal(signal.SIGHUP, self.termination_signal_handler)

        original_sigterm_handler = signal.getsignal(signal.SIGTERM)
        signal.signal(signal.SIGTERM, self.termination_signal_handler)

    # Always run the preloop first
    for func in self._preloop_hooks:
        func()
    self.preloop()

    # If an intro was supplied in the method call, allow it to override the default
    if intro:
        self.intro = intro

    # Print the intro, if there is one, right after the preloop
    if self.intro:
        self.poutput(self.intro)

    # And then call _cmdloop() to enter the main loop
    self._cmdloop()

    # Run the postloop() no matter what
    for func in self._postloop_hooks:
        func()
    self.postloop()

    # Restore original signal handlers
    signal.signal(signal.SIGINT, original_sigint_handler)

    if not sys.platform.startswith("win"):
        signal.signal(signal.SIGHUP, original_sighup_handler)
        signal.signal(signal.SIGTERM, original_sigterm_handler)

    return self.exit_code

register_preloop_hook

register_preloop_hook(func)

Register a function to be called at the beginning of the command loop.

Source code in cmd2/cmd2.py
def register_preloop_hook(self, func: Callable[[], None]) -> None:
    """Register a function to be called at the beginning of the command loop."""
    self._validate_prepostloop_callable(func)
    self._preloop_hooks.append(func)

register_postloop_hook

register_postloop_hook(func)

Register a function to be called at the end of the command loop.

Source code in cmd2/cmd2.py
def register_postloop_hook(self, func: Callable[[], None]) -> None:
    """Register a function to be called at the end of the command loop."""
    self._validate_prepostloop_callable(func)
    self._postloop_hooks.append(func)

register_postparsing_hook

register_postparsing_hook(func)

Register a function to be called after parsing user input but before running the command.

Source code in cmd2/cmd2.py
def register_postparsing_hook(self, func: Callable[[plugin.PostparsingData], plugin.PostparsingData]) -> None:
    """Register a function to be called after parsing user input but before running the command."""
    self._validate_postparsing_callable(func)
    self._postparsing_hooks.append(func)

register_precmd_hook

register_precmd_hook(func)

Register a hook to be called before the command function.

Source code in cmd2/cmd2.py
def register_precmd_hook(self, func: Callable[[plugin.PrecommandData], plugin.PrecommandData]) -> None:
    """Register a hook to be called before the command function."""
    self._validate_prepostcmd_hook(func, plugin.PrecommandData)
    self._precmd_hooks.append(func)

register_postcmd_hook

register_postcmd_hook(func)

Register a hook to be called after the command function.

Source code in cmd2/cmd2.py
def register_postcmd_hook(self, func: Callable[[plugin.PostcommandData], plugin.PostcommandData]) -> None:
    """Register a hook to be called after the command function."""
    self._validate_prepostcmd_hook(func, plugin.PostcommandData)
    self._postcmd_hooks.append(func)

register_cmdfinalization_hook

register_cmdfinalization_hook(func)

Register a hook to be called after a command is completed, whether it completes successfully or not.

Source code in cmd2/cmd2.py
def register_cmdfinalization_hook(
    self, func: Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData]
) -> None:
    """Register a hook to be called after a command is completed, whether it completes successfully or not."""
    self._validate_cmdfinalization_callable(func)
    self._cmdfinalization_hooks.append(func)