Skip to content

cmd2.Cmd

cmd2.Cmd

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

Bases: Cmd

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.

An easy but powerful framework for writing line-oriented command interpreters. Extends Python's cmd package.

PARAMETER DESCRIPTION
completekey

readline name of a completion key, default to Tab

TYPE: str DEFAULT: 'tab'

stdin

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

TYPE: Optional[TextIO] DEFAULT: None

stdout

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

TYPE: Optional[TextIO] 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

startup_script

file path to a script to execute at startup

TYPE: str DEFAULT: ''

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

include_py

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

TYPE: bool DEFAULT: False

include_ipy

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

TYPE: bool DEFAULT: False

allow_cli_args

if True, then cmd2.Cmd.init will process command line arguments as either commands to be run or, if -t or --test are given, transcript files to run. This should be set to False if your application parses its own command line arguments.

TYPE: bool DEFAULT: True

transcript_files

pass a list of transcript files to be run on initialization. This allows running transcript tests when allow_cli_args is False. If allow_cli_args is True this parameter is ignored.

TYPE: Optional[List[str]] DEFAULT: None

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

multiline_commands

list of commands allowed to accept multi-line input

TYPE: Optional[List[str]] DEFAULT: None

terminators

list 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 an empty list.

TYPE: Optional[List[str]] DEFAULT: None

shortcuts

dictionary containing shortcuts for commands. If not supplied, then defaults to constants.DEFAULT_SHORTCUTS. If you do not want any shortcuts, pass an empty dictionary.

TYPE: Optional[Dict[str, str]] DEFAULT: None

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: Optional[Iterable[CommandSet]] DEFAULT: None

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: True

allow_clipboard

If False, cmd2 will disable clipboard interactions

TYPE: bool DEFAULT: True

suggest_similar_command

If True, cmd2 will attempt to suggest the most similar command when the user types a command that does not exist. Default: False.

TYPE: bool DEFAULT: False

Source code in cmd2/cmd2.py
def __init__(
    self,
    completekey: str = 'tab',
    stdin: Optional[TextIO] = None,
    stdout: Optional[TextIO] = None,
    *,
    persistent_history_file: str = '',
    persistent_history_length: int = 1000,
    startup_script: str = '',
    silence_startup_script: bool = False,
    include_py: bool = False,
    include_ipy: bool = False,
    allow_cli_args: bool = True,
    transcript_files: Optional[List[str]] = None,
    allow_redirection: bool = True,
    multiline_commands: Optional[List[str]] = None,
    terminators: Optional[List[str]] = None,
    shortcuts: Optional[Dict[str, str]] = None,
    command_sets: Optional[Iterable[CommandSet]] = None,
    auto_load_commands: bool = True,
    allow_clipboard: bool = True,
    suggest_similar_command: bool = False,
) -> None:
    """An easy but powerful framework for writing line-oriented command
    interpreters. Extends Python's cmd package.

    :param completekey: readline name of a completion key, default to Tab
    :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 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 startup_script: file path to a script to execute at startup
    :param silence_startup_script: if ``True``, then the startup script's output will be
                                   suppressed. Anything written to stderr will still display.
    :param include_py: should the "py" command be included for an embedded Python shell
    :param include_ipy: should the "ipy" command be included for an embedded IPython shell
    :param allow_cli_args: if ``True``, then [cmd2.Cmd.__init__][] will process command
                           line arguments as either commands to be run or, if ``-t`` or
                           ``--test`` are given, transcript files to run. This should be
                           set to ``False`` if your application parses its own command line
                           arguments.
    :param transcript_files: pass a list of transcript files to be run on initialization.
                             This allows running transcript tests when ``allow_cli_args``
                             is ``False``. If ``allow_cli_args`` is ``True`` this parameter
                             is ignored.
    :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 multiline_commands: list of commands allowed to accept multi-line input
    :param terminators: list 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 an empty list.
    :param shortcuts: dictionary containing shortcuts for commands. If not supplied,
                      then defaults to constants.DEFAULT_SHORTCUTS. If you do not want
                      any shortcuts, pass an empty dictionary.
    :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 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 allow_clipboard: If False, cmd2 will disable clipboard interactions
    :param suggest_similar_command: If ``True``, ``cmd2`` will attempt to suggest the most
                                    similar command when the user types a command that does
                                    not exist. Default: ``False``.
    """
    # Check if py or ipy need to be disabled in this instance
    if not include_py:
        setattr(self, 'do_py', None)
    if not include_ipy:
        setattr(self, 'do_ipy', None)

    # initialize plugin system
    # needs to be done before we call __init__(0)
    self._initialize_plugin_system()

    # Call super class constructor
    super().__init__(completekey=completekey, stdin=stdin, stdout=stdout)

    # Attributes which should NOT be dynamically settable via the set command at runtime
    self.default_to_shell = False  # Attempt to run unrecognized commands as shell commands
    self.allow_redirection = allow_redirection  # Security setting to prevent redirection of stdout

    # Attributes which ARE dynamically settable via the set command at runtime
    self.always_show_hint = False
    self.debug = False
    self.echo = False
    self.editor = Cmd.DEFAULT_EDITOR
    self.feedback_to_output = False  # Do not include nonessentials in >, | output by default (things like timing)
    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

    # The maximum number of CompletionItems to display during tab completion. If the number of completion
    # suggestions exceeds this number, they will be displayed in the typical columnized format and will
    # not include the description value of the CompletionItems.
    self.max_completion_items = 50

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

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

    self.build_settables()

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

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

    # Commands to exclude from the help menu and tab completion
    self.hidden_commands = ['eof', '_relative_run_script']

    # Initialize history
    self.persistent_history_file = ''
    self._persistent_history_length = persistent_history_length
    self._initialize_history(persistent_history_file)

    # 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] = dict()

    # 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] = dict()

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

    self.statement_parser = 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()

    # 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: Optional[utils.ProcReader] = None

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

    # Used to keep track of whether a continuation prompt is being displayed
    self._at_continuation_prompt = False

    # The multiline command currently being typed which is used to tab complete multiline commands.
    self._multiline_in_progress = ''

    # Set the header used for the help function's listing of documented functions
    self.doc_header = "Documented commands (use 'help -v' for verbose/'help <topic>' for details):"

    # 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] = []

    # 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 {utils.quote_string(startup_script)}"
            if silence_startup_script:
                script_cmd += f" {constants.REDIRECTION_OUTPUT} {os.devnull}"
            self._startup_commands.append(script_cmd)

    # Transcript files to run instead of interactive command loop
    self._transcript_files: Optional[List[str]] = None

    # Check for command line args
    if allow_cli_args:
        parser = argparse_custom.DEFAULT_ARGUMENT_PARSER()
        parser.add_argument('-t', '--test', action="store_true", help='Test against transcript(s) in FILE (wildcards OK)')
        callopts, callargs = parser.parse_known_args()

        # If transcript testing was called for, use other arguments as transcript files
        if callopts.test:
            self._transcript_files = callargs
        # If commands were supplied at invocation, then add them to the command queue
        elif callargs:
            self._startup_commands.extend(callargs)
    elif transcript_files:
        self._transcript_files = transcript_files

    # Set the pager(s) for use with the ppaged() method for 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

    # This lock should be acquired before doing any asynchronous changes to the terminal to
    # ensure the updates to the terminal don't interfere with the input being typed or output
    # being printed by a command.
    self.terminal_lock = threading.RLock()

    # Commands that have been disabled from use. This is to support commands that are only available
    # during specific states of the application. This dictionary's keys are the command names and its
    # values are DisabledCommand objects.
    self.disabled_commands: Dict[str, DisabledCommand] = dict()

    # If any command has been categorized, then all other commands that haven't been categorized
    # will display under this section in the help output.
    self.default_category = 'Uncategorized'

    # The default key for sorting string results. Its default value performs a case-insensitive alphabetical sort.
    # If natural sorting is preferred, then set this to NATURAL_SORT_KEY.
    # cmd2 uses this key for sorting:
    #     command and category names
    #     alias, macro, settable, and shortcut names
    #     tab completion results when self.matches_sorted is False
    self.default_sort_key = Cmd.ALPHABETICAL_SORT_KEY

    ############################################################################################################
    # The following variables are used by tab completion functions. They are reset each time complete() is run
    # in _reset_completion_defaults() and it is up to completer functions to set them before returning results.
    ############################################################################################################

    # If True and a single match is returned to complete(), then a space will be appended
    # if the match appears at the end of the line
    self.allow_appended_space = True

    # If True and a single match is returned to complete(), then a closing quote
    # will be added if there is an unmatched opening quote
    self.allow_closing_quote = True

    # An optional hint which prints above tab completion suggestions
    self.completion_hint = ''

    # Normally cmd2 uses readline's formatter to columnize the list of completion suggestions.
    # If a custom format is preferred, write the formatted completions to this string. cmd2 will
    # then print it instead of the readline format. ANSI style sequences and newlines are supported
    # when using this value. Even when using formatted_completions, the full matches must still be returned
    # from your completer function. ArgparseCompleter writes its tab completion tables to this string.
    self.formatted_completions = ''

    # Used by complete() for readline tab completion
    self.completion_matches: List[str] = []

    # Use this list if you need to display tab completion suggestions that are different than the actual text
    # of the matches. For instance, if you are completing strings that contain a common delimiter and you only
    # want to display the final portion of the matches as the tab completion suggestions. The full matches
    # still must be returned from your completer function. For an example, look at path_complete() which
    # uses this to show only the basename of paths as the suggestions. delimiter_complete() also populates
    # this list. These are ignored if self.formatted_completions is populated.
    self.display_matches: List[str] = []

    # Used by functions like path_complete() and delimiter_complete() to properly
    # quote matches that are completed in a delimited fashion
    self.matches_delimited = False

    # Set to True before returning matches to complete() in cases where matches have already been sorted.
    # If False, then complete() will sort the matches using self.default_sort_key before they are displayed.
    # This does not affect self.formatted_completions.
    self.matches_sorted = False

    # Command parsers for this Cmd instance.
    self._command_parsers = _CommandParsers(self)

    # 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: Optional[Statement] = None

