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=False, allow_clipboard=True, suggest_similar_command=False, intro='')

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

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

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

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

PARAMETER | DESCRIPTION
--- | ---
`completekey` | 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: `TextIO | None` DEFAULT: `None`
`stdout` | alternate output file object, if not specified, sys.stdout is used TYPE: `TextIO | 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`
`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: `list[str] | None` 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: `list[str] | None` 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: `list[str] | None` 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: `dict[str, str] | None` 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: `Iterable[CommandSet] | None` 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: `False`
`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`. "param intro: Intro banner to print when starting the application. TYPE: `bool` DEFAULT: `False`

Source code in `cmd2/cmd2.py`

    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684

    def __init__(
        self,
        completekey: str = 'tab',
        stdin: TextIO | None = None,
        stdout: TextIO | None = 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: list[str] | None = None,
        allow_redirection: bool = True,
        multiline_commands: list[str] | None = None,
        terminators: list[str] | None = None,
        shortcuts: dict[str, str] | None = None,
        command_sets: Iterable[CommandSet] | None = None,
        auto_load_commands: bool = False,
        allow_clipboard: bool = True,
        suggest_similar_command: bool = False,
        intro: RenderableType = '',
    ) -> None:
        """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``.
        "param intro: Intro banner to print when starting the application.
        """
        # 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 = Cmd.DEFAULT_PROMPT
        self.intro = intro
        self.use_rawinput = True

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

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

        # Key used for tab completion
        self.completekey = completekey

        # 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: int = 50

        # 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] = 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: 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 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] = {}

        # 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

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

        # Characters used to draw a horizontal rule. Should not be blank.
        self.ruler = "─"

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

        # Set header for table listing documented commands.
        self.doc_header = "Documented Commands"

        # Set header for table listing help topics not related to a command.
        self.misc_header = "Miscellaneous Help Topics"

        # Set header for table listing commands that have no help info.
        self.undoc_header = "Undocumented Commands"

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

        # 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_OUTPUT} {os.devnull}"
                self._startup_commands.append(script_cmd)

        # Transcript files to run instead of interactive command loop
        self._transcript_files: list[str] | None = 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 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

        # 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 disabled during specific application states
        # Key: Command name | Value: DisabledCommand object
        self.disabled_commands: dict[str, DisabledCommand] = {}

        # Categories of commands to be disabled
        # Key: Category name | Value: Message to display
        self.disabled_categories: dict[str, str] = {}

        # 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: Callable[[str], str] = 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: str = ''

        # 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: str = ''

        # 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: bool = False

        # Command parsers for this Cmd instance.
        self._command_parsers: _CommandParsers = _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: Statement | None = None

### `` DEFAULT_EDITOR `class-attribute` `instance-attribute`

    DEFAULT_EDITOR = find_editor()

### `` ALPHABETICAL_SORT_KEY `class-attribute` `instance-attribute`

    ALPHABETICAL_SORT_KEY = norm_fold

### `` NATURAL_SORT_KEY `class-attribute` `instance-attribute`

    NATURAL_SORT_KEY = natural_keys

### `` testfiles `class-attribute`

    testfiles = []

### `` DEFAULT_PROMPT `class-attribute` `instance-attribute`

    DEFAULT_PROMPT = '(Cmd) '

### `` prompt `instance-attribute`

    prompt = DEFAULT_PROMPT

### `` intro `instance-attribute`

    intro = intro

### `` use_rawinput `instance-attribute`

    use_rawinput = True

### `` stdin `instance-attribute`

    stdin = stdin

### `` stdout `instance-attribute`

    stdout = stdout

### `` completekey `instance-attribute`

    completekey = completekey

### `` 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 = {}

### `` py_bridge_name `instance-attribute`

    py_bridge_name = 'app'

### `` py_locals `instance-attribute`

    py_locals = {}

### `` statement_parser `instance-attribute`

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

### `` last_result `instance-attribute`

    last_result = None

### `` sigint_protection `instance-attribute`

    sigint_protection = ContextFlag()

### `` ruler `instance-attribute`

    ruler = '─'

### `` doc_leader `instance-attribute`

    doc_leader = ''

### `` doc_header `instance-attribute`

    doc_header = 'Documented Commands'

### `` misc_header `instance-attribute`

    misc_header = 'Miscellaneous Help Topics'

### `` undoc_header `instance-attribute`

    undoc_header = 'Undocumented Commands'

### `` default_category `instance-attribute`

    default_category = 'Uncategorized Commands'

### `` 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 = {}

### `` disabled_categories `instance-attribute`

    disabled_categories = {}

### `` 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 sequences stripped.

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

RETURNS | DESCRIPTION
--- | ---
`str` | the stripped prompt

### `` aliases `property`

    aliases

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

### `` CommandDataType `class-attribute` `instance-attribute`

    CommandDataType = TypeVar('CommandDataType')

### `` find_commandsets

    find_commandsets(commandset_type, *, subclass_match=False)

Find all CommandSets that match the provided CommandSet type.

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

PARAMETER | DESCRIPTION
--- | ---
`commandset_type` | CommandSet sub-class type to search for TYPE: `type[CommandSet]`
`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`

    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699

    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)

Find the CommandSet that registered the command name.

PARAMETER | DESCRIPTION
--- | ---
`command_name` | command name to search TYPE: `str`

RETURNS | DESCRIPTION
--- | ---
`CommandSet | None` | CommandSet that provided the command

Source code in `cmd2/cmd2.py`

    701
    702
    703
    704
    705
    706
    707

    def find_commandset_for_command(self, command_name: str) -> CommandSet | None:
        """Find the CommandSet that registered the command name.

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

### `` register_command_set

    register_command_set(cmdset)

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

PARAMETER | DESCRIPTION
--- | ---
`cmdset` | CommandSet to load TYPE: `CommandSet`

Source code in `cmd2/cmd2.py`

    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815

    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:
                prefixed_name = f'{cmdset.settable_prefix}.{key}'
                if prefixed_name in all_settables:
                    raise CommandSetRegistrationError(f'Duplicate settable: {key}')

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

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

        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)

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

            self._installed_command_sets.add(cmdset)

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

### `` unregister_command_set

    unregister_command_set(cmdset)

Uninstalls a CommandSet and unloads all associated commands.

PARAMETER | DESCRIPTION
--- | ---
`cmdset` | CommandSet to uninstall TYPE: `CommandSet`

Source code in `cmd2/cmd2.py`

    906
    907
    908
    909
    910
    911
    912
    913
    914
    915
    916
    917
    918
    919
    920
    921
    922
    923
    924
    925
    926
    927
    928
    929
    930
    931
    932
    933
    934
    935
    936
    937
    938
    939
    940
    941
    942
    943
    944
    945
    946
    947
    948
    949

    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: (  # type: ignore[arg-type]
                    isinstance(meth, Callable)  # type: ignore[arg-type]
                    and hasattr(meth, '__name__')
                    and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
                ),
            )

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

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

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

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

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

                delattr(self, cmd_func_name)

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

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

    1160
    1161
    1162
    1163
    1164
    1165
    1166
    1167

    def add_settable(self, settable: Settable) -> None:
        """Add a settable parameter to ``self.settables``.

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

### `` remove_settable

    remove_settable(name)

Remove a settable parameter from `self.settables`.

PARAMETER | DESCRIPTION
--- | ---
`name` | name of the settable being removed TYPE: `str`

RAISES | DESCRIPTION
--- | ---
`KeyError` | if the Settable matches this name

Source code in `cmd2/cmd2.py`

    1169
    1170
    1171
    1172
    1173
    1174
    1175
    1176
    1177
    1178

    def remove_settable(self, name: str) -> None:
        """Remove a settable parameter from ``self.settables``.

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

### `` build_settables

    build_settables()

Create the dictionary of user-settable parameters.

Source code in `cmd2/cmd2.py`

    1180
    1181
    1182
    1183
    1184
    1185
    1186
    1187
    1188
    1189
    1190
    1191
    1192
    1193
    1194
    1195
    1196
    1197
    1198
    1199
    1200
    1201
    1202
    1203
    1204
    1205
    1206
    1207
    1208
    1209
    1210
    1211
    1212
    1213
    1214
    1215
    1216
    1217
    1218
    1219

    def build_settables(self) -> None:
        """Create the dictionary of user-settable parameters."""

        def get_allow_style_choices(_cli_self: Cmd) -> list[str]:
            """Tab complete allow_style values."""
            return [val.name.lower() for val in ru.AllowStyle]

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

        self.add_settable(
            Settable(
                'allow_style',
                allow_style_type,
                'Allow ANSI text style sequences in output (valid values: '
                f'{ru.AllowStyle.ALWAYS}, {ru.AllowStyle.NEVER}, {ru.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 '|' and '>' 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(file, *objects, sep=' ', end='\n', style=None, soft_wrap=True, emoji=False, markup=False, highlight=False, rich_print_kwargs=None, **kwargs)

Print objects to a given file stream.

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

PARAMETER | DESCRIPTION
--- | ---
`file` | file stream being written to TYPE: `IO[str]`
`objects` | objects to print TYPE: `Any` DEFAULT: `()`
`sep` | string to write between printed text. Defaults to " ". TYPE: `str` DEFAULT: `' '`
`end` | string to write at end of printed text. Defaults to a newline. TYPE: `str` DEFAULT: `'\n'`
`style` | optional style to apply to output TYPE: `StyleType | None` DEFAULT: `None`

`soft_wrap`

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

TYPE: `bool` DEFAULT: `True`

`emoji` | If True, Rich will replace emoji codes (e.g., ) with their corresponding Unicode characters. Defaults to False. TYPE: `bool` DEFAULT: `False`
`markup` | If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold]) as styled output. Defaults to False. TYPE: `bool` DEFAULT: `False`
`highlight` | If True, Rich will automatically apply highlighting to elements within strings, such as common Python data types like numbers, booleans, or None. This is particularly useful when pretty printing objects like lists and dictionaries to display them in color. Defaults to False. TYPE: `bool` DEFAULT: `False`
`rich_print_kwargs` | optional additional keyword arguments to pass to Rich's Console.print(). TYPE: `RichPrintKwargs | None` DEFAULT: `None`
`kwargs` | Arbitrary keyword arguments. This allows subclasses to extend the signature of this method and still call `super()` without encountering unexpected keyword argument errors. These arguments are not passed to Rich's Console.print(). See the Rich documentation for more details on emoji codes, markup tags, and highlighting. TYPE: `Any` DEFAULT: `{}`

Source code in `cmd2/cmd2.py`

    1248
    1249
    1250
    1251
    1252
    1253
    1254
    1255
    1256
    1257
    1258
    1259
    1260
    1261
    1262
    1263
    1264
    1265
    1266
    1267
    1268
    1269
    1270
    1271
    1272
    1273
    1274
    1275
    1276
    1277
    1278
    1279
    1280
    1281
    1282
    1283
    1284
    1285
    1286
    1287
    1288
    1289
    1290
    1291
    1292
    1293
    1294
    1295
    1296
    1297
    1298
    1299
    1300
    1301
    1302
    1303
    1304
    1305
    1306
    1307
    1308
    1309
    1310
    1311
    1312
    1313
    1314
    1315
    1316
    1317

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

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

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

        See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
        """
        prepared_objects = ru.prepare_objects_for_rendering(*objects)

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

### `` poutput

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

Print objects to self.stdout.

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

Source code in `cmd2/cmd2.py`

    1319
    1320
    1321
    1322
    1323
    1324
    1325
    1326
    1327
    1328
    1329
    1330
    1331
    1332
    1333
    1334
    1335
    1336
    1337
    1338
    1339
    1340
    1341
    1342
    1343
    1344
    1345
    1346
    1347

    def poutput(
        self,
        *objects: Any,
        sep: str = " ",
        end: str = "\n",
        style: StyleType | None = None,
        soft_wrap: bool = True,
        emoji: bool = False,
        markup: bool = False,
        highlight: bool = False,
        rich_print_kwargs: RichPrintKwargs | None = None,
        **kwargs: Any,  # noqa: ARG002
    ) -> None:
        """Print objects to self.stdout.

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

### `` perror

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

Print objects to sys.stderr.

PARAMETER | DESCRIPTION
--- | ---
`style` | optional style to apply to output. Defaults to Cmd2Style.ERROR. For details on the other parameters, refer to the `print_to` method documentation. TYPE: `StyleType | None` DEFAULT: `ERROR`

Source code in `cmd2/cmd2.py`

    1349
    1350
    1351
    1352
    1353
    1354
    1355
    1356
    1357
    1358
    1359
    1360
    1361
    1362
    1363
    1364
    1365
    1366
    1367
    1368
    1369
    1370
    1371
    1372
    1373
    1374
    1375
    1376
    1377
    1378
    1379

    def perror(
        self,
        *objects: Any,
        sep: str = " ",
        end: str = "\n",
        style: StyleType | None = Cmd2Style.ERROR,
        soft_wrap: bool = True,
        emoji: bool = False,
        markup: bool = False,
        highlight: bool = False,
        rich_print_kwargs: RichPrintKwargs | None = None,
        **kwargs: Any,  # noqa: ARG002
    ) -> None:
        """Print objects to sys.stderr.

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

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

### `` psuccess

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

Wrap poutput, but apply Cmd2Style.SUCCESS.

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

Source code in `cmd2/cmd2.py`

    1381
    1382
    1383
    1384
    1385
    1386
    1387
    1388
    1389
    1390
    1391
    1392
    1393
    1394
    1395
    1396
    1397
    1398
    1399
    1400
    1401
    1402
    1403
    1404
    1405
    1406
    1407

    def psuccess(
        self,
        *objects: Any,
        sep: str = " ",
        end: str = "\n",
        soft_wrap: bool = True,
        emoji: bool = False,
        markup: bool = False,
        highlight: bool = False,
        rich_print_kwargs: RichPrintKwargs | None = None,
        **kwargs: Any,  # noqa: ARG002
    ) -> None:
        """Wrap poutput, but apply Cmd2Style.SUCCESS.

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

### `` pwarning

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

Wrap perror, but apply Cmd2Style.WARNING.

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

Source code in `cmd2/cmd2.py`

    1409
    1410
    1411
    1412
    1413
    1414
    1415
    1416
    1417
    1418
    1419
    1420
    1421
    1422
    1423
    1424
    1425
    1426
    1427
    1428
    1429
    1430
    1431
    1432
    1433
    1434
    1435

    def pwarning(
        self,
        *objects: Any,
        sep: str = " ",
        end: str = "\n",
        soft_wrap: bool = True,
        emoji: bool = False,
        markup: bool = False,
        highlight: bool = False,
        rich_print_kwargs: RichPrintKwargs | None = None,
        **kwargs: Any,  # noqa: ARG002
    ) -> None:
        """Wrap perror, but apply Cmd2Style.WARNING.

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

### `` pexcept

    pexcept(exception, **kwargs)

Print an exception to sys.stderr.

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

PARAMETER | DESCRIPTION
--- | ---
`exception` | the exception to be printed. TYPE: `BaseException`
`kwargs` | Arbitrary keyword arguments. This allows subclasses to extend the signature of this method and still call `super()` without encountering unexpected keyword argument errors. TYPE: `Any` DEFAULT: `{}`

Source code in `cmd2/cmd2.py`

    1437
    1438
    1439
    1440
    1441
    1442
    1443
    1444
    1445
    1446
    1447
    1448
    1449
    1450
    1451
    1452
    1453
    1454
    1455
    1456
    1457
    1458
    1459
    1460
    1461
    1462
    1463
    1464
    1465
    1466
    1467
    1468
    1469
    1470
    1471
    1472
    1473
    1474
    1475
    1476
    1477
    1478
    1479
    1480
    1481
    1482
    1483
    1484
    1485
    1486
    1487
    1488
    1489

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

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

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

        # Only print a traceback if we're in debug mode and one exists.
        if self.debug and sys.exc_info() != (None, None, None):
            traceback = Traceback(
                width=None,  # Use all available width
                code_width=None,  # Use all available width
                show_locals=True,
                max_frames=0,  # 0 means full traceback.
                word_wrap=True,  # Wrap long lines of code instead of truncate
            )
            console.print(traceback)
            console.print()
            return

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

        if exception_str:
            highlighter = ReprHighlighter()

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

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

        console.print(final_msg)
        console.print()

### `` pfeedback

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

Print nonessential feedback.

The output can be silenced with the `quiet` setting and its inclusion in redirected output is controlled by the `feedback_to_output` setting.

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

Source code in `cmd2/cmd2.py`

    1491
    1492
    1493
    1494
    1495
    1496
    1497
    1498
    1499
    1500
    1501
    1502
    1503
    1504
    1505
    1506
    1507
    1508
    1509
    1510
    1511
    1512
    1513
    1514
    1515
    1516
    1517
    1518
    1519
    1520
    1521
    1522
    1523
    1524
    1525
    1526
    1527
    1528
    1529
    1530
    1531
    1532
    1533
    1534
    1535

    def pfeedback(
        self,
        *objects: Any,
        sep: str = " ",
        end: str = "\n",
        style: StyleType | None = None,
        soft_wrap: bool = True,
        emoji: bool = False,
        markup: bool = False,
        highlight: bool = False,
        rich_print_kwargs: RichPrintKwargs | None = None,
        **kwargs: Any,  # noqa: ARG002
    ) -> None:
        """Print nonessential feedback.

        The output can be silenced with the `quiet` setting and its inclusion in redirected output
        is controlled by the `feedback_to_output` setting.

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

### `` ppaged

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

Print output using a pager.

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

PARAMETER | DESCRIPTION
--- | ---

`chop`

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

TYPE: `bool` DEFAULT: `False`

`soft_wrap` | Enable soft wrap mode. If True, lines of text will not be word-wrapped or cropped to fit the terminal width. Defaults to True. Note: If chop is True and a pager is used, soft_wrap is automatically set to True to prevent wrapping and allow for horizontal scrolling. For details on the other parameters, refer to the `print_to` method documentation. TYPE: `bool` DEFAULT: `True`

Source code in `cmd2/cmd2.py`

    1537
    1538
    1539
    1540
    1541
    1542
    1543
    1544
    1545
    1546
    1547
    1548
    1549
    1550
    1551
    1552
    1553
    1554
    1555
    1556
    1557
    1558
    1559
    1560
    1561
    1562
    1563
    1564
    1565
    1566
    1567
    1568
    1569
    1570
    1571
    1572
    1573
    1574
    1575
    1576
    1577
    1578
    1579
    1580
    1581
    1582
    1583
    1584
    1585
    1586
    1587
    1588
    1589
    1590
    1591
    1592
    1593
    1594
    1595
    1596
    1597
    1598
    1599
    1600
    1601
    1602
    1603
    1604
    1605
    1606
    1607
    1608
    1609
    1610
    1611
    1612
    1613
    1614
    1615
    1616
    1617
    1618
    1619
    1620
    1621
    1622
    1623
    1624
    1625
    1626
    1627
    1628
    1629
    1630

    def ppaged(
        self,
        *objects: Any,
        sep: str = " ",
        end: str = "\n",
        style: StyleType | None = None,
        chop: bool = False,
        soft_wrap: bool = True,
        emoji: bool = False,
        markup: bool = False,
        highlight: bool = False,
        rich_print_kwargs: RichPrintKwargs | None = None,
        **kwargs: Any,  # noqa: ARG002
    ) -> None:
        """Print output using a pager.

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

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

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

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

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

        # Check if we are outputting to a pager.
        if functional_terminal and can_block:
            prepared_objects = ru.prepare_objects_for_rendering(*objects)

            # Chopping overrides soft_wrap
            if chop:
                soft_wrap = True

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

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

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

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

### `` tokens_for_completion

    tokens_for_completion(line, begidx, endidx)

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

PARAMETER | DESCRIPTION
--- | ---
`line` | the current input line with leading whitespace removed TYPE: `str`
`begidx` | the beginning index of the prefix text TYPE: `int`
`endidx` | the ending index of the prefix text TYPE: `int`

RETURNS | DESCRIPTION
--- | ---

`tuple[list[str], list[str]]`

A 2 item tuple where the items are On Success - tokens: list of unquoted tokens - this is generally the list needed for 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`

    1653
    1654
    1655
    1656
    1657
    1658
    1659
    1660
    1661
    1662
    1663
    1664
    1665
    1666
    1667
    1668
    1669
    1670
    1671
    1672
    1673
    1674
    1675
    1676
    1677
    1678
    1679
    1680
    1681
    1682
    1683
    1684
    1685
    1686
    1687
    1688
    1689
    1690
    1691
    1692
    1693
    1694
    1695
    1696
    1697
    1698
    1699
    1700
    1701
    1702
    1703
    1704
    1705
    1706
    1707
    1708
    1709
    1710
    1711
    1712
    1713
    1714
    1715

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

        :param line: the current input line with leading whitespace removed
        :param begidx: the beginning index of the prefix text
        :param endidx: the ending index of the prefix text
        :return: A 2 item tuple where the items are
                 **On Success**
                 - tokens: list of unquoted tokens - this is generally the list needed for 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 = [su.strip_quotes(cur_token) for cur_token in raw_tokens]

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

        return tokens, raw_tokens

