diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1261bc49..6b7bc5c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@
         * `pyscript` - replaced by `run_pyscript`
     * We apologize for any inconvenience, but the new names are more self-descriptive
         * Lots of end users were confused particularly about what exactly `load` should be loading
+* Breaking Changes
+    * Restored `cmd2.Cmd.statement_parser` to be a public attribute (no underscore) 
+        * Since it can be useful for creating [post-parsing hooks](https://cmd2.readthedocs.io/en/latest/features/hooks.html#postparsing-hooks)
+    
 
 ## 0.9.14 (June 29, 2019)
 * Enhancements
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 26636c41..3aa4beb3 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -146,9 +146,9 @@ def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) ->
     def arg_decorator(func: Callable):
         @functools.wraps(func)
         def cmd_wrapper(cmd2_instance, statement: Union[Statement, str]):
-            _, parsed_arglist = cmd2_instance._statement_parser.get_command_arg_list(command_name,
-                                                                                     statement,
-                                                                                     preserve_quotes)
+            _, parsed_arglist = cmd2_instance.statement_parser.get_command_arg_list(command_name,
+                                                                                    statement,
+                                                                                    preserve_quotes)
 
             return func(cmd2_instance, parsed_arglist)
 
@@ -185,9 +185,9 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, *,
     def arg_decorator(func: Callable):
         @functools.wraps(func)
         def cmd_wrapper(cmd2_instance, statement: Union[Statement, str]):
-            statement, parsed_arglist = cmd2_instance._statement_parser.get_command_arg_list(command_name,
-                                                                                             statement,
-                                                                                             preserve_quotes)
+            statement, parsed_arglist = cmd2_instance.statement_parser.get_command_arg_list(command_name,
+                                                                                            statement,
+                                                                                            preserve_quotes)
 
             if ns_provider is None:
                 namespace = None