prompt class-attribute instance-attribute

prompt = PROMPT

identchars class-attribute instance-attribute

identchars = IDENTCHARS

ruler class-attribute instance-attribute

ruler = '='

lastcmd class-attribute instance-attribute

lastcmd = ''

intro class-attribute instance-attribute

intro = None

doc_leader class-attribute instance-attribute

doc_leader = ''

misc_header class-attribute instance-attribute

misc_header = 'Miscellaneous help topics:'

undoc_header class-attribute instance-attribute

undoc_header = 'Undocumented commands:'

nohelp class-attribute instance-attribute

nohelp = '*** No help on %s'

use_rawinput class-attribute instance-attribute

use_rawinput = 1

stdin instance-attribute

stdin = stdin

stdout instance-attribute

stdout = stdout

cmdqueue instance-attribute

cmdqueue = []

completekey instance-attribute

completekey = completekey

DEFAULT_EDITOR class-attribute instance-attribute

DEFAULT_EDITOR = find_editor()

INTERNAL_COMMAND_EPILOG class-attribute instance-attribute

INTERNAL_COMMAND_EPILOG = 'Notes:\n  This command is for internal use and is not intended to be called from the\n  command line.'

ALPHABETICAL_SORT_KEY class-attribute instance-attribute

ALPHABETICAL_SORT_KEY = norm_fold

NATURAL_SORT_KEY class-attribute instance-attribute

NATURAL_SORT_KEY = natural_keys

default_to_shell instance-attribute

default_to_shell = False

allow_redirection instance-attribute

allow_redirection = allow_redirection

always_show_hint instance-attribute

always_show_hint = False

debug instance-attribute

debug = False

echo instance-attribute

echo = False

editor instance-attribute

editor = DEFAULT_EDITOR

feedback_to_output instance-attribute

feedback_to_output = False

quiet instance-attribute

quiet = False

scripts_add_to_history instance-attribute

scripts_add_to_history = True

timing instance-attribute

timing = False

max_completion_items instance-attribute

max_completion_items = 50

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 = ''

exclude_from_history instance-attribute

exclude_from_history = ['eof', 'history']

macros instance-attribute

macros = dict()

py_bridge_name instance-attribute

py_bridge_name = 'app'

py_locals instance-attribute

py_locals = dict()

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_header instance-attribute

doc_header = "Documented commands (use 'help -v' for verbose/'help <topic>' for details):"

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

terminal_lock instance-attribute

terminal_lock = RLock()

disabled_commands instance-attribute

disabled_commands = dict()

default_category instance-attribute

default_category = 'Uncategorized'

default_sort_key instance-attribute

default_sort_key = ALPHABETICAL_SORT_KEY

allow_appended_space instance-attribute

allow_appended_space = True

allow_closing_quote instance-attribute

allow_closing_quote = True

completion_hint instance-attribute

completion_hint = ''

formatted_completions instance-attribute

formatted_completions = ''

completion_matches instance-attribute

completion_matches = []

display_matches instance-attribute

display_matches = []

matches_delimited instance-attribute

matches_delimited = False

matches_sorted instance-attribute

matches_sorted = False

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

Read-only property needed to support do_set when it reads allow_style

visible_prompt property

visible_prompt

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

Used by transcript testing to make it easier and more reliable when users are doing things like coloring the prompt using ANSI color codes.

RETURNS DESCRIPTION
str

prompt stripped of any ANSI escape codes

aliases property

aliases

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

alias_description class-attribute instance-attribute

alias_description = 'Manage aliases\n\nAn alias is a command that enables replacement of a word by another string.'

alias_epilog class-attribute instance-attribute

alias_epilog = 'See also:\n  macro'

alias_parser class-attribute instance-attribute

alias_parser = DEFAULT_ARGUMENT_PARSER(description=alias_description, epilog=alias_epilog)

alias_create_description class-attribute instance-attribute

alias_create_description = 'Create or overwrite an alias'

alias_create_epilog class-attribute instance-attribute

alias_create_epilog = 'Notes:\n  If you want to use redirection, pipes, or terminators in the value of the\n  alias, then quote them.\n\n  Since aliases are resolved during parsing, tab completion will function as\n  it would for the actual command the alias resolves to.\n\nExamples:\n  alias create ls !ls -lF\n  alias create show_log !cat "log file.txt"\n  alias create save_results print_results ">" out.txt\n'

alias_create_parser class-attribute instance-attribute

alias_create_parser = DEFAULT_ARGUMENT_PARSER(description=alias_create_description, epilog=alias_create_epilog)

alias_delete_help class-attribute instance-attribute

alias_delete_help = 'delete aliases'

alias_delete_description class-attribute instance-attribute

alias_delete_description = 'Delete specified aliases or all aliases if --all is used'

alias_delete_parser class-attribute instance-attribute

alias_delete_parser = DEFAULT_ARGUMENT_PARSER(description=alias_delete_description)

alias_list_help class-attribute instance-attribute

alias_list_help = 'list aliases'

alias_list_description class-attribute instance-attribute

alias_list_description = 'List specified aliases in a reusable form that can be saved to a startup\nscript to preserve aliases across sessions\n\nWithout arguments, all aliases will be listed.'

alias_list_parser class-attribute instance-attribute

alias_list_parser = DEFAULT_ARGUMENT_PARSER(description=alias_list_description)

macro_description class-attribute instance-attribute

macro_description = 'Manage macros\n\nA macro is similar to an alias, but it can contain argument placeholders.'

macro_epilog class-attribute instance-attribute

macro_epilog = 'See also:\n  alias'

macro_parser class-attribute instance-attribute

macro_parser = DEFAULT_ARGUMENT_PARSER(description=macro_description, epilog=macro_epilog)

macro_create_help class-attribute instance-attribute

macro_create_help = 'create or overwrite a macro'

macro_create_description class-attribute instance-attribute

macro_create_description = 'Create or overwrite a macro'

macro_create_epilog class-attribute instance-attribute

macro_create_epilog = 'A macro is similar to an alias, but it can contain argument placeholders.\nArguments are expressed when creating a macro using {#} notation where {1}\nmeans the first argument.\n\nThe following creates a macro called my_macro that expects two arguments:\n\n  macro create my_macro make_dinner --meat {1} --veggie {2}\n\nWhen the macro is called, the provided arguments are resolved and the\nassembled command is run. For example:\n\n  my_macro beef broccoli ---> make_dinner --meat beef --veggie broccoli\n\nNotes:\n  To use the literal string {1} in your command, escape it this way: {{1}}.\n\n  Extra arguments passed to a macro are appended to resolved command.\n\n  An argument number can be repeated in a macro. In the following example the\n  first argument will populate both {1} instances.\n\n    macro create ft file_taxes -p {1} -q {2} -r {1}\n\n  To quote an argument in the resolved command, quote it during creation.\n\n    macro create backup !cp "{1}" "{1}.orig"\n\n  If you want to use redirection, pipes, or terminators in the value of the\n  macro, then quote them.\n\n    macro create show_results print_results -type {1} "|" less\n\n  Because macros do not resolve until after hitting Enter, tab completion\n  will only complete paths while typing a macro.'

macro_create_parser class-attribute instance-attribute

macro_create_parser = DEFAULT_ARGUMENT_PARSER(description=macro_create_description, epilog=macro_create_epilog)

macro_delete_help class-attribute instance-attribute

macro_delete_help = 'delete macros'

macro_delete_description class-attribute instance-attribute

macro_delete_description = 'Delete specified macros or all macros if --all is used'

macro_delete_parser class-attribute instance-attribute

macro_delete_parser = DEFAULT_ARGUMENT_PARSER(description=macro_delete_description)

macro_list_help class-attribute instance-attribute

macro_list_help = 'list macros'

macro_list_description class-attribute instance-attribute

macro_list_description = 'List specified macros in a reusable form that can be saved to a startup script\nto preserve macros across sessions\n\nWithout arguments, all macros will be listed.'

macro_list_parser class-attribute instance-attribute

macro_list_parser = DEFAULT_ARGUMENT_PARSER(description=macro_list_description)

help_parser class-attribute instance-attribute

help_parser = DEFAULT_ARGUMENT_PARSER(description='List available commands or provide detailed help for a specific command')

shortcuts_parser class-attribute instance-attribute

shortcuts_parser = DEFAULT_ARGUMENT_PARSER(description='List available shortcuts')

eof_parser class-attribute instance-attribute

eof_parser = DEFAULT_ARGUMENT_PARSER(description='Called when Ctrl-D is pressed', epilog=INTERNAL_COMMAND_EPILOG)

quit_parser class-attribute instance-attribute

quit_parser = DEFAULT_ARGUMENT_PARSER(description='Exit this application')

set_description class-attribute instance-attribute

set_description = "Set a settable parameter or show current settings of parameters\nCall without arguments for a list of all settable parameters with their values.\nCall with just param to view that parameter's value."

set_parser_parent class-attribute instance-attribute

set_parser_parent = DEFAULT_ARGUMENT_PARSER(description=set_description, add_help=False)

set_parser class-attribute instance-attribute

shell_parser class-attribute instance-attribute

shell_parser = DEFAULT_ARGUMENT_PARSER(description='Execute a command as if at the OS prompt')

py_parser class-attribute instance-attribute

py_parser = DEFAULT_ARGUMENT_PARSER(description='Run an interactive Python shell')

run_pyscript_parser class-attribute instance-attribute

run_pyscript_parser = DEFAULT_ARGUMENT_PARSER(description='Run a Python script file inside the console')