### `` basic_complete

    basic_complete(text, line, begidx, endidx, match_against)

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`

    1717
    1718
    1719
    1720
    1721
    1722
    1723
    1724
    1725
    1726
    1727
    1728
    1729
    1730
    1731
    1732
    1733
    1734
    1735
    1736

    def basic_complete(
        self,
        text: str,
        line: str,  # noqa: ARG002
        begidx: int,  # noqa: ARG002
        endidx: int,  # noqa: ARG002
        match_against: Iterable[str],
    ) -> list[str]:
        """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)

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

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`

    1738
    1739
    1740
    1741
    1742
    1743
    1744
    1745
    1746
    1747
    1748
    1749
    1750
    1751
    1752
    1753
    1754
    1755
    1756
    1757
    1758
    1759
    1760
    1761
    1762
    1763
    1764
    1765
    1766
    1767
    1768
    1769
    1770
    1771
    1772
    1773
    1774
    1775
    1776
    1777
    1778
    1779
    1780
    1781
    1782
    1783
    1784
    1785
    1786
    1787
    1788
    1789
    1790
    1791
    1792
    1793
    1794
    1795
    1796
    1797
    1798
    1799
    1800
    1801
    1802
    1803
    1804
    1805
    1806
    1807
    1808
    1809
    1810
    1811
    1812
    1813
    1814
    1815
    1816
    1817
    1818

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

        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)
        if not matches:
            return []

        # 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

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

        for cur_match in matches:
            match_tokens = cur_match.split(delimiter)

            filtered_match = delimiter.join(match_tokens[: display_token_index + 1])
            display_match = match_tokens[display_token_index]

            # If there are more tokens, then we aren't done completing a full item
            if len(match_tokens) > display_token_index + 1:
                filtered_match += delimiter
                display_match += delimiter
                self.allow_appended_space = False
                self.allow_closing_quote = False

            if filtered_match not in unique_results:
                unique_results[filtered_match] = display_match

        filtered_matches = list(unique_results.keys())
        self.display_matches = list(unique_results.values())

        return filtered_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, Iterable[str] | CompleterFunc]`
`all_else` | an optional parameter for tab completing any token that isn't preceded by a flag in flag_dict TYPE: `None | Iterable[str] | CompleterFunc` DEFAULT: `None`

RETURNS | DESCRIPTION
--- | ---
`list[str]` | a list of possible tab completions

Source code in `cmd2/cmd2.py`

    1820
    1821
    1822
    1823
    1824
    1825
    1826
    1827
    1828
    1829
    1830
    1831
    1832
    1833
    1834
    1835
    1836
    1837
    1838
    1839
    1840
    1841
    1842
    1843
    1844
    1845
    1846
    1847
    1848
    1849
    1850
    1851
    1852
    1853
    1854
    1855
    1856
    1857
    1858
    1859
    1860
    1861
    1862
    1863
    1864
    1865
    1866
    1867

    def flag_based_complete(
        self,
        text: str,
        line: str,
        begidx: int,
        endidx: int,
        flag_dict: dict[str, Iterable[str] | CompleterFunc],
        *,
        all_else: 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, Iterable[str] | CompleterFunc]`