@@ -243,9 +243,9 @@ def with_argparser(argparser: argparse.ArgumentParser, *,
     def arg_decorator(func: Callable):
         @functools.wraps(func)
         def cmd_wrapper(cmd2_instance, statement: Union[Statement, str]):
-            statement, parsed_arglist = cmd2_instance._statement_parser.get_command_arg_list(command_name,
-                                                                                             statement,
-                                                                                             preserve_quotes)
+            statement, parsed_arglist = cmd2_instance.statement_parser.get_command_arg_list(command_name,
+                                                                                            statement,
+                                                                                            preserve_quotes)
 
             if ns_provider is None:
                 namespace = None
@@ -393,10 +393,10 @@ class Cmd(cmd.Cmd):
         if shortcuts is None:
             shortcuts = constants.DEFAULT_SHORTCUTS
         shortcuts = sorted(shortcuts.items(), reverse=True)
-        self._statement_parser = StatementParser(allow_redirection=allow_redirection,
-                                                 terminators=terminators,
-                                                 multiline_commands=multiline_commands,
-                                                 shortcuts=shortcuts)
+        self.statement_parser = StatementParser(allow_redirection=allow_redirection,
+                                                terminators=terminators,
+                                                multiline_commands=multiline_commands,
+                                                shortcuts=shortcuts)
 
         # True if running inside a Python script or interactive console, False otherwise
         self._in_py = False
@@ -561,17 +561,17 @@ class Cmd(cmd.Cmd):
     @property
     def aliases(self) -> Dict[str, str]:
         """Read-only property to access the aliases stored in the StatementParser."""
-        return self._statement_parser.aliases
+        return self.statement_parser.aliases
 
     @property
     def allow_redirection(self) -> bool:
         """Getter for the allow_redirection property that determines whether or not redirection of stdout is allowed."""
-        return self._statement_parser.allow_redirection
+        return self.statement_parser.allow_redirection
 
     @allow_redirection.setter
     def allow_redirection(self, value: bool) -> None:
         """Setter for the allow_redirection property that determines whether or not redirection of stdout is allowed."""
-        self._statement_parser.allow_redirection = value
+        self.statement_parser.allow_redirection = value
 
     def poutput(self, msg: Any, *, end: str = '\n') -> None:
         """Print message to self.stdout and appends a newline by default
@@ -1385,7 +1385,7 @@ class Cmd(cmd.Cmd):
             # from text and update the indexes. This only applies if we are at the the beginning of the line.
             shortcut_to_restore = ''
             if begidx == 0:
-                for (shortcut, _) in self._statement_parser.shortcuts:
+                for (shortcut, _) in self.statement_parser.shortcuts:
                     if text.startswith(shortcut):
                         # Save the shortcut to restore later
                         shortcut_to_restore = shortcut
@@ -1399,7 +1399,7 @@ class Cmd(cmd.Cmd):
             if begidx > 0:
 
                 # Parse the command line
-                statement = self._statement_parser.parse_command_only(line)
+                statement = self.statement_parser.parse_command_only(line)
                 command = statement.command
                 expanded_line = statement.command_and_args
 
@@ -1678,7 +1678,7 @@ class Cmd(cmd.Cmd):
         :param line: line read by readline
         :return: tuple containing (command, args, line)
         """
-        statement = self._statement_parser.parse_command_only(line)
+        statement = self.statement_parser.parse_command_only(line)
         return statement.command, statement.args, statement.command_and_args
 
     def onecmd_plus_hooks(self, line: str, pyscript_bridge_call: bool = False) -> bool:
@@ -1842,7 +1842,7 @@ class Cmd(cmd.Cmd):
         """
         while True:
             try:
-                statement = self._statement_parser.parse(line)
+                statement = self.statement_parser.parse(line)
                 if statement.multiline_command and statement.terminator:
                     # we have a completed multiline command, we are done
                     break
@@ -1853,7 +1853,7 @@ class Cmd(cmd.Cmd):
             except ValueError:
                 # we have unclosed quotation marks, lets parse only the command
                 # and see if it's a multiline
-                statement = self._statement_parser.parse_command_only(line)
+                statement = self.statement_parser.parse_command_only(line)
                 if not statement.multiline_command:
                     # not a multiline command, so raise the exception
                     raise
@@ -1877,7 +1877,7 @@ class Cmd(cmd.Cmd):
                     raise ex
                 else:
                     self.poutput('^C')
-                    statement = self._statement_parser.parse('')
+                    statement = self.statement_parser.parse('')
                     break
             finally:
                 self._at_continuation_prompt = False
@@ -2290,7 +2290,7 @@ class Cmd(cmd.Cmd):
         """Create or overwrite an alias"""
 
         # Validate the alias name
-        valid, errmsg = self._statement_parser.is_valid_command(args.name)
+        valid, errmsg = self.statement_parser.is_valid_command(args.name)
         if not valid:
             self.perror("Invalid alias name: {}".format(errmsg))
             return
@@ -2301,7 +2301,7 @@ class Cmd(cmd.Cmd):
 
         # Unquote redirection and terminator tokens
         tokens_to_unquote = constants.REDIRECTION_TOKENS
-        tokens_to_unquote.extend(self._statement_parser.terminators)
+        tokens_to_unquote.extend(self.statement_parser.terminators)
         utils.unquote_specific_tokens(args.command_args, tokens_to_unquote)
 
         # Build the alias value string
@@ -2421,7 +2421,7 @@ class Cmd(cmd.Cmd):
         """Create or overwrite a macro"""
 
         # Validate the macro name
-        valid, errmsg = self._statement_parser.is_valid_command(args.name)
+        valid, errmsg = self.statement_parser.is_valid_command(args.name)
         if not valid:
             self.perror("Invalid macro name: {}".format(errmsg))
             return
@@ -2436,7 +2436,7 @@ class Cmd(cmd.Cmd):
 
         # Unquote redirection and terminator tokens
         tokens_to_unquote = constants.REDIRECTION_TOKENS
-        tokens_to_unquote.extend(self._statement_parser.terminators)
+        tokens_to_unquote.extend(self.statement_parser.terminators)
         utils.unquote_specific_tokens(args.command_args, tokens_to_unquote)
 
         # Build the macro value string
@@ -2830,7 +2830,7 @@ class Cmd(cmd.Cmd):
     @with_argparser(ACArgumentParser())
     def do_shortcuts(self, _: argparse.Namespace) -> None:
         """List available shortcuts"""
-        result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in sorted(self._statement_parser.shortcuts))
+        result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in sorted(self.statement_parser.shortcuts))
         self.poutput("Shortcuts for other commands:\n{}".format(result))
 
     @with_argparser(ACArgumentParser(epilog=INTERNAL_COMMAND_EPILOG))
@@ -2899,7 +2899,7 @@ class Cmd(cmd.Cmd):
         read_only_settings = """
         Commands may be terminated with: {}
         Output redirection and pipes allowed: {}"""
-        return read_only_settings.format(str(self._statement_parser.terminators), self.allow_redirection)
+        return read_only_settings.format(str(self.statement_parser.terminators), self.allow_redirection)
 
     def _show(self, args: argparse.Namespace, parameter: str = '') -> None:
         """Shows current settings of parameters.
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index 86087db1..2e94516a 100644
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -324,7 +324,7 @@ class StatementParser:
         This string is suitable for inclusion in an error message of your
         choice:
 
-        valid, errmsg = _statement_parser.is_valid_command('>')
+        valid, errmsg = statement_parser.is_valid_command('>')
         if not valid:
             errmsg = "Alias {}".format(errmsg)
         """
diff --git a/examples/hooks.py b/examples/hooks.py
index acd427cd..409c8979 100755
--- a/examples/hooks.py
+++ b/examples/hooks.py
@@ -61,7 +61,7 @@ class CmdLineApp(cmd2.Cmd):
         command_pattern = re.compile(r'^([^\s\d]+)(\d+)')
         match = command_pattern.search(command)
         if match:
-            data.statement = self._statement_parser.parse("{} {} {}".format(
+            data.statement = self.statement_parser.parse("{} {} {}".format(
                 match.group(1),
                 match.group(2),
                 '' if data.statement.args is None else data.statement.args
@@ -71,7 +71,7 @@ class CmdLineApp(cmd2.Cmd):
     def downcase_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
         """A hook to make uppercase commands lowercase."""
         command = data.statement.command.lower()
-        data.statement = self._statement_parser.parse("{} {}".format(
+        data.statement = self.statement_parser.parse("{} {}".format(
             command,
             '' if data.statement.args is None else data.statement.args
         ))
@@ -85,7 +85,7 @@ class CmdLineApp(cmd2.Cmd):
             possible_cmds = [cmd for cmd in self.get_all_commands() if cmd.startswith(data.statement.command)]
             if len(possible_cmds) == 1:
                 raw = data.statement.raw.replace(data.statement.command, possible_cmds[0], 1)
-                data.statement = self._statement_parser.parse(raw)
+                data.statement = self.statement_parser.parse(raw)
         return data
 
     @cmd2.with_argument_list

self.statement_parser was changed to self._statement_parser even though Docs suggest to use it.
self.statement_parser was changed to self._statement_parser, but in the docs, we have put it as a part of the processing hook (see [Postparsing Hooks](https://cmd2.readthedocs.io/en/latest/features/hooks.html) section)

diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index c9a41033..9ffe547a 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -109,7 +109,7 @@ def test_base_show_readonly(base_app):
     expected = normalize(SHOW_TXT + '\nRead only settings:' + """
         Commands may be terminated with: {}
         Output redirection and pipes allowed: {}
-""".format(base_app._statement_parser.terminators, base_app.allow_redirection))
+""".format(base_app.statement_parser.terminators, base_app.allow_redirection))
     assert out == expected
 
 