ipython_parser class-attribute instance-attribute

ipython_parser = DEFAULT_ARGUMENT_PARSER(description='Run an interactive IPython shell')

history_description class-attribute instance-attribute

history_description = 'View, run, edit, save, or clear previously entered commands'

history_parser class-attribute instance-attribute

history_parser = DEFAULT_ARGUMENT_PARSER(description=history_description)

history_action_group class-attribute instance-attribute

history_action_group = add_mutually_exclusive_group()

history_format_group class-attribute instance-attribute

history_format_group = add_argument_group(title='formatting')

history_arg_help class-attribute instance-attribute

history_arg_help = 'empty               all history items\na                   one history item by number\na..b, a:b, a:, ..b  items by indices (inclusive)\nstring              items containing string\n/regex/             items matching regular expression'

edit_description class-attribute instance-attribute

edit_description = 'Run a text editor and optionally open a file with it\n\nThe editor used is determined by a settable parameter. To set it:\n\n  set editor (program-name)'

edit_parser class-attribute instance-attribute

edit_parser = DEFAULT_ARGUMENT_PARSER(description=edit_description)

run_script_description class-attribute instance-attribute

run_script_description = 'Run commands in script file that is encoded as either ASCII or UTF-8 text\n\nScript should contain one command per line, just like the command would be\ntyped in the console.\n\nIf the -t/--transcript flag is used, this command instead records\nthe output of the script commands to a transcript for testing purposes.\n'

run_script_parser class-attribute instance-attribute

run_script_parser = DEFAULT_ARGUMENT_PARSER(description=run_script_description)

relative_run_script_description class-attribute instance-attribute

relative_run_script_description = run_script_description

relative_run_script_epilog class-attribute instance-attribute

relative_run_script_epilog = 'Notes:\n  This command is intended to only be used within text file scripts.'

relative_run_script_parser class-attribute instance-attribute

CommandDataType class-attribute instance-attribute

CommandDataType = TypeVar('CommandDataType')

emptyline

emptyline()

Called when an empty line is entered in response to the prompt.

If this method is not overridden, it repeats the last nonempty command entered.

Source code in python3.13/cmd.py
def emptyline(self):
    """Called when an empty line is entered in response to the prompt.

    If this method is not overridden, it repeats the last nonempty
    command entered.

    """
    if self.lastcmd:
        return self.onecmd(self.lastcmd)

completedefault

completedefault(*ignored)

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

By default, it returns an empty list.

Source code in python3.13/cmd.py
def completedefault(self, *ignored):
    """Method called to complete an input line when no command-specific
    complete_*() method is available.

    By default, it returns an empty list.

    """
    return []

completenames

completenames(text, *ignored)
Source code in python3.13/cmd.py
def completenames(self, text, *ignored):
    dotext = 'do_'+text
    return [a[3:] for a in self.get_names() if a.startswith(dotext)]

complete_help

complete_help(*args)
Source code in python3.13/cmd.py
def complete_help(self, *args):
    commands = set(self.completenames(*args))
    topics = set(a[5:] for a in self.get_names()
                 if a.startswith('help_' + args[0]))
    return list(commands | topics)

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]

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]

Matching CommandSets

Source code in cmd2/cmd2.py
def find_commandsets(self, commandset_type: Type[CommandSet], *, subclass_match: bool = False) -> List[CommandSet]:
    """
    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)

Finds the CommandSet that registered the command name

PARAMETER DESCRIPTION
command_name

command name to search

TYPE: str

RETURNS DESCRIPTION
Optional[CommandSet]

CommandSet that provided the command

Source code in cmd2/cmd2.py
def find_commandset_for_command(self, command_name: str) -> Optional[CommandSet]:
    """
    Finds 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

Source code in cmd2/cmd2.py
def register_command_set(self, cmdset: CommandSet) -> 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.keys():
            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.keys():
            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: isinstance(meth, Callable)  # type: ignore[arg-type]
            and hasattr(meth, '__name__')
            and meth.__name__.startswith(COMMAND_FUNC_PREFIX),
        ),
    )

    default_category = getattr(cmdset, CLASS_ATTR_DEFAULT_HELP_CATEGORY, None)

    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 default_category and not hasattr(command_method, constants.CMD_ATTR_HELP_CATEGORY):
                utils.categorize(command_method, default_category)

        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

Source code in cmd2/cmd2.py
def unregister_command_set(self, cmdset: CommandSet) -> 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: 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)

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:
        if settable.name in self.settables.keys() and settable.name not in self._settables.keys():
            raise KeyError(f'Duplicate settable: {settable.name}')
    self._settables[settable.name] = settable

remove_settable

remove_settable(name)

