Skip to content

Initialization

Here is a basic example cmd2 application which demonstrates many capabilities which you may wish to utilize while initializing the app:

examples/getting_started.py

#!/usr/bin/env python3
"""An example cmd2 application demonstrating many common features.

Features demonstrated include all of the following:
 1) Colorizing/stylizing output
 2) Persistent history
 3) How to run an initialization script at startup
 4) How to group and categorize commands when displaying them in help
 5) Opting-in to using the ipy command to run an IPython shell
 6) Allowing access to your application in py and ipy
 7) Displaying an intro banner upon starting your application
 8) Using a custom prompt
 9) How to make custom attributes settable at runtime
10) Shortcuts for commands
11) Persistent bottom toolbar with realtime status updates
12) Right prompt which displays contextual information
13) Background thread to update the content displayed by the bottom toolbar outside of the UI thread to keep things responsive
14) Using preloop() and postloop() hooks to start and stop a background thread
15) Using the with_annotated decorator to parse typed command arguments
16) Using the with_argparser decorator to parse command arguments with a custom parser
"""

import argparse
import datetime
import pathlib
import sys
import threading
from typing import Annotated

from prompt_toolkit.application import get_app
from prompt_toolkit.formatted_text import AnyFormattedText
from rich.style import Style

import cmd2
from cmd2 import (
    Color,
    stylize,
)
from cmd2.annotated import Option