@@ -532,7 +532,7 @@ def test_feedback_to_output_false(base_app):
 
 def test_disallow_redirection(base_app):
     # Set allow_redirection to False
-    base_app._statement_parser.allow_redirection = False
+    base_app.statement_parser.allow_redirection = False
 
     filename = 'test_allow_redirect.txt'
 
diff --git a/tests/test_completion.py b/tests/test_completion.py
index eea34ba6..5cfc741c 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -703,7 +703,7 @@ def test_tokens_for_completion_quoted_redirect(cmd2_app):
     endidx = len(line)
     begidx = endidx - len(text)
 
-    cmd2_app._statement_parser.redirection = True
+    cmd2_app.statement_parser.redirection = True
     expected_tokens = ['command', '>file']
     expected_raw_tokens = ['command', '">file']
 
@@ -717,7 +717,7 @@ def test_tokens_for_completion_redirect_off(cmd2_app):
     endidx = len(line)
     begidx = endidx - len(text)
 
-    cmd2_app._statement_parser.allow_redirection = False
+    cmd2_app.statement_parser.allow_redirection = False
     expected_tokens = ['command', '>file']
     expected_raw_tokens = ['command', '>file']
 

pip install -e .[dev]
parse_log_pytest
attrs>=16.3.0 colorama>=0.3.7 pyperclip>=1.6 wcwidth>=0.1.7
pytest
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs @ file:///croot/attrs_1668696182826/work
Babel==2.14.0
bleach==6.0.0
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
-e git+https://github.com/python-cmd2/cmd2.git@dbd83973b62b87e8dc74426af410b0852360f7e8#egg=cmd2
codecov==2.1.13
colorama @ file:///croot/colorama_1672386526460/work
coverage==7.2.7
cryptography==44.0.2
distlib==0.3.9
doc8==1.0.0
docutils==0.20.1
exceptiongroup==1.2.2
filelock==3.12.2
flake8==5.0.4
idna==3.10
imagesize==1.4.1
importlib-metadata==4.2.0
iniconfig==2.0.0
invoke==2.2.0
jaraco.classes==3.2.3
jeepney==0.9.0
Jinja2==3.1.6
keyring==23.9.3
livereload==2.7.1
markdown-it-py==2.2.0
MarkupSafe==2.1.5
mccabe==0.7.0
mdurl==0.1.2
more-itertools==9.1.0
packaging==24.0
pbr==6.1.1
pkginfo==1.10.0
platformdirs==2.6.2
pluggy==1.2.0
py==1.11.0
pycodestyle==2.9.1
pycparser==2.21
pyflakes==2.5.0
Pygments==2.17.2
pyperclip @ file:///home/conda/feedstock_root/build_artifacts/pyperclip_1731332155486/work
pytest==7.4.4
pytest-cov==4.1.0
pytest-mock==3.11.1
pytz==2025.2
readme-renderer==37.3
requests==2.31.0
requests-toolbelt==1.0.0
restructuredtext-lint==1.4.0
rfc3986==2.0.0
rich==13.8.1
SecretStorage==3.3.3
six==1.17.0
snowballstemmer==2.2.0
Sphinx==3.5.3
sphinx-autobuild==2021.3.14
sphinx-rtd-theme==0.5.1
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
stevedore==3.5.2
tomli==2.0.1
tornado==6.2
tox==3.28.0
twine==4.0.2
typing_extensions==4.7.1
urllib3==2.0.7
virtualenv==20.16.2
wcwidth @ file:///Users/ktietz/demo/mc3/conda-bld/wcwidth_1629357192024/work
webencodings==0.5.1
zipp==3.15.0