Convenience method for removing 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:
    """
    Convenience method for removing 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:
        raise KeyError(name + " is not a settable parameter")

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) -> List[str]:
        """Used to tab complete allow_style values"""
        return [val.name.lower() for val in ansi.AllowStyle]

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

    self.add_settable(
        Settable(
            'allow_style',
            allow_style_type,
            'Allow ANSI text style sequences in output (valid values: '
            f'{ansi.AllowStyle.ALWAYS}, {ansi.AllowStyle.NEVER}, {ansi.AllowStyle.TERMINAL})',
            self,
            choices_provider=cast(ChoicesProviderFunc, get_allow_style_choices),
        )
    )

    self.add_settable(
        Settable('always_show_hint', bool, 'Display tab completion hint even when completion suggestions print', self)
    )
    self.add_settable(Settable('debug', bool, "Show full traceback on exception", self))
    self.add_settable(Settable('echo', bool, "Echo command issued into output", self))
    self.add_settable(Settable('editor', str, "Program used by 'edit'", self))
    self.add_settable(Settable('feedback_to_output', bool, "Include nonessentials in '|', '>' results", self))
    self.add_settable(
        Settable('max_completion_items', int, "Maximum number of CompletionItems to display during tab completion", 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))

print_to

print_to(dest, msg, *, end='\n', style=None)

Print message to a given file object.

PARAMETER DESCRIPTION
dest

the file object being written to

TYPE: IO[str]

msg

object to print

TYPE: Any

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

style

optional style function to format msg with (e.g. ansi.style_success)

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

Source code in cmd2/cmd2.py
def print_to(
    self,
    dest: IO[str],
    msg: Any,
    *,
    end: str = '\n',
    style: Optional[Callable[[str], str]] = None,
) -> None:
    """
    Print message to a given file object.

    :param dest: the file object being written to
    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    :param style: optional style function to format msg with (e.g. ansi.style_success)
    """
    final_msg = style(msg) if style is not None else msg
    try:
        ansi.style_aware_write(dest, f'{final_msg}{end}')
    except BrokenPipeError:
        # This occurs if a command's output is being piped to another
        # process and that process closes before the command is
        # finished. 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:
            sys.stderr.write(self.broken_pipe_warning)

poutput

poutput(msg='', *, end='\n')

Print message to self.stdout and appends a newline by default

PARAMETER DESCRIPTION
msg

object to print

TYPE: Any DEFAULT: ''

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

Source code in cmd2/cmd2.py
def poutput(self, msg: Any = '', *, end: str = '\n') -> None:
    """Print message to self.stdout and appends a newline by default

    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    """
    self.print_to(self.stdout, msg, end=end)

perror

perror(msg='', *, end='\n', apply_style=True)

Print message to sys.stderr

PARAMETER DESCRIPTION
msg

object to print

TYPE: Any DEFAULT: ''

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

apply_style

If True, then ansi.style_error will be applied to the message text. Set to False in cases where the message text already has the desired style. Defaults to True.

TYPE: bool DEFAULT: True

Source code in cmd2/cmd2.py
def perror(self, msg: Any = '', *, end: str = '\n', apply_style: bool = True) -> None:
    """Print message to sys.stderr

    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    :param apply_style: If True, then ansi.style_error will be applied to the message text. Set to False in cases
                        where the message text already has the desired style. Defaults to True.
    """
    self.print_to(sys.stderr, msg, end=end, style=ansi.style_error if apply_style else None)

psuccess

psuccess(msg='', *, end='\n')

Wraps poutput, but applies ansi.style_success by default

PARAMETER DESCRIPTION
msg

object to print

TYPE: Any DEFAULT: ''

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

Source code in cmd2/cmd2.py
def psuccess(self, msg: Any = '', *, end: str = '\n') -> None:
    """Wraps poutput, but applies ansi.style_success by default

    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    """
    msg = ansi.style_success(msg)
    self.poutput(msg, end=end)

pwarning

pwarning(msg='', *, end='\n')

Wraps perror, but applies ansi.style_warning by default

PARAMETER DESCRIPTION
msg

object to print

TYPE: Any DEFAULT: ''

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

Source code in cmd2/cmd2.py
def pwarning(self, msg: Any = '', *, end: str = '\n') -> None:
    """Wraps perror, but applies ansi.style_warning by default

    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    """
    msg = ansi.style_warning(msg)
    self.perror(msg, end=end, apply_style=False)

pexcept

pexcept(msg, *, end='\n', apply_style=True)

Print Exception message to sys.stderr. If debug is true, print exception traceback if one exists.

PARAMETER DESCRIPTION
msg

message or Exception to print

TYPE: Any

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

apply_style

If True, then ansi.style_error will be applied to the message text. Set to False in cases where the message text already has the desired style. Defaults to True.

TYPE: bool DEFAULT: True

Source code in cmd2/cmd2.py
def pexcept(self, msg: Any, *, end: str = '\n', apply_style: bool = True) -> None:
    """Print Exception message to sys.stderr. If debug is true, print exception traceback if one exists.

    :param msg: message or Exception to print
    :param end: string appended after the end of the message, default a newline
    :param apply_style: If True, then ansi.style_error will be applied to the message text. Set to False in cases
                        where the message text already has the desired style. Defaults to True.
    """
    if self.debug and sys.exc_info() != (None, None, None):
        import traceback

        traceback.print_exc()

    if isinstance(msg, Exception):
        final_msg = f"EXCEPTION of type '{type(msg).__name__}' occurred with message: {msg}"
    else:
        final_msg = str(msg)

    if apply_style:
        final_msg = ansi.style_error(final_msg)

    if not self.debug and 'debug' in self.settables:
        warning = "\nTo enable full traceback, run the following command: 'set debug true'"
        final_msg += ansi.style_warning(warning)

    self.perror(final_msg, end=end, apply_style=False)

pfeedback

pfeedback(msg, *, end='\n')

For printing nonessential feedback. Can be silenced with quiet. Inclusion in redirected output is controlled by feedback_to_output.

PARAMETER DESCRIPTION
msg

object to print

TYPE: Any

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

Source code in cmd2/cmd2.py
def pfeedback(self, msg: Any, *, end: str = '\n') -> None:
    """For printing nonessential feedback.  Can be silenced with `quiet`.
    Inclusion in redirected output is controlled by `feedback_to_output`.

    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    """
    if not self.quiet:
        if self.feedback_to_output:
            self.poutput(msg, end=end)
        else:
            self.perror(msg, end=end, apply_style=False)

ppaged

ppaged(msg, *, end='\n', chop=False)

Print output using a pager if it would go off screen and stdout isn't currently being redirected.

Never uses a pager inside a script (Python or text) or when output is being redirected or piped or when stdout or stdin are not a fully functional terminal.

PARAMETER DESCRIPTION
msg

object to print

TYPE: Any

end

string appended after the end of the message, default a newline

TYPE: str DEFAULT: '\n'

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

Source code in cmd2/cmd2.py
def ppaged(self, msg: Any, *, end: str = '\n', chop: bool = False) -> None:
    """Print output using a pager if it would go off screen and stdout isn't currently being redirected.

    Never uses a pager inside a script (Python or text) or when output is being redirected or piped or when
    stdout or stdin are not a fully functional terminal.

    :param msg: object to print
    :param end: string appended after the end of the message, default a newline
    :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
    """
    # Attempt to detect if we are not running within a fully functional terminal.
    # Don't try to use the pager when being run by a continuous integration system like Jenkins + pexpect.
    functional_terminal = False

    if self.stdin.isatty() and self.stdout.isatty():
        if sys.platform.startswith('win') or os.environ.get('TERM') is not None:
            functional_terminal = True

    # Don't attempt to use a pager that can block if redirecting or running a script (either text or Python).
    # Also only attempt to use a pager if actually running in a real fully functional terminal.
    if functional_terminal and not self._redirecting and not self.in_pyscript() and not self.in_script():
        final_msg = f"{msg}{end}"
        if ansi.allow_style == ansi.AllowStyle.NEVER:
            final_msg = ansi.strip_style(final_msg)

        pager = self.pager
        if chop:
            pager = self.pager_chop

        try:
            # 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(pager, shell=True, stdin=subprocess.PIPE, stdout=self.stdout)
                pipe_proc.communicate(final_msg.encode('utf-8', 'replace'))
        except BrokenPipeError:
            # This occurs if a command's output is being piped to another process and that process closes before the
            # command is finished. 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:
                sys.stderr.write(self.broken_pipe_warning)
    else:
        self.poutput(msg, end=end)

tokens_for_completion

tokens_for_completion(line, begidx, endidx)

Used by tab completion functions to get all tokens through the one being completed.

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 tab 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]]:
    """Used by tab completion functions to get all tokens through the one being completed.

    :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 tab 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
    """
    import copy

    unclosed_quote = ''
    quotes_to_try = copy.copy(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 = [utils.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)

Basic tab completion function that matches against a list of strings without considering line contents or cursor position. The args required by this function are defined in the header of Python's cmd.py.

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 strings being matched against

TYPE: Iterable[str]

RETURNS DESCRIPTION
List[str]

a list of possible tab completions

Source code in cmd2/cmd2.py
def basic_complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
    match_against: Iterable[str],
) -> List[str]:
    """
    Basic tab completion function that matches against a list of strings without considering line contents
    or cursor position. The args required by this function are defined in the header of Python's cmd.py.

    :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 strings being matched against
    :return: a list of possible tab completions
    """
    return [cur_match for cur_match in match_against if cur_match.startswith(text)]

delimiter_complete

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

Performs tab completion against a list but each match is split on a delimiter and only the portion of the match being tab 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 tab 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
List[str]

a list of possible tab completions

Source code in cmd2/cmd2.py
def delimiter_complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
    match_against: Iterable[str],
    delimiter: str,
) -> List[str]:
    """
    Performs tab completion against a list but each match is split on a delimiter and only
    the portion of the match being tab 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 tab 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 list of possible tab completions
    """
    matches = self.basic_complete(text, line, begidx, endidx, match_against)

    # Display only the portion of the match that's being completed based on delimiter
    if matches:
        # Set this to True for proper quoting of matches with spaces
        self.matches_delimited = True

        # Get the common beginning for the matches
        common_prefix = os.path.commonprefix(matches)
        prefix_tokens = common_prefix.split(delimiter)

        # Calculate what portion of the match we are completing
        display_token_index = 0
        if prefix_tokens:
            display_token_index = len(prefix_tokens) - 1

        # Get this portion for each match and store them in self.display_matches
        for cur_match in matches:
            match_tokens = cur_match.split(delimiter)
            display_token = match_tokens[display_token_index]

            if not display_token:
                display_token = delimiter
            self.display_matches.append(display_token)

    return matches

flag_based_complete

flag_based_complete(text, line, begidx, endidx, flag_dict, *, all_else=None)

Tab completes based on a particular flag preceding the token being completed.

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

flag_dict

dictionary whose structure is the following: keys - flags (ex: -c, --create) that result in tab completion for the next argument in the command line values - there are two types of values: 1. iterable list of strings to match against (dictionaries, lists, etc.) 2. function that performs tab completion (ex: path_complete)

TYPE: Dict[str, Union[Iterable[str], CompleterFunc]]

all_else

an optional parameter for tab completing any token that isn't preceded by a flag in flag_dict

TYPE: Union[None, Iterable[str], CompleterFunc] DEFAULT: None

RETURNS DESCRIPTION
List[str]

a list of possible tab completions

Source code in cmd2/cmd2.py
def flag_based_complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
    flag_dict: Dict[str, Union[Iterable[str], CompleterFunc]],
    *,
    all_else: Union[None, Iterable[str], CompleterFunc] = None,
) -> List[str]:
    """Tab completes based on a particular flag preceding the token being completed.

    :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 flag_dict: dictionary whose structure is the following:
                      `keys` - flags (ex: -c, --create) that result in tab completion for the next argument in the
                      command line
                      `values` - there are two types of values:
                      1. iterable list of strings to match against (dictionaries, lists, etc.)
                      2. function that performs tab completion (ex: path_complete)
    :param all_else: an optional parameter for tab completing any token that isn't preceded by a flag in flag_dict
    :return: a list of possible tab completions
    """
    # Get all tokens through the one being completed
    tokens, _ = self.tokens_for_completion(line, begidx, endidx)
    if not tokens:  # pragma: no cover
        return []

    completions_matches = []
    match_against = all_else

    # Must have at least 2 args for a flag to precede the token being completed
    if len(tokens) > 1:
        flag = tokens[-2]
        if flag in flag_dict:
            match_against = flag_dict[flag]

    # Perform tab completion using an Iterable
    if isinstance(match_against, Iterable):
        completions_matches = self.basic_complete(text, line, begidx, endidx, match_against)

    # Perform tab completion using a function
    elif callable(match_against):
        completions_matches = match_against(text, line, begidx, endidx)

    return completions_matches

index_based_complete

index_based_complete(text, line, begidx, endidx, index_dict, *, all_else=None)

Tab completes based on a fixed position in the input 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

index_dict

dictionary whose structure is the following: keys - 0-based token indexes into command line that determine which tokens perform tab completion values - there are two types of values: 1. iterable list of strings to match against (dictionaries, lists, etc.) 2. function that performs tab completion (ex: path_complete)

TYPE: Mapping[int, Union[Iterable[str], CompleterFunc]]

all_else

an optional parameter for tab completing any token that isn't at an index in index_dict

TYPE: Optional[Union[Iterable[str], CompleterFunc]] DEFAULT: None

RETURNS DESCRIPTION
List[str]

a list of possible tab completions

Source code in cmd2/cmd2.py
def index_based_complete(
    self,
    text: str,
    line: str,
    begidx: int,
    endidx: int,
    index_dict: Mapping[int, Union[Iterable[str], CompleterFunc]],
    *,
    all_else: Optional[Union[Iterable[str], CompleterFunc]] = None,
) -> List[str]:
    """Tab completes based on a fixed position in the input 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 index_dict: dictionary whose structure is the following:
                       `keys` - 0-based token indexes into command line that determine which tokens perform tab
                       completion
                       `values` - there are two types of values:
                       1. iterable list of strings to match against (dictionaries, lists, etc.)
                       2. function that performs tab completion (ex: path_complete)
    :param all_else: an optional parameter for tab completing any token that isn't at an index in index_dict
    :return: a list of possible tab completions
    """
    # Get all tokens through the one being completed
    tokens, _ = self.tokens_for_completion(line, begidx, endidx)
    if not tokens:  # pragma: no cover
        return []

    matches = []

    # Get the index of the token being completed
    index = len(tokens) - 1

    # Check if token is at an index in the dictionary
    match_against: Optional[Union[Iterable[str], CompleterFunc]]
    if index in index_dict:
        match_against = index_dict[index]
    else:
        match_against = all_else

    # Perform tab completion using a Iterable
    if isinstance(match_against, Iterable):
        matches = self.basic_complete(text, line, begidx, endidx, match_against)

    # Perform tab completion using a function
    elif callable(match_against):
        matches = match_against(text, line, begidx, endidx)

    return matches

path_complete

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

Performs 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: Optional[Callable[[str], bool]] DEFAULT: None

RETURNS DESCRIPTION
List[str]

a list of possible tab completions

Source code in cmd2/cmd2.py
def path_complete(
    self, text: str, line: str, begidx: int, endidx: int, *, path_filter: Optional[Callable[[str], bool]] = None
) -> List[str]:
    """Performs 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 list of possible tab completions
    """

    # Used to complete ~ and ~user strings
    def complete_users() -> List[str]:
        users = []

        # Windows lacks the pwd module so we can't get a list of users.
        # Instead we will return a result once the user enters text that
        # resolves to an existing home directory.
        if sys.platform.startswith('win'):
            expanded_path = os.path.expanduser(text)
            if os.path.isdir(expanded_path):
                user = text
                if add_trailing_sep_if_dir:
                    user += os.path.sep
                users.append(user)
        else:
            import pwd

            # Iterate through a list of users from the password database
            for cur_pw in pwd.getpwall():
                # Check if the user has an existing home dir
                if os.path.isdir(cur_pw.pw_dir):
                    # Add a ~ to the user to match against text
                    cur_user = '~' + cur_pw.pw_name
                    if cur_user.startswith(text):
                        if add_trailing_sep_if_dir:
                            cur_user += os.path.sep
                        users.append(cur_user)

        if users:
            # We are returning ~user strings that resolve to directories,
            # so don't append a space or quote in the case of a single result.
            self.allow_appended_space = False
            self.allow_closing_quote = False

        return users

    # 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 []

        # 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 complete_users()

            # Otherwise expand the user dir
            else:
                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 matches:
        # Set this to True for proper quoting of paths with spaces
        self.matches_delimited = True

        # Don't append a space or closing quote to directory
        if len(matches) == 1 and os.path.isdir(matches[0]):
            self.allow_appended_space = False
            self.allow_closing_quote = False

        # Sort the matches before any trailing slashes are added
        matches.sort(key=self.default_sort_key)
        self.matches_sorted = True

        # Build display_matches and add a slash to directories
        for index, cur_match in enumerate(matches):
            # Display only the basename of this path in the tab completion suggestions
            self.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
                self.display_matches[index] += os.path.sep

        # Remove cwd if it was added to match the text readline expects
        if cwd_added:
            if cwd == os.path.sep:
                to_replace = cwd
            else:
                to_replace = 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 readline expects
        if expanded_tilde_path:
            matches = [cur_path.replace(expanded_tilde_path, orig_tilde_path, 1) for cur_path in matches]

    return matches

shell_cmd_complete

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

Performs 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
List[str]

a list of possible tab completions

Source code in cmd2/cmd2.py
def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *, complete_blank: bool = False) -> List[str]:
    """Performs 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 list of possible tab completions
    """
    # Don't tab complete anything if no shell command has been started
    if not complete_blank and not text:
        return []

    # 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:
        return utils.get_exes_in_path(text)

    # Otherwise look for executables in the given path
    else:
        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, state, custom_settings=None)

Override of cmd's complete method which returns the next possible completion for 'text'

This completer function is called by readline as complete(text, state), for state in 0, 1, 2, …, until it returns a non-string value. It should return the next possible completion starting with text.

Since readline suppresses any exception raised in completer functions, they can be difficult to debug. Therefore, this function wraps the actual tab completion logic and prints to stderr any exception that occurs before returning control to readline.

PARAMETER DESCRIPTION
text

the current word that user is typing

TYPE: str

state

non-negative integer

TYPE: int

custom_settings

used when not tab completing the main command line

TYPE: Optional[CustomCompletionSettings] DEFAULT: None

RETURNS DESCRIPTION
Optional[str]

the next possible completion for text or None

Source code in cmd2/cmd2.py
def complete(  # type: ignore[override]
    self, text: str, state: int, custom_settings: Optional[utils.CustomCompletionSettings] = None
) -> Optional[str]:
    """Override of cmd's complete method which returns the next possible completion for 'text'

    This completer function is called by readline as complete(text, state), for state in 0, 1, 2, …,
    until it returns a non-string value. It should return the next possible completion starting with text.

    Since readline suppresses any exception raised in completer functions, they can be difficult to debug.
    Therefore, this function wraps the actual tab completion logic and prints to stderr any exception that
    occurs before returning control to readline.

    :param text: the current word that user is typing
    :param state: non-negative integer
    :param custom_settings: used when not tab completing the main command line
    :return: the next possible completion for text or None
    """
    try:
        if state == 0:
            self._reset_completion_defaults()

            # Check if we are completing a multiline command
            if self._at_continuation_prompt:
                # lstrip and prepend the previously typed portion of this multiline command
                lstripped_previous = self._multiline_in_progress.lstrip()
                line = lstripped_previous + readline.get_line_buffer()

                # Increment the indexes to account for the prepended text
                begidx = len(lstripped_previous) + readline.get_begidx()
                endidx = len(lstripped_previous) + readline.get_endidx()
            else:
                # lstrip the original line
                orig_line = readline.get_line_buffer()
                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(readline.get_begidx() - num_stripped, 0)
                endidx = max(readline.get_endidx() - num_stripped, 0)

            # Shortcuts are not word break characters when tab 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_custom.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_for_completion(),
                    )
                    custom_settings = utils.CustomCompletionSettings(parser)

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

            # Check if we need to restore a shortcut in the tab completions
            # so it doesn't get erased from the command line
            if shortcut_to_restore:
                self.completion_matches = [shortcut_to_restore + match for match in self.completion_matches]

            # If we have one result and we are at the end of the line, then add a space if allowed
            if len(self.completion_matches) == 1 and endidx == len(line) and self.allow_appended_space:
                self.completion_matches[0] += ' '

            # Sort matches if they haven't already been sorted
            if not self.matches_sorted:
                self.completion_matches.sort(key=self.default_sort_key)
                self.display_matches.sort(key=self.default_sort_key)
                self.matches_sorted = True

        try:
            return self.completion_matches[state]
        except IndexError:
            return None

    except CompletionError as ex:
        # Don't print error and redraw the prompt unless the error has length
        err_str = str(ex)
        if err_str:
            if ex.apply_style:
                err_str = ansi.style_error(err_str)
            ansi.style_aware_write(sys.stdout, '\n' + err_str + '\n')
            rl_force_redisplay()
        return None
    except Exception as ex:
        # Insert a newline so the exception doesn't print in the middle of the command line being tab completed
        self.perror()
        self.pexcept(ex)
        rl_force_redisplay()
        return None

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

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

_

the current stack frame or None

TYPE: Optional[FrameType]

Source code in cmd2/cmd2.py
def sigint_handler(self, signum: int, _: Optional[FrameType]) -> 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 _: 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: Optional[FrameType]

Source code in cmd2/cmd2.py
def termination_signal_handler(self, signum: int, _: Optional[FrameType]) -> 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)

precmd

precmd(statement)

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

PARAMETER DESCRIPTION
statement

subclass of str which also contains the parsed input

TYPE: Union[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: Union[Statement, str]) -> Statement:
    """Hook method executed just before the command is executed by
    [cmd2.Cmd.onecmd][] and after adding it to history.

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