`all_else` | an optional parameter for tab completing any token that isn't at an index in index_dict TYPE: `Iterable[str] | CompleterFunc | None` DEFAULT: `None`

RETURNS | DESCRIPTION
--- | ---
`list[str]` | a list of possible tab completions

Source code in `cmd2/cmd2.py`

    1869
    1870
    1871
    1872
    1873
    1874
    1875
    1876
    1877
    1878
    1879
    1880
    1881
    1882
    1883
    1884
    1885
    1886
    1887
    1888
    1889
    1890
    1891
    1892
    1893
    1894
    1895
    1896
    1897
    1898
    1899
    1900
    1901
    1902
    1903
    1904
    1905
    1906
    1907
    1908
    1909
    1910
    1911
    1912
    1913
    1914
    1915
    1916

    def index_based_complete(
        self,
        text: str,
        line: str,
        begidx: int,
        endidx: int,
        index_dict: Mapping[int, Iterable[str] | CompleterFunc],
        *,
        all_else: Iterable[str] | CompleterFunc | None = 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: Iterable[str] | CompleterFunc | None
        match_against = index_dict.get(index, 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)

Perform completion of local file system paths.

PARAMETER | DESCRIPTION
--- | ---
`text` | the string prefix we are attempting to match (all matches must begin with it) TYPE: `str`
`line` | the current input line with leading whitespace removed TYPE: `str`
`begidx` | the beginning index of the prefix text TYPE: `int`
`endidx` | the ending index of the prefix text TYPE: `int`
`path_filter` | optional filter function that determines if a path belongs in the results this function takes a path as its argument and returns True if the path should be kept in the results TYPE: `Callable[[str], bool] | None` DEFAULT: `None`

RETURNS | DESCRIPTION
--- | ---
`list[str]` | a list of possible tab completions

Source code in `cmd2/cmd2.py`

    1918
    1919
    1920
    1921
    1922
    1923
    1924
    1925
    1926
    1927
    1928
    1929
    1930
    1931
    1932
    1933
    1934
    1935
    1936
    1937
    1938
    1939
    1940
    1941
    1942
    1943
    1944
    1945
    1946
    1947
    1948
    1949
    1950
    1951
    1952
    1953
    1954
    1955
    1956
    1957
    1958
    1959
    1960
    1961
    1962
    1963
    1964
    1965
    1966
    1967
    1968
    1969
    1970
    1971
    1972
    1973
    1974
    1975
    1976
    1977
    1978
    1979
    1980
    1981
    1982
    1983
    1984
    1985
    1986
    1987
    1988
    1989
    1990
    1991
    1992
    1993
    1994
    1995
    1996
    1997
    1998
    1999
    2000
    2001
    2002
    2003
    2004
    2005
    2006
    2007
    2008
    2009
    2010
    2011
    2012
    2013
    2014
    2015
    2016
    2017
    2018
    2019
    2020
    2021
    2022
    2023
    2024
    2025
    2026
    2027
    2028
    2029
    2030
    2031
    2032
    2033
    2034
    2035
    2036
    2037
    2038
    2039
    2040
    2041
    2042
    2043
    2044
    2045
    2046
    2047
    2048
    2049
    2050
    2051
    2052
    2053
    2054
    2055
    2056
    2057
    2058
    2059
    2060
    2061

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

        :param text: the string prefix we are attempting to match (all matches must begin with it)
        :param line: the current input line with leading whitespace removed
        :param begidx: the beginning index of the prefix text
        :param endidx: the ending index of the prefix text
        :param path_filter: optional filter function that determines if a path belongs in the results
                            this function takes a path as its argument and returns True if the path should
                            be kept in the results
        :return: a 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
                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:
                to_replace = cwd if cwd == os.path.sep else cwd + os.path.sep
                matches = [cur_path.replace(to_replace, '', 1) for cur_path in matches]

            # Restore the tilde string if we expanded one to match the text 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)

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

PARAMETER | DESCRIPTION
--- | ---
`text` | the string prefix we are attempting to match (all matches must begin with it) TYPE: `str`
`line` | the current input line with leading whitespace removed TYPE: `str`
`begidx` | the beginning index of the prefix text TYPE: `int`
`endidx` | the ending index of the prefix text TYPE: `int`
`complete_blank` | If True, then a blank will complete all shell commands in a user's path. If False, then no completion is performed. Defaults to False to match Bash shell behavior. TYPE: `bool` DEFAULT: `False`

RETURNS | DESCRIPTION
--- | ---
`list[str]` | a list of possible tab completions

Source code in `cmd2/cmd2.py`

    2063
    2064
    2065
    2066
    2067
    2068
    2069
    2070
    2071
    2072
    2073
    2074
    2075
    2076
    2077
    2078
    2079
    2080
    2081
    2082
    2083
    2084
    2085

    def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *, complete_blank: bool = False) -> list[str]:
        """Perform completion of executables either in a user's path or a given path.

        :param text: the string prefix we are attempting to match (all matches must begin with it)
        :param line: the current input line with leading whitespace removed
        :param begidx: the beginning index of the prefix text
        :param endidx: the ending index of the prefix text
        :param complete_blank: If True, then a blank will complete all shell commands in a user's path. If False, then
                               no completion is performed. Defaults to False to match Bash shell behavior.
        :return: a 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
        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: `CustomCompletionSettings | None` DEFAULT: `None`

RETURNS | DESCRIPTION
--- | ---
`str | None` | the next possible completion for text or None

Source code in `cmd2/cmd2.py`

    2477
    2478
    2479
    2480
    2481
    2482
    2483
    2484
    2485
    2486
    2487
    2488
    2489
    2490
    2491
    2492
    2493
    2494
    2495
    2496
    2497
    2498
    2499
    2500
    2501
    2502
    2503
    2504
    2505
    2506
    2507
    2508
    2509
    2510
    2511
    2512
    2513
    2514
    2515
    2516
    2517
    2518
    2519
    2520
    2521
    2522
    2523
    2524
    2525
    2526
    2527
    2528
    2529
    2530
    2531
    2532
    2533
    2534
    2535
    2536
    2537
    2538
    2539
    2540
    2541
    2542
    2543
    2544
    2545
    2546
    2547
    2548
    2549
    2550
    2551
    2552
    2553
    2554
    2555
    2556
    2557
    2558
    2559
    2560
    2561
    2562
    2563
    2564
    2565
    2566
    2567
    2568
    2569
    2570
    2571
    2572
    2573
    2574
    2575
    2576
    2577
    2578
    2579
    2580
    2581

    def complete(self, text: str, state: int, custom_settings: utils.CustomCompletionSettings | None = None) -> str | 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.

        :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:
                self.print_to(
                    sys.stdout,
                    Text.assemble(
                        "\n",
                        (err_str, Cmd2Style.ERROR if ex.apply_style else ""),
                    ),
                )
                rl_force_redisplay()
            return None
        except Exception as ex:  # noqa: BLE001
            # 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`

    2583
    2584
    2585

    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`

    2587
    2588
    2589

    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`

    2596
    2597
    2598

    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`

    2600
    2601
    2602
    2603
    2604
    2605
    2606

    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`

    2608
    2609
    2610
    2611
    2612
    2613
    2614

    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`

    2656
    2657
    2658
    2659
    2660
    2661
    2662
    2663
    2664
    2665

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

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

### `` sigint_handler

    sigint_handler(signum, frame)

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

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

PARAMETER | DESCRIPTION
--- | ---
`signum` | signal number TYPE: `int`
`frame` | the current stack frame or None TYPE: `FrameType | None`

Source code in `cmd2/cmd2.py`

    2667
    2668
    2669
    2670
    2671
    2672
    2673
    2674
    2675
    2676
    2677
    2678
    2679
    2680
    2681
    2682
    2683
    2684
    2685
    2686
    2687
    2688
    2689
    2690
    2691

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

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

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

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

### `` termination_signal_handler

    termination_signal_handler(signum, _)

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

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

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

PARAMETER | DESCRIPTION
--- | ---
`signum` | signal number TYPE: `int`
`_` | the current stack frame or None TYPE: `FrameType | None`

Source code in `cmd2/cmd2.py`

    2693
    2694
    2695
    2696
    2697
    2698
    2699
    2700
    2701
    2702
    2703
    2704
    2705
    2706
    2707

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

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

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

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

### `` precmd

    precmd(statement)

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

PARAMETER | DESCRIPTION
--- | ---
`statement` | subclass of str which also contains the parsed input TYPE: `Statement | str`

RETURNS | DESCRIPTION
--- | ---
`Statement` | a potentially modified version of the input Statement object See cmd2.Cmd.register_postparsing_hook and cmd2.Cmd.register_precmd_hook for more robust ways to run hooks before the command is executed. See Hooks for more information.

Source code in `cmd2/cmd2.py`

    2713
    2714
    2715
    2716
    2717
    2718
    2719
    2720
    2721
    2722

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

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

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

### `` postcmd

    postcmd(stop, statement)

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

PARAMETER | DESCRIPTION
--- | ---
`stop` | return `True` to request the command loop terminate TYPE: `bool`
`statement` | subclass of str which also contains the parsed input See cmd2.Cmd.register_postcmd_hook and cmd2.Cmd.register_cmdfinalization_hook for more robust ways to run hooks after the command is executed. See Hooks for more information. TYPE: `Statement | str`

Source code in `cmd2/cmd2.py`

    2724
    2725
    2726
    2727
    2728
    2729
    2730
    2731
    2732
    2733

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

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

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

### `` preloop

    preloop()

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

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

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

Source code in `cmd2/cmd2.py`

    2735
    2736
    2737
    2738
    2739
    2740
    2741
    2742

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

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

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

### `` postloop

    postloop()

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

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

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

Source code in `cmd2/cmd2.py`

    2744
    2745
    2746
    2747
    2748
    2749
    2750
    2751

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

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

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

### `` parseline

    parseline(line)

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

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`

    2753
    2754
    2755
    2756
    2757
    2758
    2759
    2760

    def parseline(self, line: str) -> tuple[str, str, str]:
        """Parse the line into a command name and a string containing the arguments.

        :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: `int | None` DEFAULT: `None`