name: cmd2
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - colorama=0.4.6=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc=14.2.0=h767d61c_2
  - libgcc-ng=14.2.0=h69a702a_2
  - libgomp=14.2.0=h767d61c_2
  - libstdcxx-ng=11.2.0=h1234567_1
  - libuuid=2.38.1=h0b41bf4_0
  - libxcb=1.17.0=h8a09558_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - pthread-stubs=0.3=h0ce48e5_1
  - pyperclip=1.9.0=pyha804496_1
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wcwidth=0.2.5=pyhd3eb1b0_0
  - wheel=0.38.4=py37h06a4308_0
  - xclip=0.13=hb9d3cd8_4
  - xorg-libice=1.1.2=hb9d3cd8_0
  - xorg-libsm=1.2.6=he73a12e_0
  - xorg-libx11=1.8.12=h4f16b4b_0
  - xorg-libxau=1.0.12=hb9d3cd8_0
  - xorg-libxdmcp=1.1.5=hb9d3cd8_0
  - xorg-libxext=1.3.6=hb9d3cd8_0
  - xorg-libxmu=1.2.1=hb9d3cd8_1
  - xorg-libxt=1.3.1=hb9d3cd8_0
  - xsel=1.2.1=hb9d3cd8_6
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - babel==2.14.0
      - bleach==6.0.0
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - codecov==2.1.13
      - coverage==7.2.7
      - cryptography==44.0.2
      - distlib==0.3.9
      - doc8==1.0.0
      - docutils==0.20.1
      - exceptiongroup==1.2.2
      - filelock==3.12.2
      - flake8==5.0.4
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.2.0
      - iniconfig==2.0.0
      - invoke==2.2.0
      - jaraco-classes==3.2.3
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==23.9.3
      - livereload==2.7.1
      - markdown-it-py==2.2.0
      - markupsafe==2.1.5
      - mccabe==0.7.0
      - mdurl==0.1.2
      - more-itertools==9.1.0
      - packaging==24.0
      - pbr==6.1.1
      - pkginfo==1.10.0
      - platformdirs==2.6.2
      - pluggy==1.2.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pyflakes==2.5.0
      - pygments==2.17.2
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - pytest-mock==3.11.1
      - pytz==2025.2
      - readme-renderer==37.3
      - requests==2.31.0
      - requests-toolbelt==1.0.0
      - restructuredtext-lint==1.4.0
      - rfc3986==2.0.0
      - rich==13.8.1
      - secretstorage==3.3.3
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==3.5.3
      - sphinx-autobuild==2021.3.14
      - sphinx-rtd-theme==0.5.1
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - stevedore==3.5.2
      - tomli==2.0.1
      - tornado==6.2
      - tox==3.28.0
      - twine==4.0.2
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - virtualenv==20.16.2
      - webencodings==0.5.1
      - zipp==3.15.0