Hook method executed just after a command is executed by cmd2.Cmd.onecmd.

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: Union[Statement, str]

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

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

Hook method executed once when the cmd2.Cmd.cmdloop method is called.

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

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

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

postloop

postloop()

Hook method executed once when the cmd2.Cmd.cmdloop method is about to return.

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:
    """Hook method executed once when the [cmd2.Cmd.cmdloop][] method is about to return.

    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.
    """
    pass

parseline

parseline(line)

Parse the line into a command name and a string containing the arguments.

NOTE: This is an override of a parent class method. It is only used by other parent class methods.

Different from the parent class method, this ignores self.identchars.

PARAMETER DESCRIPTION
line

line read by readline

TYPE: str

RETURNS DESCRIPTION
Tuple[str, str, str]

tuple containing (command, args, line)

Source code in cmd2/cmd2.py
def parseline(self, line: str) -> Tuple[str, str, str]:
    """Parse the line into a command name and a string containing the arguments.

    NOTE: This is an override of a parent class method.  It is only used by other parent class methods.

    Different from the parent class method, this ignores self.identchars.

    :param line: line read by readline
    :return: tuple containing (command, args, line)
    """
    statement = self.statement_parser.parse_command_only(line)
    return statement.command, statement.args, statement.command_and_args

onecmd_plus_hooks

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

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

orig_rl_history_length

Optional length of the readline history before the current command was typed. This is used to assist in combining multiline readline history entries and is only populated by cmd2. Defaults to None.

TYPE: Optional[int] DEFAULT: None

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,
    orig_rl_history_length: Optional[int] = None,
) -> 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.
    :param orig_rl_history_length: Optional length of the readline history before the current command was typed.
                                   This is used to assist in combining multiline readline history entries and is only
                                   populated by cmd2. Defaults to None.
    :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, orig_rl_history_length=orig_rl_history_length)

        # 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

        redir_saved_state: Optional[utils.RedirectionSavedState] = 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()

            # 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.pfeedback(f'Elapsed: {datetime.datetime.now() - timestart}')
        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 as ex:
        if raise_keyboard_interrupt and not stop:
            raise ex
    except SystemExit as ex:
        if isinstance(ex.code, int):
            self.exit_code = ex.code
        stop = True
    except PassThroughException as ex:
        raise ex.wrapped_ex
    except Exception as ex:
        self.pexcept(ex)
    finally:
        try:
            stop = self._run_cmdfinalization_hooks(stop, statement)
        except KeyboardInterrupt as ex:
            if raise_keyboard_interrupt and not stop:
                raise ex
        except SystemExit as ex:
            if isinstance(ex.code, int):
                self.exit_code = ex.code
            stop = True
        except PassThroughException as ex:
            raise ex.wrapped_ex
        except Exception as ex:
            self.pexcept(ex)

    return stop