RETURNS | DESCRIPTION
--- | ---
`bool` | True if running of commands should stop

Source code in `cmd2/cmd2.py`

    2762
    2763
    2764
    2765
    2766
    2767
    2768
    2769
    2770
    2771
    2772
    2773
    2774
    2775
    2776
    2777
    2778
    2779
    2780
    2781
    2782
    2783
    2784
    2785
    2786
    2787
    2788
    2789
    2790
    2791
    2792
    2793
    2794
    2795
    2796
    2797
    2798
    2799
    2800
    2801
    2802
    2803
    2804
    2805
    2806
    2807
    2808
    2809
    2810
    2811
    2812
    2813
    2814
    2815
    2816
    2817
    2818
    2819
    2820
    2821
    2822
    2823
    2824
    2825
    2826
    2827
    2828
    2829
    2830
    2831
    2832
    2833
    2834
    2835
    2836
    2837
    2838
    2839
    2840
    2841
    2842
    2843
    2844
    2845
    2846
    2847
    2848
    2849
    2850
    2851
    2852
    2853
    2854
    2855
    2856
    2857
    2858
    2859
    2860
    2861
    2862
    2863
    2864
    2865
    2866
    2867
    2868
    2869
    2870
    2871
    2872
    2873
    2874
    2875
    2876
    2877
    2878
    2879
    2880
    2881
    2882
    2883
    2884
    2885
    2886
    2887
    2888
    2889
    2890

    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: int | None = 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  # noqa: TRY301

            redir_saved_state: utils.RedirectionSavedState | None = None

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

                    redir_saved_state = self._redirect_output(statement)

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

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

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

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

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

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

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

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

        return stop