class BasicApp(cmd2.Cmd):
    """Cmd2 application to demonstrate many common features."""

    DEFAULT_CATEGORY = "My Custom Commands"

    def __init__(self) -> None:
        """Initialize the cmd2 application."""
        # Startup script that defines a couple aliases for running shell commands
        alias_script = pathlib.Path(__file__).absolute().parent / ".cmd2rc"

        # Create a shortcut for one of our commands
        shortcuts = cmd2.DEFAULT_SHORTCUTS
        shortcuts.update({"&": "intro"})

        super().__init__(
            auto_suggest=True,
            enable_bottom_toolbar=True,
            enable_rprompt=True,
            include_ipy=True,
            persistent_history_file="cmd2_history.dat",
            refresh_interval=0.5,  # refresh the UI twice a second to keep the bottom toolbar timestamp current
            shortcuts=shortcuts,
            startup_script=str(alias_script),
        )

        # Prints an intro banner once upon application startup
        self.intro = (
            stylize(
                "Welcome to cmd2!",
                style=Style(color=Color.GREEN1, bgcolor=Color.GRAY0, bold=True),
            )
            + " Note the full Unicode support:  😇 💩"
            + " and the persistent bottom bar with realtime status updates!"
        )

        # Show this as the prompt when asking for input
        self.prompt = "myapp> "

        # Allow access to your application in py and ipy via self
        self.self_in_py = True

        # Color to output text in with echo command
        self.foreground_color = Color.CYAN.value

        # Make echo_fg settable at runtime
        fg_colors = [c.value for c in Color]
        self.add_settable(
            cmd2.Settable(
                "foreground_color",
                str,
                "Foreground color to use with echo command",
                self,
                choices=fg_colors,
            )
        )

        # Initialize background thread state for the bottom toolbar
        self._toolbar_state = {"now": ""}
        self._toolbar_lock = threading.Lock()
        self._stop_thread_event = threading.Event()
        self._toolbar_thread: threading.Thread | None = None

    def _update_toolbar_state(self) -> None:
        """Background thread worker to update toolbar state continuously."""
        while not self._stop_thread_event.is_set():
            # Get the current time in ISO format with 0.01s precision
            dt = datetime.datetime.now(datetime.timezone.utc).astimezone()
            now = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-4] + dt.strftime("%z")

            with self._toolbar_lock:
                self._toolbar_state["now"] = now

            # Sleep to yield CPU, polling 4 times a second
            self._stop_thread_event.wait(0.25)

    def preloop(self) -> None:
        """Hook method executed once when the cmdloop() method is called."""
        self._stop_thread_event.clear()
        self._toolbar_thread = threading.Thread(target=self._update_toolbar_state, daemon=True)
        self._toolbar_thread.start()

    def postloop(self) -> None:
        """Hook method executed once when the cmdloop() method is about to return."""
        if self._toolbar_thread and self._toolbar_thread.is_alive():
            self._stop_thread_event.set()
            self._toolbar_thread.join()

    def get_bottom_toolbar(self) -> AnyFormattedText:
        left_text = sys.argv[0]

        with self._toolbar_lock:
            now = self._toolbar_state.get("now", "")

        # Fetch the terminal width to calculate padding for right-alignment.
        # If called outside a running app loop (e.g., in unit tests), get_app()
        # safely returns a dummy app with an 80-column fallback.
        cols = get_app().output.get_size().columns
        padding_size = cols - len(left_text) - len(now)
        if padding_size < 1:
            padding_size = 1
        padding = " " * padding_size

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

    def get_rprompt(self) -> AnyFormattedText:
        current_working_directory = pathlib.Path.cwd()
        style = "bg:ansired fg:ansiwhite"
        text = f"cwd={current_working_directory}"
        return [(style, text)]

    @cmd2.with_annotated
    def do_cat(
        self,
        path: pathlib.Path,  # Required positional argument with type annotation, tab-completes filesystem paths automatically
        numbered: Annotated[  # Optional flag argument with type annotation, default value, and help text
            bool, Option("-n", "--number", help_text="prefix each line with its number")
        ] = False,
    ) -> None:
        """Print a file's contents. `path` tab-completes filesystem paths automatically.

        Try:
            cat <TAB>              # path completes files/dirs -- no completer wired
            cat notes.txt
            cat notes.txt -n       # -n / --number, declared via Option metadata
            cat notes.txt --no-number
        """
        text = path.read_text()
        lines = text.splitlines()
        if numbered:
            numbered_lines = []
            for index, line in enumerate(lines, start=1):
                numbered_lines.append(f"{index}: {line}")
            self.ppaged("\n".join(numbered_lines))
        else:
            # Just print the contents using a pager
            self.ppaged(path.read_text())

    def do_intro(self, _: cmd2.Statement) -> None:
        """Display the intro banner.

        This command uses raw statement parsing. In general, we strongly recommend against this approach. But since this
        command effectively takes no arguments, it is safe to use raw statement parsing here.

        The & key is also used as a shortcut for this command, so you can also type & to display the intro banner.
        """
        self.poutput(self.intro)

    @staticmethod
    def _build_echo_parser() -> cmd2.Cmd2ArgumentParser:
        """Parser factory method for use with the echo command."""
        echo_parser = cmd2.Cmd2ArgumentParser(description="Command that echoes input.")
        echo_parser.add_argument("-u", "--upper", action="store_true", help="uppercase the output")
        echo_parser.add_argument("-r", "--repeat", type=int, default=1, help="output [n] times")
        echo_parser.add_argument("words", nargs="+", help="words to print")
        return echo_parser

    @cmd2.with_argparser(_build_echo_parser)
    def do_echo(self, args: argparse.Namespace) -> None:
        """Command using with_argparser decorator for parsing arguments."""
        output_str = " ".join(args.words)
        if args.upper:
            output_str = output_str.upper()

        for _ in range(args.repeat):
            self.poutput(
                stylize(
                    output_str,
                    style=Style(color=self.foreground_color),
                )
            )


if __name__ == "__main__":
    app = BasicApp()
    sys.exit(app.cmdloop())

Cmd class initializer

A cmd2.Cmd instance or subclass instance is an interactive CLI application framework. There is no good reason to instantiate Cmd itself; rather, it's useful as a superclass of a class you define yourself in order to inherit Cmd's methods and encapsulate action methods.

Certain things must be initialized within the __init__() method of your class derived from cmd2.Cmd(all arguments to __init__() are optional):

cmd2.Cmd.__init__

__init__(
    completekey=None,
    stdin=None,
    stdout=None,
    *,
    allow_cli_args=True,
    allow_clipboard=True,
    allow_redirection=True,
    auto_load_commands=False,
    auto_suggest=True,
    complete_in_thread=True,
    command_sets=None,
    enable_bottom_toolbar=False,
    enable_rprompt=False,
    include_ipy=False,
    include_py=False,
    intro="",
    multiline_commands=None,
    persistent_history_file="",
    persistent_history_length=1000,
    refresh_interval=0.0,
    shortcuts=None,
    silence_startup_script=False,
    startup_script="",
    suggest_similar_command=False,
    terminators=None,
)

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

PARAMETER DESCRIPTION
completekey

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

TYPE: str | None DEFAULT: None

stdin

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

TYPE: TextIO | None DEFAULT: None