runcmds_plus_hooks

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

Used when commands are being run in an automated fashion 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: Union[List[HistoryItem], List[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: Union[List[HistoryItem], List[str]],
    *,
    add_to_history: bool = True,
    stop_on_keyboard_interrupt: bool = False,
) -> bool:
    """
    Used when commands are being run in an automated fashion 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

        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

cmd_func

cmd_func(command)

Get the function for a command

PARAMETER DESCRIPTION
command

the name of the command Example: py helpfunc = self.cmd_func('help') helpfunc now contains a reference to the do_help method

TYPE: str

Source code in cmd2/cmd2.py
def cmd_func(self, command: str) -> Optional[CommandFunc]:
    """
    Get the function for a command

    :param command: the name of the command

    Example:

    ```py
    helpfunc = self.cmd_func('help')
    ```

    helpfunc now contains a reference to the ``do_help`` method
    """
    func_name = constants.COMMAND_FUNC_PREFIX + command
    func = getattr(self, func_name, None)
    return cast(CommandFunc, func) if callable(func) else None

onecmd

onecmd(statement, *, add_to_history=True)

This executes 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: Union[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: Union[Statement, str], *, add_to_history: bool = True) -> bool:
    """This executes 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)

    func = self.cmd_func(statement.command)
    if 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 = func(statement)
        finally:
            self.current_command = None

    else:
        stop = self.default(statement)

    return stop if stop is not None else False

default

default(statement)

Executed 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) -> Optional[bool]:  # type: ignore[override]
    """Executed when the command given isn't a recognized command implemented by a do_* method.

    :param statement: Statement object with parsed input
    """
    if self.default_to_shell:
        if 'shell' not in self.exclude_from_history:
            self.history.append(statement)

        return self.do_shell(statement.command_and_args)
    else:
        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)}"

        # Set apply_style to False so styles for default_error and default_suggestion_message are not overridden
        self.perror(err_msg, apply_style=False)
        return None

read_input

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

Read input from appropriate stdin value. Also supports tab completion and up-arrow history while input is being entered.

PARAMETER DESCRIPTION
prompt

prompt to display to user

TYPE: str

history

optional list of strings to use for up-arrow history. If completion_mode is CompletionMode.COMMANDS and this is None, then cmd2's command list history will be used. 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: Optional[List[str]] DEFAULT: None

completion_mode

tells what type of tab completion to support. Tab completion only works when self.use_rawinput is True and sys.stdin is a terminal. Defaults to CompletionMode.NONE. The following optional settings apply when completion_mode is CompletionMode.CUSTOM:

TYPE: CompletionMode 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 tab completing flag-like tokens (e.g. -o, --option) and you don't want them to be treated as argparse flags when quoted. Set this to True if you plan on passing the string to argparse with the tokens still quoted. A maximum of one of these should be provided:

TYPE: bool DEFAULT: False

choices

iterable of accepted values for single argument

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

choices_provider

function that provides choices for single argument

TYPE: Optional[ChoicesProviderFunc] DEFAULT: None

completer

tab completion function that provides choices for single argument

TYPE: Optional[CompleterFunc] DEFAULT: None

parser

an argument parser which supports the tab completion of multiple arguments

TYPE: Optional[ArgumentParser] DEFAULT: None

RETURNS DESCRIPTION
str

the line read from stdin with all trailing new lines removed

RAISES DESCRIPTION
Exception

any exceptions raised by input() and stdin.readline()

Source code in cmd2/cmd2.py
def read_input(
    self,
    prompt: str,
    *,
    history: Optional[List[str]] = None,
    completion_mode: utils.CompletionMode = utils.CompletionMode.NONE,
    preserve_quotes: bool = False,
    choices: Optional[Iterable[Any]] = None,
    choices_provider: Optional[ChoicesProviderFunc] = None,
    completer: Optional[CompleterFunc] = None,
    parser: Optional[argparse.ArgumentParser] = None,
) -> str:
    """
    Read input from appropriate stdin value. Also supports tab completion and up-arrow history while
    input is being entered.

    :param prompt: prompt to display to user
    :param history: optional list of strings to use for up-arrow history. If completion_mode is
                    CompletionMode.COMMANDS and this is None, then cmd2's command list history will
                    be used. 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 completion_mode: tells what type of tab completion to support. Tab completion only works when
                            self.use_rawinput is True and sys.stdin is a terminal. Defaults to
                            CompletionMode.NONE.

    The following optional settings apply when completion_mode is CompletionMode.CUSTOM:

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

    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: tab completion function that provides choices for single argument
    :param parser: an argument parser which supports the tab completion of multiple arguments

    :return: the line read from stdin with all trailing new lines removed
    :raises Exception: any exceptions raised by input() and stdin.readline()
    """
    readline_configured = False
    saved_completer: Optional[CompleterFunc] = None
    saved_history: Optional[List[str]] = None

    def configure_readline() -> None:
        """Configure readline tab completion and history"""
        nonlocal readline_configured
        nonlocal saved_completer
        nonlocal saved_history
        nonlocal parser

        if readline_configured or rl_type == RlType.NONE:  # pragma: no cover
            return

        # Configure tab completion
        if self._completion_supported():
            saved_completer = readline.get_completer()

            # Disable completion
            if completion_mode == utils.CompletionMode.NONE:

                def complete_none(text: str, state: int) -> Optional[str]:  # pragma: no cover
                    return None

                complete_func = complete_none

            # Complete commands
            elif completion_mode == utils.CompletionMode.COMMANDS:
                complete_func = self.complete

            # Set custom completion settings
            else:
                if parser is None:
                    parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False)
                    parser.add_argument(
                        'arg',
                        suppress_tab_hint=True,
                        choices=choices,  # type: ignore[arg-type]
                        choices_provider=choices_provider,
                        completer=completer,
                    )

                custom_settings = utils.CustomCompletionSettings(parser, preserve_quotes=preserve_quotes)
                complete_func = functools.partial(self.complete, custom_settings=custom_settings)

            readline.set_completer(complete_func)

        # Overwrite history if not completing commands or new history was provided
        if completion_mode != utils.CompletionMode.COMMANDS or history is not None:
            saved_history = []
            for i in range(1, readline.get_current_history_length() + 1):
                saved_history.append(readline.get_history_item(i))

            readline.clear_history()
            if history is not None:
                for item in history:
                    readline.add_history(item)

        readline_configured = True

    def restore_readline() -> None:
        """Restore readline tab completion and history"""
        nonlocal readline_configured
        if not readline_configured or rl_type == RlType.NONE:  # pragma: no cover
            return

        if self._completion_supported():
            readline.set_completer(saved_completer)

        if saved_history is not None:
            readline.clear_history()
            for item in saved_history:
                readline.add_history(item)

        readline_configured = False

    # Check we are reading from sys.stdin
    if self.use_rawinput:
        if sys.stdin.isatty():
            try:
                # Deal with the vagaries of readline and ANSI escape codes
                escaped_prompt = rl_escape_prompt(prompt)

                with self.sigint_protection:
                    configure_readline()
                line = input(escaped_prompt)
            finally:
                with self.sigint_protection:
                    restore_readline()
        else:
            line = input()
            if self.echo:
                sys.stdout.write(f'{prompt}{line}\n')

    # Otherwise read from self.stdin
    else:
        if self.stdin.isatty():
            # on a tty, print the prompt first, then read the line
            self.poutput(prompt, end='')
            self.stdout.flush()
            line = self.stdin.readline()
            if len(line) == 0:
                line = 'eof'
        else:
            # we are reading from a pipe, read the line to see if there is
            # anything there, if so, then decide whether to print the
            # prompt or not
            line = self.stdin.readline()
            if len(line):
                # we read something, output the prompt and the something
                if self.echo:
                    self.poutput(f'{prompt}{line}')
            else:
                line = 'eof'

    return line.rstrip('\r\n')

do_alias

do_alias(args)

Manage aliases

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

do_macro

do_macro(args)

Manage macros

Source code in cmd2/cmd2.py
@with_argparser(macro_parser, preserve_quotes=True)
def do_macro(self, args: argparse.Namespace) -> None:
    """Manage macros"""
    # Call handler for whatever subcommand was selected
    handler = args.cmd2_handler.get()
    handler(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) -> List[str]:
    """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: Dict[str, List[str]]
) -> List[str]:
    """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 []

    # Check if this command uses argparse
    if (func := self.cmd_func(command)) is None or (argparser := self._command_parsers.get(func)) is None:
        return []

    completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, 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(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:
        self._help_menu(args.verbose)

    else:
        # Getting help for a specific command
        func = self.cmd_func(args.command)
        help_func = getattr(self, constants.HELP_FUNC_PREFIX + args.command, None)
        argparser = None if func is None else self._command_parsers.get(func)

        # If the command function uses argparse, then use argparse's help
        if func is not None and argparser is not None:
            completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, self)

            # Set end to blank so the help output matches how it looks when "command -h" is used
            self.poutput(completer.format_help(args.subcommands), end='')

        # If there is a help func delegate to do_help
        elif help_func is not None:
            super().do_help(args.command)

        # If there's no help_func __doc__ then format and output it
        elif func is not None and func.__doc__ is not None:
            self.poutput(pydoc.getdoc(func))

        # If there is no help information then print an error
        else:
            err_msg = self.help_error.format(args.command)

            # Set apply_style to False so help_error's style is not overridden
            self.perror(err_msg, apply_style=False)
            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 handle headers with newlines, ANSI style sequences, and wide characters

PARAMETER DESCRIPTION
header

string to print above commands being printed

TYPE: str

cmds

list of topics to print