### `` runcmds_plus_hooks

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

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

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

PARAMETER | DESCRIPTION
--- | ---
`cmds` | commands to run TYPE: `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`

    2911
    2912
    2913
    2914
    2915
    2916
    2917
    2918
    2919
    2920
    2921
    2922
    2923
    2924
    2925
    2926
    2927
    2928
    2929
    2930
    2931
    2932
    2933
    2934
    2935
    2936
    2937
    2938
    2939
    2940
    2941
    2942
    2943
    2944
    2945
    2946

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

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

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

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

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

        return False

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

    3283
    3284
    3285
    3286
    3287
    3288
    3289
    3290
    3291
    3292
    3293
    3294
    3295
    3296
    3297
    3298

    def cmd_func(self, command: str) -> CommandFunc | None:
        """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)

Execute the actual do_* method for a command.

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

PARAMETER | DESCRIPTION
--- | ---
`statement` | intended to be a Statement instance parsed command from the input stream, alternative acceptance of a str is present only for backward compatibility with cmd TYPE: `Statement | str`
`add_to_history` | If True, then add this command to history. Defaults to True. TYPE: `bool` DEFAULT: `True`

RETURNS | DESCRIPTION
--- | ---
`bool` | a flag indicating whether the interpretation of commands should stop

Source code in `cmd2/cmd2.py`

    3300
    3301
    3302
    3303
    3304
    3305
    3306
    3307
    3308
    3309
    3310
    3311
    3312
    3313
    3314
    3315
    3316
    3317
    3318
    3319
    3320
    3321
    3322
    3323
    3324
    3325
    3326
    3327
    3328
    3329
    3330
    3331
    3332
    3333

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

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

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

        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)

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

PARAMETER | DESCRIPTION
--- | ---
`statement` | Statement object with parsed input TYPE: `Statement`

Source code in `cmd2/cmd2.py`

    3335
    3336
    3337
    3338
    3339
    3340
    3341
    3342
    3343
    3344
    3345
    3346
    3347
    3348
    3349
    3350

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

        :param statement: Statement object with parsed input
        """
        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)

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

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

### `` completedefault

    completedefault(*_ignored)

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

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

By default, it returns an empty list.

Source code in `cmd2/cmd2.py`

    3352
    3353
    3354
    3355
    3356
    3357
    3358
    3359

    def completedefault(self, *_ignored: list[str]) -> list[str]:
        """Call to complete an input line when no command-specific complete_*() method is available.

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

        By default, it returns an empty list.
        """
        return []