stdout

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

TYPE: TextIO | None DEFAULT: None

allow_cli_args

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

TYPE: bool DEFAULT: True

allow_clipboard

If False, cmd2 will disable clipboard interactions

TYPE: bool DEFAULT: True

allow_redirection

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

TYPE: bool DEFAULT: True

auto_load_commands

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

TYPE: bool DEFAULT: False

auto_suggest

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

TYPE: bool DEFAULT: True

complete_in_thread

if True, then completion will run in a separate thread.

TYPE: bool DEFAULT: True

command_sets

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

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

enable_bottom_toolbar

if True, enables a bottom toolbar while at the main prompt. Override get_bottom_toolbar() to define its content.

TYPE: bool DEFAULT: False

enable_rprompt

if True, enables a right prompt while at the main prompt. Override get_rprompt() to define its content.

TYPE: bool DEFAULT: False

include_ipy

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

TYPE: bool DEFAULT: False

include_py

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

TYPE: bool DEFAULT: False

intro

introduction to display at startup

TYPE: RenderableType DEFAULT: ''

multiline_commands

Iterable of commands allowed to accept multi-line input

TYPE: Iterable[str] | None DEFAULT: None

persistent_history_file

file path to load a persistent cmd2 command history from

TYPE: str DEFAULT: ''

persistent_history_length

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

TYPE: int DEFAULT: 1000

refresh_interval

How often, in seconds, to refresh the UI. Defaults to 0.0. prompt-toolkit already refreshes the UI every time a key is pressed. Set this value if you need the UI to update automatically without user input (e.g., for displaying a clock or background status updates in the bottom toolbar).

TYPE: float DEFAULT: 0.0

shortcuts

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

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

silence_startup_script

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

TYPE: bool DEFAULT: False

startup_script

file path to a script to execute at startup

TYPE: str DEFAULT: ''

suggest_similar_command

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

TYPE: bool DEFAULT: False

terminators

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