TYPE: Optional[List[str]]

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: Optional[List[str]], cmdlen: int, maxcol: int) -> None:
    """
    Print groups of commands and topics in columns and an optional header
    Override of cmd's print_topics() to handle headers with newlines, ANSI style sequences, and wide characters

    :param header: string to print above commands being printed
    :param cmds: list of topics to print
    :param cmdlen: unused, even by cmd's version
    :param maxcol: max number of display columns to fit into
    """
    if cmds:
        self.poutput(header)
        if self.ruler:
            divider = utils.align_left('', fill_char=self.ruler, width=ansi.widest_line(header))
            self.poutput(divider)
        self.columnize(cmds, maxcol - 1)
        self.poutput()

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() to handle strings with ANSI style sequences and wide characters

Each column is only as wide as necessary. Columns are separated by two spaces (one was not legible enough).

Source code in cmd2/cmd2.py
def columnize(self, str_list: Optional[List[str]], display_width: int = 80) -> None:
    """Display a list of single-line strings as a compact set of columns.
    Override of cmd's columnize() to handle strings with ANSI style sequences and wide characters

    Each column is only as wide as necessary.
    Columns are separated by two spaces (one was not legible enough).
    """
    if not str_list:
        self.poutput("<empty>")
        return

    nonstrings = [i for i in range(len(str_list)) if not isinstance(str_list[i], str)]
    if nonstrings:
        raise TypeError(f"str_list[i] not a string for i in {nonstrings}")
    size = len(str_list)
    if size == 1:
        self.poutput(str_list[0])
        return
    # 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, ansi.style_aware_wcswidth(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
        colwidths = [1]
    for row in range(nrows):
        texts = []
        for col in range(ncols):
            i = row + nrows * col
            if i >= size:
                x = ""
            else:
                x = str_list[i]
            texts.append(x)
        while texts and not texts[-1]:
            del texts[-1]
        for col in range(len(texts)):
            texts[col] = utils.align_left(texts[col], width=colwidths[col])
        self.poutput("  ".join(texts))

do_shortcuts

do_shortcuts(_)

List available shortcuts