### `` 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: `list[str] | None` 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: `Iterable[Any] | None` DEFAULT: `None`
`choices_provider` | function that provides choices for single argument TYPE: `ChoicesProviderFunc | None` DEFAULT: `None`
`completer` | tab completion function that provides choices for single argument TYPE: `CompleterFunc | None` DEFAULT: `None`
`parser` | an argument parser which supports the tab completion of multiple arguments TYPE: `ArgumentParser | None` 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`

    3364
    3365
    3366
    3367
    3368
    3369
    3370
    3371
    3372
    3373
    3374
    3375
    3376
    3377
    3378
    3379
    3380
    3381
    3382
    3383
    3384
    3385
    3386
    3387
    3388
    3389
    3390
    3391
    3392
    3393
    3394
    3395
    3396
    3397
    3398
    3399
    3400
    3401
    3402
    3403
    3404
    3405
    3406
    3407
    3408
    3409
    3410
    3411
    3412
    3413
    3414
    3415
    3416
    3417
    3418
    3419
    3420
    3421
    3422
    3423
    3424
    3425
    3426
    3427
    3428
    3429
    3430
    3431
    3432
    3433
    3434
    3435
    3436
    3437
    3438
    3439
    3440
    3441
    3442
    3443
    3444
    3445
    3446
    3447
    3448
    3449
    3450
    3451
    3452
    3453
    3454
    3455
    3456
    3457
    3458
    3459
    3460
    3461
    3462
    3463
    3464
    3465
    3466
    3467
    3468
    3469
    3470
    3471
    3472
    3473
    3474
    3475
    3476
    3477
    3478
    3479
    3480
    3481
    3482
    3483
    3484
    3485
    3486
    3487
    3488
    3489
    3490
    3491
    3492
    3493
    3494
    3495
    3496
    3497
    3498
    3499
    3500
    3501
    3502
    3503
    3504
    3505
    3506
    3507
    3508
    3509
    3510
    3511
    3512
    3513
    3514
    3515
    3516
    3517
    3518
    3519
    3520
    3521

    def read_input(
        self,
        prompt: str,
        *,
        history: list[str] | None = None,
        completion_mode: utils.CompletionMode = utils.CompletionMode.NONE,
        preserve_quotes: bool = False,
        choices: Iterable[Any] | None = None,
        choices_provider: ChoicesProviderFunc | None = None,
        completer: CompleterFunc | None = None,
        parser: argparse.ArgumentParser | None = 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: CompleterFunc | None = None
        saved_history: list[str] | None = 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) -> str | None:  # pragma: no cover  # noqa: ARG001
                        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,
                            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
        elif 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`

    3662
    3663
    3664
    3665
    3666
    3667

    @with_argparser(_build_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)

### `` macro_arg_complete

    macro_arg_complete(text, line, begidx, endidx)

Tab completes arguments to a macro.

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

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`

RETURNS | DESCRIPTION
--- | ---
`list[str]` | a list of possible tab completions

Source code in `cmd2/cmd2.py`

    3835
    3836
    3837
    3838
    3839
    3840
    3841
    3842
    3843
    3844
    3845
    3846
    3847
    3848
    3849
    3850
    3851
    3852
    3853
    3854

    def macro_arg_complete(
        self,
        text: str,
        line: str,
        begidx: int,
        endidx: int,
    ) -> list[str]:
        """Tab completes arguments to a macro.

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

        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
        :return: a list of possible tab completions
        """
        return self.path_complete(text, line, begidx, endidx)

### `` do_macro

    do_macro(args)

Manage macros.

Source code in `cmd2/cmd2.py`

    3874
    3875
    3876
    3877
    3878
    3879

    @with_argparser(_build_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`

    4117
    4118
    4119
    4120
    4121
    4122
    4123

    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`

    4125
    4126
    4127
    4128
    4129
    4130
    4131
    4132
    4133
    4134
    4135
    4136
    4137
    4138
    4139

    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`

    4205
    4206
    4207
    4208
    4209
    4210
    4211
    4212
    4213
    4214
    4215
    4216
    4217
    4218
    4219
    4220
    4221
    4222
    4223
    4224
    4225
    4226
    4227
    4228
    4229
    4230
    4231
    4232
    4233
    4234
    4235
    4236
    4237
    4238
    4239
    4240
    4241
    4242
    4243
    4244
    4245
    4246
    4247
    4248
    4249
    4250
    4251
    4252
    4253
    4254
    4255
    4256
    4257
    4258
    4259
    4260
    4261
    4262
    4263
    4264
    4265

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

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

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

            # Print any categories first and then the remaining documented commands.
            sorted_categories = sorted(cmds_cats.keys(), key=self.default_sort_key)
            all_cmds = {category: cmds_cats[category] for category in sorted_categories}
            if all_cmds:
                all_cmds[self.default_category] = cmds_doc
            else:
                all_cmds[self.doc_header] = cmds_doc

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

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

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

            if previous_table_printed and (help_topics or cmds_undoc):
                self.poutput()

            self.print_topics(self.misc_header, help_topics, 15, 80)
            self.print_topics(self.undoc_header, cmds_undoc, 15, 80)

        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)
                completer.print_help(args.subcommands, self.stdout)

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

            # If the command function has a docstring, then print it
            elif 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)
                self.perror(err_msg, style=None)
                self.last_result = False

### `` print_topics

    print_topics(header, cmds, cmdlen, maxcol)

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

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

PARAMETER | DESCRIPTION
--- | ---
`header` | string to print above commands being printed TYPE: `str`
`cmds` | list of topics to print TYPE: `list[str] | None`
`cmdlen` | unused, even by cmd's version TYPE: `int`
`maxcol` | max number of display columns to fit into TYPE: `int`

Source code in `cmd2/cmd2.py`

    4267
    4268
    4269
    4270
    4271
    4272
    4273
    4274
    4275
    4276
    4277
    4278
    4279
    4280
    4281
    4282
    4283
    4284
    4285
    4286
    4287
    4288
    4289
    4290

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

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

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

        # Print a row that looks like a table header.
        if header:
            header_grid = Table.grid()
            header_grid.add_row(Text(header, style=Cmd2Style.HELP_HEADER))
            header_grid.add_row(Rule(characters=self.ruler, style=Cmd2Style.TABLE_BORDER))
            self.poutput(header_grid, soft_wrap=False)

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

### `` render_columns

    render_columns(str_list, display_width=80)

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

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

PARAMETER | DESCRIPTION
--- | ---
`str_list` | list of single-line strings to display TYPE: `list[str] | None`
`display_width` | max number of display columns to fit into TYPE: `int` DEFAULT: `80`

RETURNS | DESCRIPTION
--- | ---
`str` | a string containing the columnized output

Source code in `cmd2/cmd2.py`

    4356
    4357
    4358
    4359
    4360
    4361
    4362
    4363
    4364
    4365
    4366
    4367
    4368
    4369
    4370
    4371
    4372
    4373
    4374
    4375
    4376
    4377
    4378
    4379
    4380
    4381
    4382
    4383
    4384
    4385
    4386
    4387
    4388
    4389
    4390
    4391
    4392
    4393
    4394
    4395
    4396
    4397
    4398
    4399
    4400
    4401
    4402
    4403
    4404
    4405
    4406
    4407
    4408
    4409
    4410
    4411
    4412
    4413

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

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

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

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

        rows: list[str] = []

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

        return "\n".join(rows)

### `` columnize

    columnize(str_list, display_width=80)

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

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

PARAMETER | DESCRIPTION
--- | ---
`str_list` | list of single-line strings to display TYPE: `list[str] | None`
`display_width` | max number of display columns to fit into TYPE: `int` DEFAULT: `80`

Source code in `cmd2/cmd2.py`

    4415
    4416
    4417
    4418
    4419
    4420
    4421
    4422
    4423
    4424
    4425
    4426

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

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

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

### `` do_shortcuts

    do_shortcuts(_)

List available shortcuts.

Source code in `cmd2/cmd2.py`

    4432
    4433
    4434
    4435
    4436
    4437
    4438
    4439

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

### `` do_eof

    do_eof(_)

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

This can be overridden if quit should be called differently.