TYPE: Iterable[str] | None DEFAULT: None

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

    :param completekey: name of a completion key, default to 'tab'. (If None or an empty string, 'tab' is used)
    :param stdin: alternate input file object, if not specified, sys.stdin is used
    :param stdout: alternate output file object, if not specified, sys.stdout is used
    :param allow_cli_args: if ``True``, then [cmd2.Cmd.__init__][] will process command
                           line arguments as either commands to be run. This should be
                           set to ``False`` if your application parses its own command line
                           arguments.
    :param allow_clipboard: If False, cmd2 will disable clipboard interactions
    :param allow_redirection: If ``False``, prevent output redirection and piping to shell
                              commands. This parameter prevents redirection and piping, but
                              does not alter parsing behavior. A user can still type
                              redirection and piping tokens, and they will be parsed as such
                              but they won't do anything.
    :param auto_load_commands: If True, cmd2 will check for all subclasses of `CommandSet`
                               that are currently loaded by Python and automatically
                               instantiate and register all commands. If False, CommandSets
                               must be manually installed with `register_command_set`.
    :param auto_suggest: If True, cmd2 will provide fish shell style auto-suggestions
                        based on history. User can press right-arrow key to accept the
                        provided suggestion.
    :param complete_in_thread: if ``True``, then completion will run in a separate thread.
    :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 enable_bottom_toolbar: if ``True``, enables a bottom toolbar while at the main prompt.
                                  Override ``get_bottom_toolbar()`` to define its content.
    :param enable_rprompt: if ``True``, enables a right prompt while at the main prompt.
                           Override ``get_rprompt()`` to define its content.
    :param include_ipy: should the "ipy" command be included for an embedded IPython shell
    :param include_py: should the "py" command be included for an embedded Python shell
    :param intro: introduction to display at startup
    :param multiline_commands: Iterable of commands allowed to accept multi-line input
    :param persistent_history_file: file path to load a persistent cmd2 command history from
    :param persistent_history_length: max number of history items to write
                                      to the persistent history file
    :param refresh_interval: How often, in seconds, to refresh the UI. Defaults to 0.0.
                             prompt-toolkit already refreshes the UI every time a key is pressed.
                             Set this value if you need the UI to update automatically without
                             user input (e.g., for displaying a clock or background status
                             updates in the bottom toolbar).
    :param shortcuts: Mapping containing shortcuts for commands. If not supplied,
                      then defaults to constants.DEFAULT_SHORTCUTS. If you do not want
                      any shortcuts, pass None and an empty dictionary will be created.
    :param silence_startup_script: if ``True``, then the startup script's output will be
                                   suppressed. Anything written to stderr will still display.
    :param startup_script: file path to a script to execute at startup
    :param suggest_similar_command: if ``True``, then when a command is not found,
                                    [cmd2.Cmd][] will look for similar commands and suggest them.
    :param terminators: Iterable of characters that terminate a command. These are mainly
                        intended for terminating multiline commands, but will also
                        terminate single-line commands. If not supplied, the default
                        is a semicolon. If your app only contains single-line commands
                        and you want terminators to be treated as literals by the parser,
                        then set this to None.
    """
    # Check if py or ipy need to be disabled in this instance
    if not include_py:
        setattr(self, "do_py", None)  # noqa: B010
    if not include_ipy:
        setattr(self, "do_ipy", None)  # noqa: B010

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

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

    if not completekey:
        completekey = self.DEFAULT_COMPLETEKEY

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

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

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

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

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

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

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

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

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

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

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

    self.build_settables()

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

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

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

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

    # Create the main PromptSession
    self.main_session = self._create_main_session(
        auto_suggest=auto_suggest,
        complete_in_thread=complete_in_thread,
        completekey=completekey,
        enable_bottom_toolbar=enable_bottom_toolbar,
        enable_rprompt=enable_rprompt,
        refresh_interval=refresh_interval,
    )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    if auto_load_commands:
        self._autoload_commands()

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

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

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

Cmd class variables

The cmd2.Cmd class provides several class-level variables that can be overridden in subclasses to change default behavior across all instances of that class.

  • DEFAULT_CATEGORY: The default help category for commands defined in the class which haven't been explicitly categorized. (Default: "Cmd2 Commands")
  • DEFAULT_EDITOR: The default editor program used by the edit command.
  • DEFAULT_PROMPT: The default prompt string. (Default: "(Cmd) ")
  • MISC_HEADER: Header for the help section listing miscellaneous help topics. (Default: "Miscellaneous Help Topics")

Cmd instance attributes

The cmd2.Cmd class provides a large number of public instance attributes which allow developers to customize a cmd2 application further beyond the options provided by the __init__() method.

Public instance attributes

Here are instance attributes of cmd2.Cmd which developers might wish to override:

  • broken_pipe_warning: if non-empty, this string will be displayed if a broken pipe error occurs
  • continuation_prompt: used for multiline commands on 2nd+ line of input
  • debug: if True, show full stack trace on error (Default: False)
  • default_error: the error that prints when a non-existent command is run
  • disabled_commands: 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.
  • echo: if True, each command the user issues will be repeated to the screen before it is executed. This is particularly useful when running scripts. This behavior does not occur when running a command at the prompt. (Default: False)
  • editor: text editor program to use with edit command (e.g. vim)
  • exclude_from_history: commands to exclude from the history command
  • exit_code: this determines the value returned by cmdloop() when exiting the application
  • help_error: the error that prints when no help information can be found
  • hidden_commands: commands to exclude from the help menu and tab completion
  • last_result: stores results from the last command run to enable usage of results in a Python script or interactive console. Built-in commands don't make use of this. It is purely there for user-defined commands and convenience.
  • macros: dictionary of macro names and their values
  • max_column_completion_results: The maximum number of completion results to display in a single column (Default: 7)
  • max_completion_table_items: The maximum number of completion results allowed for a completion table to appear (Default: 50)
  • pager: sets the pager command used by the Cmd.ppaged() method for displaying wrapped output using a pager
  • pager_chop: sets the pager command used by the Cmd.ppaged() method for displaying chopped/truncated output using a pager
  • py_bridge_name: name by which embedded Python environments and scripts refer to the cmd2 application by in order to call commands (Default: app)
  • py_locals: dictionary that defines specific variables/functions available in Python shells and scripts (provides more fine-grained control than making everything available with self_in_py)
  • quiet: if True, then completely suppress nonessential output (Default: False)
  • scripts_add_to_history: if True, scripts and pyscripts add commands to history (Default: True)
  • self_in_py: if True, allow access to your application in py command via self (Default: False)
  • settable: dictionary that controls which of these instance attributes are settable at runtime using the set command
  • timing: if True, display execution time for each command (Default: False)