Source code in cmd2/cmd2.py
@with_argparser(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: self.default_sort_key(x[0]))
    result = "\n".join('{}: {}'.format(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(_)

Called when Ctrl-D is pressed and calls quit with no arguments. This can be overridden if quit should be called differently.

Source code in cmd2/cmd2.py
@with_argparser(eof_parser)
def do_eof(self, _: argparse.Namespace) -> Optional[bool]:
    """
    Called when Ctrl-D is pressed and calls quit with no arguments.
    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(quit_parser)
def do_quit(self, _: argparse.Namespace) -> Optional[bool]:
    """Exit this application"""
    # Return True to stop the command loop
    self.last_result = True
    return True

select

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

Presents a numbered 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: Union[str, List[str], List[Tuple[Any, Optional[str]]]], prompt: str = 'Your choice? ') -> Any:
    """Presents a numbered 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: Union[List[str], List[Tuple[Any, Optional[str]]]]
    if isinstance(opts, str):
        local_opts = cast(List[Tuple[Any, Optional[str]]], list(zip(opts.split(), opts.split())))
    else:
        local_opts = opts
    fulloptions: List[Tuple[Any, Optional[str]]] = []
    for opt in local_opts:
        if isinstance(opt, str):
            fulloptions.append((opt, opt))
        else:
            try:
                fulloptions.append((opt[0], opt[1]))
            except IndexError:
                fulloptions.append((opt[0], opt[0]))
    for idx, (_, text) in enumerate(fulloptions):
        self.poutput('  %2d. %s' % (idx + 1, text))

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

        if not response:
            continue

        try:
            choice = int(response)
            if choice < 1:
                raise IndexError
            return fulloptions[choice - 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: Dict[str, List[str]]
) -> List[str]:
    """Completes the value argument of set"""
    param = arg_tokens['param'][0]
    try:
        settable = self.settables[param]
    except KeyError:
        raise CompletionError(param + " is not a settable parameter")

    # Create a parser with a value field based on this settable
    settable_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(parents=[Cmd.set_parser_parent])

    # Settables with choices list the values of those choices instead of the arg name
    # in help text and this shows in tab 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,  # type: ignore[arg-type]
        choices_provider=settable.choices_provider,
        completer=settable.completer,
    )

    completer = argparse_completer.DEFAULT_AP_COMPLETER(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(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:
                orig_value = settable.get_value()
                settable.set_value(utils.strip_quotes(args.value))
            except Exception as ex:
                self.perror(f"Error setting {args.param}: {ex}")
            else:
                self.poutput(f"{args.param} - was: {orig_value!r}\nnow: {settable.get_value()!r}")
                self.last_result = True
            return

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

    # Define the table structure
    name_label = 'Name'
    max_name_width = max([ansi.style_aware_wcswidth(param) for param in to_show])
    max_name_width = max(max_name_width, ansi.style_aware_wcswidth(name_label))

    cols: List[Column] = [
        Column(name_label, width=max_name_width),
        Column('Value', width=30),
        Column('Description', width=60),
    ]

    table = SimpleTable(cols, divider_char=self.ruler)
    self.poutput(table.generate_header())

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

    for param in sorted(to_show, key=self.default_sort_key):
        settable = self.settables[param]
        row_data = [param, settable.get_value(), settable.description]
        self.poutput(table.generate_data_row(row_data))
        self.last_result[param] = settable.get_value()

do_shell

do_shell(args)

Execute a command as if at the OS prompt

Source code in cmd2/cmd2.py
@with_argparser(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] = dict()

    # 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(  # type: ignore[call-overload]
            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,  # type: ignore[unreachable]
            shell=True,
            **kwargs,
        )

        proc_reader = utils.ProcReader(proc, cast(TextIO, self.stdout), sys.stderr)  # type: ignore[arg-type]
        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
Optional[bool]

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(py_parser)
def do_py(self, _: argparse.Namespace) -> Optional[bool]:
    """
    Run an interactive Python shell
    :return: True if running of commands should stop
    """
    # self.last_resort will be set by _run_python()
    return self._run_python()

do_run_pyscript

do_run_pyscript(args)

Run a Python script file inside the console

RETURNS DESCRIPTION
Optional[bool]

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(run_pyscript_parser)
def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]:
    """
    Run a Python script file inside the console

    :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_resort 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(_)

Enter an interactive IPython shell

RETURNS DESCRIPTION
Optional[bool]

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(ipython_parser)
def do_ipy(self, _: argparse.Namespace) -> Optional[bool]:  # pragma: no cover
    """
    Enter 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 TraitletsLoader  # type: ignore[import]

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

        from IPython.terminal.interactiveshell import (  # type: ignore[import]
            TerminalInteractiveShell,
        )
        from IPython.terminal.ipapp import (  # type: ignore[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 = TraitletsLoader.Config()  # type: ignore
        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
Optional[bool]

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(history_parser)
def do_history(self, args: argparse.Namespace) -> Optional[bool]:
    """
    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:
        if args.clear or args.edit or args.output_file or args.run or args.transcript or args.expanded or args.script:
            self.poutput("-v cannot be used with any other options")
            self.poutput(self.history_parser.format_usage())
            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 or args.transcript):
        self.poutput("-s and -x cannot be used with -c, -r, -e, -o, or -t")
        self.poutput(self.history_parser.format_usage())
        return None

    if args.clear:
        self.last_result = True

        # Clear command and readline history
        self.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

        if rl_type != RlType.NONE:
            readline.clear_history()
        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_resort will be set by do_run_script()
            return self.do_run_script(utils.quote_string(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
    elif args.transcript:
        # self.last_resort will be set by _generate_transcript()
        self._generate_transcript(list(history.values()), args.transcript)
    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(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: Optional[str] DEFAULT: None

RAISES DESCRIPTION
EnvironmentError

if self.editor is not set

Source code in cmd2/cmd2.py
def run_editor(self, file_path: Optional[str] = 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 EnvironmentError: if self.editor is not set
    """
    if not self.editor:
        raise EnvironmentError("Please use 'set editor' to specify your text editing program of choice.")

    command = utils.quote_string(os.path.expanduser(self.editor))
    if file_path:
        command += " " + utils.quote_string(os.path.expanduser(file_path))

    self.do_shell(command)

do_run_script

do_run_script(args)

Run commands in script file that is encoded as either ASCII or UTF-8 text.

RETURNS DESCRIPTION
Optional[bool]

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(run_script_parser)
def do_run_script(self, args: argparse.Namespace) -> Optional[bool]:
    """Run commands in script file that is encoded as either ASCII or UTF-8 text.

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

        if args.transcript:
            # self.last_resort will be set by _generate_transcript()
            self._generate_transcript(
                script_commands,
                os.path.expanduser(args.transcript),
                add_to_history=self.scripts_add_to_history,
            )
        else:
            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()
    return None

do__relative_run_script

do__relative_run_script(args)

Run commands in script file that is encoded as either ASCII or UTF-8 text

RETURNS DESCRIPTION
Optional[bool]

True if running of commands should stop

Source code in cmd2/cmd2.py
@with_argparser(relative_run_script_parser)
def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]:
    """
    Run commands in script file that is encoded as either ASCII or UTF-8 text

    :return: True if running of commands should stop
    """
    file_path = args.file_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 '', file_path)

    # self.last_result will be set by do_run_script()
    return self.do_run_script(utils.quote_string(relative_path))

async_alert

async_alert(alert_msg, new_prompt=None)

Display an important message to the user while they are at a command line prompt. To the user it appears as if an alert message is printed above the prompt and their current input text and cursor location is left alone.

This function needs to acquire self.terminal_lock to ensure a prompt is on screen. Therefore, it is best to acquire the lock before calling this function to avoid raising a RuntimeError.

This function is only needed when you need to print an alert or update the prompt while the main thread is blocking at the prompt. Therefore, this should never be called from the main thread. Doing so will raise a RuntimeError.

PARAMETER DESCRIPTION
alert_msg

the message to display to the user

TYPE: str

new_prompt

If you also want to change the prompt that is displayed, then include it here. See async_update_prompt() docstring for guidance on updating a prompt.

TYPE: Optional[str] DEFAULT: None

RAISES DESCRIPTION
RuntimeError

if called from the main thread.

RuntimeError

if called while another thread holds terminal_lock

Source code in cmd2/cmd2.py
def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None:  # pragma: no cover
    """
    Display an important message to the user while they are at a command line prompt.
    To the user it appears as if an alert message is printed above the prompt and their
    current input text and cursor location is left alone.

    This function needs to acquire self.terminal_lock to ensure a prompt is on screen.
    Therefore, it is best to acquire the lock before calling this function to avoid
    raising a RuntimeError.

    This function is only needed when you need to print an alert or update the prompt while the
    main thread is blocking at the prompt. Therefore, this should never be called from the main
    thread. Doing so will raise a RuntimeError.

    :param alert_msg: the message to display to the user
    :param new_prompt: If you also want to change the prompt that is displayed, then include it here.
                       See async_update_prompt() docstring for guidance on updating a prompt.
    :raises RuntimeError: if called from the main thread.
    :raises RuntimeError: if called while another thread holds `terminal_lock`
    """
    if threading.current_thread() is threading.main_thread():
        raise RuntimeError("async_alert should not be called from the main thread")

    if not (vt100_support and self.use_rawinput):
        return

    # Sanity check that can't fail if self.terminal_lock was acquired before calling this function
    if self.terminal_lock.acquire(blocking=False):
        # Windows terminals tend to flicker when we redraw the prompt and input lines.
        # To reduce how often this occurs, only update terminal if there are changes.
        update_terminal = False

        if alert_msg:
            alert_msg += '\n'
            update_terminal = True

        if new_prompt is not None:
            self.prompt = new_prompt

        # Check if the onscreen prompt needs to be refreshed to match self.prompt.
        if self.need_prompt_refresh():
            update_terminal = True
            rl_set_prompt(self.prompt)

        if update_terminal:
            import shutil

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

            # Print a string which replaces the onscreen prompt and input lines with the alert.
            terminal_str = ansi.async_alert_str(
                terminal_columns=terminal_columns,
                prompt=rl_get_display_prompt(),
                line=readline.get_line_buffer(),
                cursor_offset=rl_get_point(),
                alert_msg=alert_msg,
            )
            if rl_type == RlType.GNU:
                sys.stderr.write(terminal_str)
                sys.stderr.flush()
            elif rl_type == RlType.PYREADLINE:
                readline.rl.mode.console.write(terminal_str)

            # Redraw the prompt and input lines below the alert
            rl_force_redisplay()

        self.terminal_lock.release()

    else:
        raise RuntimeError("another thread holds terminal_lock")

async_update_prompt

async_update_prompt(new_prompt)

Update the command line prompt while the user is still typing at it.

This is good for alerting the user to system changes dynamically in between commands. For instance you could alter the color of the prompt to indicate a system status or increase a counter to report an event. If you do alter the actual text of the prompt, it is best to keep the prompt the same width as what's on screen. Otherwise the user's input text will be shifted and the update will not be seamless.

If user is at a continuation prompt while entering a multiline command, the onscreen prompt will not change. However, self.prompt will still be updated and display immediately after the multiline line command completes.

PARAMETER DESCRIPTION
new_prompt

what to change the prompt to

TYPE: str

RAISES DESCRIPTION
RuntimeError

if called from the main thread.

RuntimeError

if called while another thread holds terminal_lock

Source code in cmd2/cmd2.py
def async_update_prompt(self, new_prompt: str) -> None:  # pragma: no cover
    """
    Update the command line prompt while the user is still typing at it.

    This is good for alerting the user to system changes dynamically in between commands.
    For instance you could alter the color of the prompt to indicate a system status or increase a
    counter to report an event. If you do alter the actual text of the prompt, it is best to keep
    the prompt the same width as what's on screen. Otherwise the user's input text will be shifted
    and the update will not be seamless.

    If user is at a continuation prompt while entering a multiline command, the onscreen prompt will
    not change. However, self.prompt will still be updated and display immediately after the multiline
    line command completes.

    :param new_prompt: what to change the prompt to
    :raises RuntimeError: if called from the main thread.
    :raises RuntimeError: if called while another thread holds `terminal_lock`
    """
    self.async_alert('', new_prompt)

async_refresh_prompt

async_refresh_prompt()

Refresh the oncreen prompt to match self.prompt.

One case where the onscreen prompt and self.prompt can get out of sync is when async_alert() is called while a user is in search mode (e.g. Ctrl-r). To prevent overwriting readline's onscreen search prompt, self.prompt is updated but readline's saved prompt isn't.

Therefore when a user aborts a search, the old prompt is still on screen until they press Enter or this method is called. Call need_prompt_refresh() in an async print thread to know when a refresh is needed.

RAISES DESCRIPTION
RuntimeError

if called from the main thread.

RuntimeError

if called while another thread holds terminal_lock

Source code in cmd2/cmd2.py
def async_refresh_prompt(self) -> None:  # pragma: no cover
    """
    Refresh the oncreen prompt to match self.prompt.

    One case where the onscreen prompt and self.prompt can get out of sync is
    when async_alert() is called while a user is in search mode (e.g. Ctrl-r).
    To prevent overwriting readline's onscreen search prompt, self.prompt is updated
    but readline's saved prompt isn't.

    Therefore when a user aborts a search, the old prompt is still on screen until they
    press Enter or this method is called. Call need_prompt_refresh() in an async print
    thread to know when a refresh is needed.

    :raises RuntimeError: if called from the main thread.
    :raises RuntimeError: if called while another thread holds `terminal_lock`
    """
    self.async_alert('')

need_prompt_refresh

need_prompt_refresh()

Check whether the onscreen prompt needs to be asynchronously refreshed to match self.prompt.

Source code in cmd2/cmd2.py
def need_prompt_refresh(self) -> bool:  # pragma: no cover
    """Check whether the onscreen prompt needs to be asynchronously refreshed to match self.prompt."""
    if not (vt100_support and self.use_rawinput):
        return False

    # Don't overwrite a readline search prompt or a continuation prompt.
    return not rl_in_search_mode() and not self._at_continuation_prompt and self.prompt != rl_get_prompt()

set_window_title staticmethod

set_window_title(title)

Set the terminal window title.

NOTE: This function writes to stderr. Therefore, if you call this during a command run by a pyscript, the string which updates the title will appear in that command's CommandResult.stderr data.

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.

    NOTE: This function writes to stderr. Therefore, if you call this during a command run by a pyscript,
          the string which updates the title will appear in that command's CommandResult.stderr data.

    :param title: the new window title
    """
    if not vt100_support:
        return

    try:
        sys.stderr.write(ansi.set_title(title))
        sys.stderr.flush()
    except AttributeError:
        # Debugging in Pycharm has issues with setting terminal title
        pass

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 commands is already enabled, then return
    if command not in self.disabled_commands:
        return

    cmd_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, cmd_func_name, dc.command_function)

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

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

    # 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
    """
    for cmd_name in list(self.disabled_commands):
        func = self.disabled_commands[cmd_name].command_function
        if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category:
            self.enable_command(cmd_name)

disable_command

disable_command(command, message_to_print)

Disable a command and overwrite its functions

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 overwrite its functions

    :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 commands is already disabled, then return
    if command in self.disabled_commands:
        return

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

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

    # Add the disabled command record
    self.disabled_commands[command] = DisabledCommand(
        command_function=command_function,
        help_function=getattr(self, help_func_name, None),
        completer_function=getattr(self, completer_func_name, None),
    )

    # Overwrite the command and help functions to print the message
    new_func = functools.partial(
        self._report_disabled_command_usage, message_to_print=message_to_print.replace(constants.COMMAND_NAME, command)
    )
    setattr(self, cmd_func_name, new_func)
    setattr(self, help_func_name, new_func)

    # Set the completer to a function that returns a blank list
    setattr(self, completer_func_name, lambda *args, **kwargs: [])

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"
    """
    all_commands = self.get_all_commands()

    for cmd_name in all_commands:
        func = self.cmd_func(cmd_name)
        if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category:
            self.disable_command(cmd_name, message_to_print)

cmdloop

cmdloop(intro=None)

This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2.

_cmdloop() provides the main loop equivalent to cmd.cmdloop(). This is a wrapper around that which deals with the following extra features provided by cmd2: - transcript testing - intro banner - exit code

PARAMETER DESCRIPTION
intro

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

TYPE: Optional[str] DEFAULT: None

Source code in cmd2/cmd2.py
def cmdloop(self, intro: Optional[str] = None) -> int:  # type: ignore[override]
    """This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2.

    _cmdloop() provides the main loop equivalent to cmd.cmdloop().  This is a wrapper around that which deals with
    the following extra features provided by cmd2:
    - transcript testing
    - intro banner
    - exit code

    :param intro: if provided this overrides self.intro and serves as the intro banner printed once at start
    """
    # 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)

    # Grab terminal lock before the command line prompt has been drawn by readline
    self.terminal_lock.acquire()

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

    # If transcript-based regression testing was requested, then do that instead of the main loop
    if self._transcript_files is not None:
        self._run_transcript_tests([os.path.expanduser(tf) for tf in self._transcript_files])
    else:
        # If an intro was supplied in the method call, allow it to override the default
        if intro is not None:
            self.intro = intro

        # Print the intro, if there is one, right after the preloop
        if self.intro is not None:
            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()

    # Release terminal lock now that postloop code should have stopped any terminal updater threads
    # This will also zero the lock count in case cmdloop() is called again
    self.terminal_lock.release()

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