Source code in `cmd2/cmd2.py`

    4451
    4452
    4453
    4454
    4455
    4456
    4457
    4458
    4459
    4460

    @with_argparser(_build_eof_parser)
    def do_eof(self, _: argparse.Namespace) -> bool | None:
        """Quit with no arguments, called when Ctrl-D is pressed.

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

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

### `` do_quit

    do_quit(_)

Exit this application.

Source code in `cmd2/cmd2.py`

    4466
    4467
    4468
    4469
    4470
    4471

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

### `` select

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

Present a 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`

    4473
    4474
    4475
    4476
    4477
    4478
    4479
    4480
    4481
    4482
    4483
    4484
    4485
    4486
    4487
    4488
    4489
    4490
    4491
    4492
    4493
    4494
    4495
    4496
    4497
    4498
    4499
    4500
    4501
    4502
    4503
    4504
    4505
    4506
    4507
    4508
    4509
    4510
    4511
    4512
    4513
    4514
    4515
    4516
    4517
    4518
    4519
    4520
    4521
    4522

    def select(self, opts: str | list[str] | list[tuple[Any, str | None]], prompt: str = 'Your choice? ') -> Any:
        """Present 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: list[str] | list[tuple[Any, str | None]]
        if isinstance(opts, str):
            local_opts = cast(list[tuple[Any, str | None]], list(zip(opts.split(), opts.split(), strict=False)))
        else:
            local_opts = opts
        fulloptions: list[tuple[Any, str | None]] = []
        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))  # noqa: UP031

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

            if not response:
                continue

            try:
                choice = int(response)
                if choice < 1:
                    raise IndexError  # noqa: TRY301
                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`

    4547
    4548
    4549
    4550
    4551
    4552
    4553
    4554
    4555
    4556
    4557
    4558
    4559
    4560
    4561
    4562
    4563
    4564
    4565
    4566
    4567
    4568
    4569
    4570
    4571
    4572
    4573
    4574
    4575
    4576

    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 as exc:
            raise CompletionError(param + " is not a settable parameter") from exc

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

        # Settables with choices list the values of those choices instead of the arg name
        # in help text and this shows in 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,
            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`

    4593
    4594
    4595
    4596
    4597
    4598
    4599
    4600
    4601
    4602
    4603
    4604
    4605
    4606
    4607
    4608
    4609
    4610
    4611
    4612
    4613
    4614
    4615
    4616
    4617
    4618
    4619
    4620
    4621
    4622
    4623
    4624
    4625
    4626
    4627
    4628
    4629
    4630
    4631
    4632
    4633
    4634
    4635
    4636
    4637
    4638
    4639
    4640
    4641
    4642
    4643
    4644
    4645
    4646
    4647
    4648
    4649
    4650
    4651

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

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

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

            if args.value:
                # Try to update the settable's value
                try:
                    orig_value = settable.value
                    settable.value = su.strip_quotes(args.value)
                except ValueError as ex:
                    self.perror(f"Error setting {args.param}: {ex}")
                else:
                    self.poutput(f"{args.param} - was: {orig_value!r}\nnow: {settable.value!r}")
                    self.last_result = True
                return

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

        # Define the table structure
        settable_table = Table(
            Column("Name", no_wrap=True),
            Column("Value", overflow="fold"),
            Column("Description", overflow="fold"),
            box=rich.box.SIMPLE_HEAD,
            show_edge=False,
            border_style=Cmd2Style.TABLE_BORDER,
        )

        # 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]
            settable_table.add_row(
                param,
                str(settable.value),
                settable.description,
            )
            self.last_result[param] = settable.value

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

### `` do_shell

    do_shell(args)

Execute a command as if at the OS prompt.

Source code in `cmd2/cmd2.py`

    4664
    4665
    4666
    4667
    4668
    4669
    4670
    4671
    4672
    4673
    4674
    4675
    4676
    4677
    4678
    4679
    4680
    4681
    4682
    4683
    4684
    4685
    4686
    4687
    4688
    4689
    4690
    4691
    4692
    4693
    4694
    4695
    4696
    4697
    4698
    4699
    4700
    4701
    4702
    4703
    4704
    4705
    4706
    4707
    4708
    4709
    4710
    4711
    4712
    4713
    4714
    4715
    4716
    4717
    4718
    4719

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

        kwargs: dict[str, Any] = {}

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

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

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

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

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

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

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

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

### `` do_py

    do_py(_)

Run an interactive Python shell.

RETURNS | DESCRIPTION
--- | ---
`bool | None` | True if running of commands should stop.

Source code in `cmd2/cmd2.py`

    4956
    4957
    4958
    4959
    4960
    4961
    4962
    4963

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

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

### `` do_run_pyscript

    do_run_pyscript(args)

Run Python script within this application's environment.

RETURNS | DESCRIPTION
--- | ---
`bool | None` | True if running of commands should stop

Source code in `cmd2/cmd2.py`

    4977
    4978
    4979
    4980
    4981
    4982
    4983
    4984
    4985
    4986
    4987
    4988
    4989
    4990
    4991
    4992
    4993
    4994
    4995
    4996
    4997
    4998
    4999
    5000
    5001
    5002
    5003
    5004
    5005
    5006
    5007
    5008
    5009

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

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

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

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

        # Save current command line arguments
        orig_args = sys.argv

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

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

        return py_return

### `` do_ipy

    do_ipy(_)

Run an interactive IPython shell.

RETURNS | DESCRIPTION
--- | ---
`bool | None` | True if running of commands should stop

Source code in `cmd2/cmd2.py`

    5015
    5016
    5017
    5018
    5019
    5020
    5021
    5022
    5023
    5024
    5025
    5026
    5027
    5028
    5029
    5030
    5031
    5032
    5033
    5034
    5035
    5036
    5037
    5038
    5039
    5040
    5041
    5042
    5043
    5044
    5045
    5046
    5047
    5048
    5049
    5050
    5051
    5052
    5053
    5054
    5055
    5056
    5057
    5058
    5059
    5060
    5061
    5062
    5063
    5064
    5065
    5066
    5067
    5068
    5069
    5070
    5071
    5072
    5073
    5074
    5075
    5076
    5077
    5078
    5079
    5080
    5081
    5082
    5083
    5084
    5085

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

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

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

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

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

        from .py_bridge import (
            PyBridge,
        )

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

        self.last_result = True

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

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

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

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

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

            return py_bridge.stop
        finally:
            self._in_py = False

### `` do_history

    do_history(args)

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

RETURNS | DESCRIPTION
--- | ---
`bool | None` | True if running of commands should stop

Source code in `cmd2/cmd2.py`

    5150
    5151
    5152
    5153
    5154
    5155
    5156
    5157
    5158
    5159
    5160
    5161
    5162
    5163
    5164
    5165
    5166
    5167
    5168
    5169
    5170
    5171
    5172
    5173
    5174
    5175
    5176
    5177
    5178
    5179
    5180
    5181
    5182
    5183
    5184
    5185
    5186
    5187
    5188
    5189
    5190
    5191
    5192
    5193
    5194
    5195
    5196
    5197
    5198
    5199
    5200
    5201
    5202
    5203
    5204
    5205
    5206
    5207
    5208
    5209
    5210
    5211
    5212
    5213
    5214
    5215
    5216
    5217
    5218
    5219
    5220
    5221
    5222
    5223
    5224
    5225
    5226
    5227
    5228
    5229
    5230
    5231
    5232
    5233
    5234
    5235
    5236
    5237
    5238
    5239

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

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

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

        # -s and -x can only be used if none of these options are present: [-c -r -e -o -t]
        if (args.script or args.expanded) and (args.clear or args.edit or args.output_file or args.run or args.transcript):
            self.poutput("-s and -x cannot be used with -c, -r, -e, -o, or -t")
            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_result will be set by do_run_script()
                return self.do_run_script(su.quote(fname))
            finally:
                os.remove(fname)
        elif args.output_file:
            full_path = os.path.abspath(os.path.expanduser(args.output_file))
            try:
                with open(full_path, 'w') as fobj:
                    for item in history.values():
                        if item.statement.multiline_command:
                            fobj.write(f"{item.expanded}\n")
                        else:
                            fobj.write(f"{item.raw}\n")
                plural = '' if len(history) == 1 else 's'
            except OSError as ex:
                self.perror(f"Error saving history file '{full_path}': {ex}")
            else:
                self.pfeedback(f"{len(history)} command{plural} saved to {full_path}")
                self.last_result = True
        elif args.transcript:
            # self.last_result 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`

    5493
    5494
    5495
    5496
    5497

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