prefix: /opt/conda/envs/cmd2

tests/test_cmd2.py::test_base_show_readonly tests/test_cmd2.py::test_disallow_redirection tests/test_completion.py::test_tokens_for_completion_quoted_redirect tests/test_completion.py::test_tokens_for_completion_redirect_off
tests/test_cmd2.py::test_which_editor_good
tests/test_cmd2.py::test_version tests/test_cmd2.py::test_not_in_main_thread tests/test_cmd2.py::test_empty_statement tests/test_cmd2.py::test_base_help tests/test_cmd2.py::test_base_help_verbose tests/test_cmd2.py::test_base_argparse_help tests/test_cmd2.py::test_base_invalid_option tests/test_cmd2.py::test_base_shortcuts tests/test_cmd2.py::test_base_show tests/test_cmd2.py::test_base_show_long tests/test_cmd2.py::test_cast tests/test_cmd2.py::test_cast_problems tests/test_cmd2.py::test_base_set tests/test_cmd2.py::test_set_not_supported tests/test_cmd2.py::test_set_quiet tests/test_cmd2.py::test_set_allow_ansi[Never-False-Never] tests/test_cmd2.py::test_set_allow_ansi[neVeR-False-Never] tests/test_cmd2.py::test_set_allow_ansi[Terminal-False-Terminal] tests/test_cmd2.py::test_set_allow_ansi[TeRMInal-False-Terminal] tests/test_cmd2.py::test_set_allow_ansi[Always-False-Always] tests/test_cmd2.py::test_set_allow_ansi[AlWaYs-False-Always] tests/test_cmd2.py::test_set_allow_ansi[invalid-True-Terminal] tests/test_cmd2.py::test_set_onchange_hook tests/test_cmd2.py::test_base_shell tests/test_cmd2.py::test_base_py tests/test_cmd2.py::test_py_run_script tests/test_cmd2.py::test_base_error tests/test_cmd2.py::test_run_script tests/test_cmd2.py::test_run_script_with_empty_args tests/test_cmd2.py::test_run_script_with_nonexistent_file tests/test_cmd2.py::test_run_script_with_directory tests/test_cmd2.py::test_run_script_with_empty_file tests/test_cmd2.py::test_run_script_with_binary_file tests/test_cmd2.py::test_run_script_with_utf8_file tests/test_cmd2.py::test_run_script_nested_run_scripts tests/test_cmd2.py::test_runcmds_plus_hooks tests/test_cmd2.py::test_relative_run_script tests/test_cmd2.py::test_relative_run_script_requires_an_argument tests/test_cmd2.py::test_output_redirection tests/test_cmd2.py::test_output_redirection_to_nonexistent_directory tests/test_cmd2.py::test_output_redirection_to_too_long_filename tests/test_cmd2.py::test_feedback_to_output_true tests/test_cmd2.py::test_feedback_to_output_false tests/test_cmd2.py::test_pipe_to_shell tests/test_cmd2.py::test_pipe_to_shell_and_redirect tests/test_cmd2.py::test_pipe_to_shell_error tests/test_cmd2.py::test_base_timing tests/test_cmd2.py::test_base_debug tests/test_cmd2.py::test_edit_file tests/test_cmd2.py::test_edit_file_with_spaces tests/test_cmd2.py::test_edit_blank tests/test_cmd2.py::test_base_py_interactive tests/test_cmd2.py::test_base_cmdloop_with_startup_commands tests/test_cmd2.py::test_base_cmdloop_without_startup_commands tests/test_cmd2.py::test_cmdloop_without_rawinput tests/test_cmd2.py::test_precmd_hook_success tests/test_cmd2.py::test_precmd_hook_failure tests/test_cmd2.py::test_interrupt_quit tests/test_cmd2.py::test_interrupt_noquit tests/test_cmd2.py::test_default_to_shell tests/test_cmd2.py::test_ansi_prompt_not_esacped tests/test_cmd2.py::test_ansi_prompt_escaped tests/test_cmd2.py::test_custom_command_help tests/test_cmd2.py::test_custom_help_menu tests/test_cmd2.py::test_help_undocumented tests/test_cmd2.py::test_help_overridden_method tests/test_cmd2.py::test_help_cat_base tests/test_cmd2.py::test_help_cat_verbose tests/test_cmd2.py::test_select_options tests/test_cmd2.py::test_select_invalid_option_too_big tests/test_cmd2.py::test_select_invalid_option_too_small tests/test_cmd2.py::test_select_list_of_strings tests/test_cmd2.py::test_select_list_of_tuples tests/test_cmd2.py::test_select_uneven_list_of_tuples tests/test_cmd2.py::test_help_with_no_docstring tests/test_cmd2.py::test_which_editor_bad tests/test_cmd2.py::test_multiline_complete_empty_statement_raises_exception tests/test_cmd2.py::test_multiline_complete_statement_without_terminator tests/test_cmd2.py::test_multiline_complete_statement_with_unclosed_quotes tests/test_cmd2.py::test_multiline_input_line_to_statement tests/test_cmd2.py::test_clipboard_failure tests/test_cmd2.py::test_commandresult_truthy tests/test_cmd2.py::test_commandresult_falsy tests/test_cmd2.py::test_is_text_file_bad_input tests/test_cmd2.py::test_eof tests/test_cmd2.py::test_echo tests/test_cmd2.py::test_pseudo_raw_input_tty_rawinput_true tests/test_cmd2.py::test_pseudo_raw_input_tty_rawinput_false tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_true_echo_true tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_true_echo_false tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_false_echo_true tests/test_cmd2.py::test_pseudo_raw_input_piped_rawinput_false_echo_false tests/test_cmd2.py::test_raw_input tests/test_cmd2.py::test_stdin_input tests/test_cmd2.py::test_empty_stdin_input tests/test_cmd2.py::test_poutput_string tests/test_cmd2.py::test_poutput_zero tests/test_cmd2.py::test_poutput_empty_string tests/test_cmd2.py::test_poutput_none tests/test_cmd2.py::test_poutput_ansi_always tests/test_cmd2.py::test_poutput_ansi_never tests/test_cmd2.py::test_get_alias_names tests/test_cmd2.py::test_get_macro_names tests/test_cmd2.py::test_get_settable_names tests/test_cmd2.py::test_alias_no_subcommand tests/test_cmd2.py::test_alias_create tests/test_cmd2.py::test_alias_create_with_quoted_value tests/test_cmd2.py::test_alias_create_invalid_name[""] tests/test_cmd2.py::test_alias_create_invalid_name[#] tests/test_cmd2.py::test_alias_create_invalid_name[!no_shortcut] tests/test_cmd2.py::test_alias_create_invalid_name[">"] tests/test_cmd2.py::test_alias_create_invalid_name["no>pe"] tests/test_cmd2.py::test_alias_create_invalid_name["no tests/test_cmd2.py::test_alias_create_invalid_name["nopipe|"] tests/test_cmd2.py::test_alias_create_invalid_name["noterm;"] tests/test_cmd2.py::test_alias_create_invalid_name[noembedded"quotes] tests/test_cmd2.py::test_alias_create_with_macro_name tests/test_cmd2.py::test_alias_that_resolves_into_comment tests/test_cmd2.py::test_alias_list_invalid_alias tests/test_cmd2.py::test_alias_delete tests/test_cmd2.py::test_alias_delete_all tests/test_cmd2.py::test_alias_delete_non_existing tests/test_cmd2.py::test_alias_delete_no_name tests/test_cmd2.py::test_multiple_aliases tests/test_cmd2.py::test_macro_no_subcommand tests/test_cmd2.py::test_macro_create tests/test_cmd2.py::test_macro_create_with_quoted_value tests/test_cmd2.py::test_macro_create_invalid_name[""] tests/test_cmd2.py::test_macro_create_invalid_name[#] tests/test_cmd2.py::test_macro_create_invalid_name[!no_shortcut] tests/test_cmd2.py::test_macro_create_invalid_name[">"] tests/test_cmd2.py::test_macro_create_invalid_name["no>pe"] tests/test_cmd2.py::test_macro_create_invalid_name["no tests/test_cmd2.py::test_macro_create_invalid_name["nopipe|"] tests/test_cmd2.py::test_macro_create_invalid_name["noterm;"] tests/test_cmd2.py::test_macro_create_invalid_name[noembedded"quotes] tests/test_cmd2.py::test_macro_create_with_alias_name tests/test_cmd2.py::test_macro_create_with_command_name tests/test_cmd2.py::test_macro_create_with_args tests/test_cmd2.py::test_macro_create_with_escaped_args tests/test_cmd2.py::test_macro_usage_with_missing_args tests/test_cmd2.py::test_macro_usage_with_exta_args tests/test_cmd2.py::test_macro_create_with_missing_arg_nums tests/test_cmd2.py::test_macro_create_with_invalid_arg_num tests/test_cmd2.py::test_macro_create_with_unicode_numbered_arg tests/test_cmd2.py::test_macro_create_with_missing_unicode_arg_nums tests/test_cmd2.py::test_macro_that_resolves_into_comment tests/test_cmd2.py::test_macro_list_invalid_macro tests/test_cmd2.py::test_macro_delete tests/test_cmd2.py::test_macro_delete_all tests/test_cmd2.py::test_macro_delete_non_existing tests/test_cmd2.py::test_macro_delete_no_name tests/test_cmd2.py::test_multiple_macros tests/test_cmd2.py::test_nonexistent_macro tests/test_cmd2.py::test_ppaged tests/test_cmd2.py::test_ppaged_strips_ansi_when_redirecting tests/test_cmd2.py::test_ppaged_strips_ansi_when_redirecting_if_always tests/test_cmd2.py::test_parseline_empty tests/test_cmd2.py::test_parseline tests/test_cmd2.py::test_onecmd_raw_str_continue tests/test_cmd2.py::test_onecmd_raw_str_quit tests/test_cmd2.py::test_get_all_commands tests/test_cmd2.py::test_get_help_topics tests/test_cmd2.py::test_exit_code_default tests/test_cmd2.py::test_exit_code_nonzero tests/test_cmd2.py::test_ansi_pouterr_always_tty tests/test_cmd2.py::test_ansi_pouterr_always_notty tests/test_cmd2.py::test_ansi_terminal_tty tests/test_cmd2.py::test_ansi_terminal_notty tests/test_cmd2.py::test_ansi_never_tty tests/test_cmd2.py::test_ansi_never_notty tests/test_cmd2.py::test_disable_and_enable_category tests/test_cmd2.py::test_enable_enabled_command tests/test_cmd2.py::test_disable_fake_command tests/test_cmd2.py::test_disable_command_twice tests/test_cmd2.py::test_disabled_command_not_in_history tests/test_cmd2.py::test_disabled_message_command_name tests/test_cmd2.py::test_startup_script tests/test_cmd2.py::test_transcripts_at_init tests/test_completion.py::test_cmd2_command_completion_single tests/test_completion.py::test_complete_command_single tests/test_completion.py::test_complete_empty_arg tests/test_completion.py::test_complete_bogus_command tests/test_completion.py::test_complete_exception tests/test_completion.py::test_complete_macro tests/test_completion.py::test_matches_sort_key tests/test_completion.py::test_cmd2_command_completion_multiple tests/test_completion.py::test_cmd2_command_completion_nomatch tests/test_completion.py::test_cmd2_help_completion_single tests/test_completion.py::test_cmd2_help_completion_multiple tests/test_completion.py::test_cmd2_help_completion_nomatch tests/test_completion.py::test_shell_command_completion_shortcut tests/test_completion.py::test_shell_command_completion_doesnt_match_wildcards tests/test_completion.py::test_shell_command_completion_multiple tests/test_completion.py::test_shell_command_completion_nomatch tests/test_completion.py::test_shell_command_completion_doesnt_complete_when_just_shell tests/test_completion.py::test_shell_command_completion_does_path_completion_when_after_command tests/test_completion.py::test_shell_commmand_complete_in_path tests/test_completion.py::test_path_completion_single_end tests/test_completion.py::test_path_completion_multiple tests/test_completion.py::test_path_completion_nomatch tests/test_completion.py::test_default_to_shell_completion tests/test_completion.py::test_path_completion_no_text tests/test_completion.py::test_path_completion_no_path tests/test_completion.py::test_path_completion_cwd_is_root_dir tests/test_completion.py::test_path_completion_doesnt_match_wildcards tests/test_completion.py::test_path_completion_complete_user tests/test_completion.py::test_path_completion_user_path_expansion tests/test_completion.py::test_path_completion_directories_only tests/test_completion.py::test_basic_completion_single tests/test_completion.py::test_basic_completion_multiple tests/test_completion.py::test_basic_completion_nomatch tests/test_completion.py::test_delimiter_completion tests/test_completion.py::test_flag_based_completion_single tests/test_completion.py::test_flag_based_completion_multiple tests/test_completion.py::test_flag_based_completion_nomatch tests/test_completion.py::test_flag_based_default_completer tests/test_completion.py::test_flag_based_callable_completer tests/test_completion.py::test_index_based_completion_single tests/test_completion.py::test_index_based_completion_multiple tests/test_completion.py::test_index_based_completion_nomatch tests/test_completion.py::test_index_based_default_completer tests/test_completion.py::test_index_based_callable_completer tests/test_completion.py::test_tokens_for_completion_quoted tests/test_completion.py::test_tokens_for_completion_unclosed_quote tests/test_completion.py::test_tokens_for_completion_redirect tests/test_completion.py::test_add_opening_quote_basic_no_text tests/test_completion.py::test_add_opening_quote_basic_nothing_added tests/test_completion.py::test_add_opening_quote_basic_quote_added tests/test_completion.py::test_add_opening_quote_basic_single_quote_added tests/test_completion.py::test_add_opening_quote_basic_text_is_common_prefix tests/test_completion.py::test_add_opening_quote_delimited_no_text tests/test_completion.py::test_add_opening_quote_delimited_nothing_added tests/test_completion.py::test_add_opening_quote_delimited_quote_added tests/test_completion.py::test_add_opening_quote_delimited_text_is_common_prefix tests/test_completion.py::test_add_opening_quote_delimited_space_in_prefix tests/test_completion.py::test_no_completer tests/test_completion.py::test_quote_as_command tests/test_completion.py::test_redirect_complete[fake-RedirCompType.DEFAULT] tests/test_completion.py::test_redirect_complete[fake tests/test_completion.py::test_cmd2_subcommand_completion_single_end tests/test_completion.py::test_cmd2_subcommand_completion_multiple tests/test_completion.py::test_cmd2_subcommand_completion_nomatch tests/test_completion.py::test_cmd2_help_subcommand_completion_single tests/test_completion.py::test_cmd2_help_subcommand_completion_multiple tests/test_completion.py::test_cmd2_help_subcommand_completion_nomatch tests/test_completion.py::test_subcommand_tab_completion tests/test_completion.py::test_subcommand_tab_completion_with_no_completer tests/test_completion.py::test_subcommand_tab_completion_space_in_text tests/test_completion.py::test_cmd2_subcmd_with_unknown_completion_single_end tests/test_completion.py::test_cmd2_subcmd_with_unknown_completion_multiple tests/test_completion.py::test_cmd2_subcmd_with_unknown_completion_nomatch tests/test_completion.py::test_cmd2_help_subcommand_completion_single_scu tests/test_completion.py::test_cmd2_help_subcommand_completion_multiple_scu tests/test_completion.py::test_cmd2_help_subcommand_completion_with_flags_before_command tests/test_completion.py::test_complete_help_subcommand_with_no_command tests/test_completion.py::test_cmd2_help_subcommand_completion_nomatch_scu tests/test_completion.py::test_subcommand_tab_completion_scu tests/test_completion.py::test_subcommand_tab_completion_with_no_completer_scu tests/test_completion.py::test_subcommand_tab_completion_space_in_text_scu
cmd2/cmd2.py cmd2/parsing.py CHANGELOG.md examples/hooks.py
cmd2/cmd2.py cmd2/parsing.py CHANGELOG.md examples/hooks.py