### `` run_editor

    run_editor(file_path=None)

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

PARAMETER | DESCRIPTION
--- | ---
`file_path` | optional path of the file to edit. Defaults to None. TYPE: `str | None` DEFAULT: `None`

RAISES | DESCRIPTION
--- | ---
`ValueError` | if self.editor is not set

Source code in `cmd2/cmd2.py`

    5499
    5500
    5501
    5502
    5503
    5504
    5505
    5506
    5507
    5508
    5509
    5510
    5511
    5512

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

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

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

        self.do_shell(command)

### `` do_run_script

    do_run_script(args)

Run text script.

RETURNS | DESCRIPTION
--- | ---
`bool | None` | True if running of commands should stop

Source code in `cmd2/cmd2.py`

    5551
    5552
    5553
    5554
    5555
    5556
    5557
    5558
    5559
    5560
    5561
    5562
    5563
    5564
    5565
    5566
    5567
    5568
    5569
    5570
    5571
    5572
    5573
    5574
    5575
    5576
    5577
    5578
    5579
    5580
    5581
    5582
    5583
    5584
    5585
    5586
    5587
    5588
    5589
    5590
    5591
    5592
    5593
    5594
    5595
    5596
    5597
    5598
    5599
    5600
    5601
    5602
    5603
    5604
    5605
    5606
    5607
    5608
    5609
    5610
    5611
    5612

    @with_argparser(_build_run_script_parser)
    def do_run_script(self, args: argparse.Namespace) -> bool | None:
        """Run text script.

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

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

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

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

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

        orig_script_dir_count = len(self._script_dir)

        try:
            self._script_dir.append(os.path.dirname(expanded_path))

            if args.transcript:
                # self.last_result 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 text script.

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

RETURNS | DESCRIPTION
--- | ---
`bool | None` | True if running of commands should stop

Source code in `cmd2/cmd2.py`

    5635
    5636
    5637
    5638
    5639
    5640
    5641
    5642
    5643
    5644
    5645
    5646
    5647
    5648

    @with_argparser(_build_relative_run_script_parser)
    def do__relative_run_script(self, args: argparse.Namespace) -> bool | None:
        """Run text script.

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

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

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

### `` 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: `str | None` 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`

    5714
    5715
    5716
    5717
    5718
    5719
    5720
    5721
    5722
    5723
    5724
    5725
    5726
    5727
    5728
    5729
    5730
    5731
    5732
    5733
    5734
    5735
    5736
    5737
    5738
    5739
    5740
    5741
    5742
    5743
    5744
    5745
    5746
    5747
    5748
    5749
    5750
    5751
    5752
    5753
    5754
    5755
    5756
    5757
    5758
    5759
    5760
    5761
    5762
    5763
    5764
    5765
    5766
    5767
    5768
    5769
    5770
    5771
    5772
    5773
    5774
    5775
    5776
    5777
    5778
    5779

    def async_alert(self, alert_msg: str, new_prompt: str | None = 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:
                from .terminal_utils import async_alert_str

                # Print a string which replaces the onscreen prompt and input lines with the alert.
                terminal_str = async_alert_str(
                    terminal_columns=ru.console_width(),
                    prompt=rl_get_display_prompt(),
                    line=readline.get_line_buffer(),
                    cursor_offset=rl_get_point(),
                    alert_msg=alert_msg,
                )

                sys.stdout.write(terminal_str)
                sys.stdout.flush()

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

    5781
    5782
    5783
    5784
    5785
    5786
    5787
    5788
    5789
    5790
    5791
    5792
    5793
    5794
    5795
    5796
    5797
    5798

    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`

    5800
    5801
    5802
    5803
    5804
    5805
    5806
    5807
    5808
    5809
    5810
    5811
    5812
    5813
    5814
    5815

    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`

    5817
    5818
    5819
    5820
    5821
    5822
    5823

    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.

PARAMETER | DESCRIPTION
--- | ---
`title` | the new window title TYPE: `str`

Source code in `cmd2/cmd2.py`

    5825
    5826
    5827
    5828
    5829
    5830
    5831
    5832
    5833
    5834
    5835
    5836
    5837
    5838
    5839
    5840
    5841

    @staticmethod
    def set_window_title(title: str) -> None:  # pragma: no cover
        """Set the terminal window title.

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

        from .terminal_utils import set_title_str

        try:
            sys.stdout.write(set_title_str(title))
            sys.stdout.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`

    5843
    5844
    5845
    5846
    5847
    5848
    5849
    5850
    5851
    5852
    5853
    5854
    5855
    5856
    5857
    5858
    5859
    5860
    5861
    5862
    5863
    5864
    5865
    5866
    5867
    5868
    5869
    5870
    5871
    5872
    5873

    def enable_command(self, command: str) -> None:
        """Enable a command by restoring its functions.

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

        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`

    5875
    5876
    5877
    5878
    5879
    5880
    5881
    5882
    5883
    5884
    5885
    5886
    5887
    5888
    5889

    def enable_category(self, category: str) -> None:
        """Enable an entire category of commands.

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

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

        del self.disabled_categories[category]

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

    5891
    5892
    5893
    5894
    5895
    5896
    5897
    5898
    5899
    5900
    5901
    5902
    5903
    5904
    5905
    5906
    5907
    5908
    5909
    5910
    5911
    5912
    5913
    5914
    5915
    5916
    5917
    5918
    5919
    5920
    5921
    5922
    5923
    5924
    5925
    5926
    5927
    5928
    5929

    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 command 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`

    5931
    5932
    5933
    5934
    5935
    5936
    5937
    5938
    5939
    5940
    5941
    5942
    5943
    5944
    5945
    5946
    5947
    5948
    5949
    5950
    5951

    def disable_category(self, category: str, message_to_print: str) -> None:
        """Disable an entire category of commands.

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

        all_commands = self.get_all_commands()

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

        self.disabled_categories[category] = message_to_print

### `` cmdloop

    cmdloop(intro='')

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

_cmdloop() provides the main loop. This provides the following extra features provided by cmd2: - 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: `RenderableType` DEFAULT: `''`

Source code in `cmd2/cmd2.py`

    5962
    5963
    5964
    5965
    5966
    5967
    5968
    5969
    5970
    5971
    5972
    5973
    5974
    5975
    5976
    5977
    5978
    5979
    5980
    5981
    5982
    5983
    5984
    5985
    5986
    5987
    5988
    5989
    5990
    5991
    5992
    5993
    5994
    5995
    5996
    5997
    5998
    5999
    6000
    6001
    6002
    6003
    6004
    6005
    6006
    6007
    6008
    6009
    6010
    6011
    6012
    6013
    6014
    6015
    6016
    6017
    6018
    6019
    6020
    6021
    6022
    6023
    6024
    6025
    6026
    6027
    6028
    6029

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

        _cmdloop() provides the main loop.  This provides the following extra features provided by cmd2:
        - 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:
                self.intro = intro

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

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

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

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

    6064
    6065
    6066
    6067

    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`

    6069
    6070
    6071
    6072

    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`

    6087
    6088
    6089
    6090

    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`

    6115
    6116
    6117
    6118

    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`

    6120
    6121
    6122
    6123

    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`

    6142
    6143
    6144
    6145
    6146
    6147

    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)