diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50d72395..57237b47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
     * Added `set_choices_function()`, `set_choices_method()`, `set_completer_function()`, and `set_completer_method()`
     to support cases where this functionality needs to be added to an argparse action outside of the normal
     `parser.add_argument()` call.
+* Breaking Changes
+    * Aliases and macros can no longer have the same name as a command
 
 ## 0.9.15 (July 24, 2019)
 * Bug Fixes
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 514d8a05..d82e58c9 100755
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -1672,13 +1672,10 @@ class Cmd(cmd.Cmd):
         statement = self.statement_parser.parse_command_only(line)
         return statement.command, statement.args, statement.command_and_args
 
-    def onecmd_plus_hooks(self, line: str, *, expand: bool = True, add_to_history: bool = True,
-                          py_bridge_call: bool = False) -> bool:
+    def onecmd_plus_hooks(self, line: str, *, add_to_history: bool = True, py_bridge_call: bool = False) -> bool:
         """Top-level function called by cmdloop() to handle parsing a line and running the command and all of its hooks.
 
         :param line: command line to run
-        :param expand: If True, then aliases, macros, and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :param add_to_history: If True, then add this command to history. Defaults to True.
         :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
@@ -1689,7 +1686,7 @@ class Cmd(cmd.Cmd):
 
         stop = False
         try:
-            statement = self._input_line_to_statement(line, expand=expand)
+            statement = self._input_line_to_statement(line)
         except EmptyStatement:
             return self._run_cmdfinalization_hooks(stop, None)
         except ValueError as ex:
@@ -1804,15 +1801,12 @@ class Cmd(cmd.Cmd):
         except Exception as ex:
             self.pexcept(ex)
 
-    def runcmds_plus_hooks(self, cmds: List[Union[HistoryItem, str]], *,
-                           expand: bool = True, add_to_history: bool = True) -> bool:
+    def runcmds_plus_hooks(self, cmds: List[Union[HistoryItem, str]], *, add_to_history: bool = True) -> bool:
         """
         Used when commands are being run in an automated fashion like text scripts or history replays.
         The prompt and command line for each command will be printed if echo is True.
 
         :param cmds: commands to run
-        :param expand: If True, then aliases, macros, and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :param add_to_history: If True, then add these commands to history. Defaults to True.
         :return: True if running of commands should stop
         """
@@ -1823,12 +1817,12 @@ class Cmd(cmd.Cmd):
             if self.echo:
                 self.poutput('{}{}'.format(self.prompt, line))
 
-            if self.onecmd_plus_hooks(line, expand=expand, add_to_history=add_to_history):
+            if self.onecmd_plus_hooks(line, add_to_history=add_to_history):
                 return True
 
         return False
 
-    def _complete_statement(self, line: str, *, expand: bool = True) -> Statement:
+    def _complete_statement(self, line: str) -> Statement:
         """Keep accepting lines of input until the command is complete.
 
         There is some pretty hacky code here to handle some quirks of
@@ -1837,13 +1831,11 @@ class Cmd(cmd.Cmd):
         backwards compatibility with the standard library version of cmd.
 
         :param line: the line being parsed
-        :param expand: If True, then aliases and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :return: the completed Statement
         """
         while True:
             try:
-                statement = self.statement_parser.parse(line, expand=expand)
+                statement = self.statement_parser.parse(line)
                 if statement.multiline_command and statement.terminator:
                     # we have a completed multiline command, we are done
                     break
@@ -1854,7 +1846,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, expand=expand)
+                statement = self.statement_parser.parse_command_only(line)
                 if not statement.multiline_command:
                     # not a multiline command, so raise the exception
                     raise
@@ -1891,13 +1883,11 @@ class Cmd(cmd.Cmd):
             raise EmptyStatement()
         return statement
 
-    def _input_line_to_statement(self, line: str, *, expand: bool = True) -> Statement:
+    def _input_line_to_statement(self, line: str) -> Statement:
         """
         Parse the user's input line and convert it to a Statement, ensuring that all macros are also resolved
 
         :param line: the line being parsed
-        :param expand: If True, then aliases, macros, and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :return: parsed command line as a Statement
         """
         used_macros = []
@@ -1906,14 +1896,14 @@ class Cmd(cmd.Cmd):
         # Continue until all macros are resolved
         while True:
             # Make sure all input has been read and convert it to a Statement
-            statement = self._complete_statement(line, expand=expand)
+            statement = self._complete_statement(line)
 
             # Save the fully entered line if this is the first loop iteration
             if orig_line is None:
                 orig_line = statement.raw
 
             # Check if this command matches a macro and wasn't already processed to avoid an infinite loop
-            if expand and statement.command in self.macros.keys() and statement.command not in used_macros:
+            if statement.command in self.macros.keys() and statement.command not in used_macros:
                 used_macros.append(statement.command)
                 line = self._resolve_macro(statement)
                 if line is None:
@@ -2127,22 +2117,19 @@ class Cmd(cmd.Cmd):
         return target if callable(getattr(self, target, None)) else ''
 
     # noinspection PyMethodOverriding
-    def onecmd(self, statement: Union[Statement, str], *,
-               expand: bool = True, add_to_history: bool = True) -> bool:
+    def onecmd(self, statement: Union[Statement, str], *, add_to_history: bool = True) -> bool:
         """ This executes the actual do_* method for a command.
 
         If the command provided doesn't exist, then it executes default() instead.
 
         :param statement: intended to be a Statement instance parsed command from the input stream, alternative
                           acceptance of a str is present only for backward compatibility with cmd
-        :param expand: If True, then aliases, macros, and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :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, expand=expand)
+            statement = self._input_line_to_statement(statement)
 
         func = self.cmd_func(statement.command)
         if func:
@@ -2331,6 +2318,10 @@ class Cmd(cmd.Cmd):
             self.perror("Invalid alias name: {}".format(errmsg))
             return
 
+        if args.name in self.get_all_commands():
+            self.perror("Alias cannot have the same name as a command")
+            return
+
         if args.name in self.macros:
             self.perror("Alias cannot have the same name as a macro")
             return
@@ -2460,8 +2451,8 @@ class Cmd(cmd.Cmd):
             self.perror("Invalid macro name: {}".format(errmsg))
             return
 
-        if args.name in self.statement_parser.multiline_commands:
-            self.perror("Macro cannot have the same name as a multiline command")
+        if args.name in self.get_all_commands():
+            self.perror("Macro cannot have the same name as a command")
             return
 
         if args.name in self.aliases:
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index 84b2468e..4e690b0b 100755
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -356,20 +356,17 @@ class StatementParser:
                 errmsg = ''
         return valid, errmsg
 
-    def tokenize(self, line: str, *, expand: bool = True) -> List[str]:
+    def tokenize(self, line: str) -> List[str]:
         """
         Lex a string into a list of tokens. Shortcuts and aliases are expanded and comments are removed
 
         :param line: the command line being lexed
-        :param expand: If True, then aliases and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :return: A list of tokens
         :raises ValueError if there are unclosed quotation marks.
         """
 
         # expand shortcuts and aliases
-        if expand:
-            line = self._expand(line)
+        line = self._expand(line)
 
         # check if this line is a comment
         if line.lstrip().startswith(constants.COMMENT_CHAR):
@@ -382,15 +379,13 @@ class StatementParser:
         tokens = self.split_on_punctuation(tokens)
         return tokens
 
-    def parse(self, line: str, *, expand: bool = True) -> Statement:
+    def parse(self, line: str) -> Statement:
         """
         Tokenize the input and parse it into a Statement object, stripping
         comments, expanding aliases and shortcuts, and extracting output
         redirection directives.
 
         :param line: the command line being parsed
-        :param expand: If True, then aliases and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :return: the created Statement
         :raises ValueError if there are unclosed quotation marks
         """
@@ -407,7 +402,7 @@ class StatementParser:
         arg_list = []
 
         # lex the input into a list of tokens
-        tokens = self.tokenize(line, expand=expand)
+        tokens = self.tokenize(line)
 
         # of the valid terminators, find the first one to occur in the input
         terminator_pos = len(tokens) + 1
@@ -529,7 +524,7 @@ class StatementParser:
                               output_to=output_to)
         return statement
 
-    def parse_command_only(self, rawinput: str, *, expand: bool = True) -> Statement:
+    def parse_command_only(self, rawinput: str) -> Statement:
         """Partially parse input into a Statement object.
 
         The command is identified, and shortcuts and aliases are expanded.
@@ -554,15 +549,12 @@ class StatementParser:
         whitespace.
 
         :param rawinput: the command line as entered by the user
-        :param expand: If True, then aliases and shortcuts will be expanded.
-                       Set this to False if the command token should not be altered. Defaults to True.
         :return: the created Statement
         """
         line = rawinput
 
         # expand shortcuts and aliases
-        if expand:
-            line = self._expand(rawinput)
+        line = self._expand(rawinput)
 
         command = ''
         args = ''
@@ -616,7 +608,7 @@ class StatementParser:
         """
         # Check if to_parse needs to be converted to a Statement
         if not isinstance(to_parse, Statement):
-            to_parse = self.parse(command_name + ' ' + to_parse, expand=False)
+            to_parse = self.parse(command_name + ' ' + to_parse)
 
         if preserve_quotes:
             return to_parse, to_parse.arg_list
diff --git a/docs/features/shortcuts_aliases_macros.rst b/docs/features/shortcuts_aliases_macros.rst
index 0f06fe08..5628dc66 100644
--- a/docs/features/shortcuts_aliases_macros.rst
+++ b/docs/features/shortcuts_aliases_macros.rst
@@ -65,6 +65,7 @@ Use ``alias delete`` to remove aliases
 
 For more details run: ``help alias delete``
 
+Note: Aliases cannot have the same name as a command or macro
 
 Macros
 ------
@@ -93,3 +94,5 @@ sessions.
 For more details on listing macros run: ``help macro list``
 
 For more details on deleting macros run: ``help macro delete``
+
+Note: Macros cannot have the same name as a command or alias
\ No newline at end of file

Alias and Macro names

It has been brought to my attention by another developer that allowing aliases and macros to share names with commands could cause issues with users being unwise. Specifically he said:

> being able to create an alias that overrides/hides a command is bad for usability

Currently we have the following restrictions:

1. Alias cannot share its name with a macro
2. Macro cannot share its name with an alias or a multiline command

When I first wrote aliases, I did prevent them from sharing their name with a command. Then I decided to remove this restriction to be like bash. However, in bash you can still call the original command if you know its path. (e.g. /bin/ls). **cmd2** does not work in the same way since our commands are not executable files somewhere on disk.

I'm OK adding the restriction back, but I would like some consensus on the issue. And I really don't want the answer to be, "add a flag".

@kotfu @tleonhardt @anselor What are your thoughts?

diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 8389b9ed..1ba10f6b 100755
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -1625,6 +1625,10 @@ def test_alias_create_invalid_name(base_app, alias_name, capsys):
     out, err = run_cmd(base_app, 'alias create {} help'.format(alias_name))
     assert "Invalid alias name" in err[0]
 
+def test_alias_create_with_command_name(base_app):
+    out, err = run_cmd(base_app, 'alias create help stuff')
+    assert "Alias cannot have the same name as a command" in err[0]
+
 def test_alias_create_with_macro_name(base_app):
     macro = "my_macro"
     run_cmd(base_app, 'macro create {} help'.format(macro))
@@ -1713,19 +1717,16 @@ def test_macro_create_invalid_name(base_app, macro_name):
     out, err = run_cmd(base_app, 'macro create {} help'.format(macro_name))
     assert "Invalid macro name" in err[0]
 
+def test_macro_create_with_command_name(base_app):
+    out, err = run_cmd(base_app, 'macro create help stuff')
+    assert "Macro cannot have the same name as a command" in err[0]
+
 def test_macro_create_with_alias_name(base_app):
     macro = "my_macro"
     run_cmd(base_app, 'alias create {} help'.format(macro))
     out, err = run_cmd(base_app, 'macro create {} help'.format(macro))
     assert "Macro cannot have the same name as an alias" in err[0]
 
-def test_macro_create_with_command_name(multiline_app):
-    out, err = run_cmd(multiline_app, 'macro create help stuff')
-    assert out == normalize("Macro 'help' created")
-
-    out, err = run_cmd(multiline_app, 'macro create orate stuff')
-    assert "Macro cannot have the same name as a multiline command" in err[0]
-
 def test_macro_create_with_args(base_app):
     # Create the macro
     out, err = run_cmd(base_app, 'macro create fake {1} {2}')
@@ -1843,37 +1844,6 @@ def test_nonexistent_macro(base_app):
 
     assert exception is not None
 
-def test_input_line_to_statement_expand(base_app):
-    # Enable/Disable expansion of shortcuts
-    line = '!ls'
-    statement = base_app._input_line_to_statement(line, expand=True)
-    assert statement.command == 'shell'
-
-    statement = base_app._input_line_to_statement(line, expand=False)
-    assert statement.command == '!ls'
-
-    # Enable/Disable expansion of aliases
-    run_cmd(base_app, 'alias create help macro')
-
-    line = 'help'
-    statement = base_app._input_line_to_statement(line, expand=True)
-    assert statement.command == 'macro'
-
-    statement = base_app._input_line_to_statement(line, expand=False)
-    assert statement.command == 'help'
-
-    run_cmd(base_app, 'alias delete help')
-
-    # Enable/Disable expansion of macros
-    run_cmd(base_app, 'macro create help alias')
-
-    line = 'help'
-    statement = base_app._input_line_to_statement(line, expand=True)
-    assert statement.command == 'alias'
-
-    statement = base_app._input_line_to_statement(line, expand=False)
-    assert statement.command == 'help'
-
 def test_ppaged(outsim_app):
     msg = 'testing...'
     end = '\n'
diff --git a/tests/test_parsing.py b/tests/test_parsing.py
index ac540183..2114bfaa 100755
--- a/tests/test_parsing.py
+++ b/tests/test_parsing.py
@@ -604,19 +604,11 @@ def test_empty_statement_raises_exception():
     ('l', 'shell', 'ls -al')
 ])
 def test_parse_alias_and_shortcut_expansion(parser, line, command, args):
-    # Test first with expansion
     statement = parser.parse(line)
     assert statement.command == command
     assert statement == args
     assert statement.args == statement
 
-    # Now allow no expansion
-    tokens = shlex_split(line)
-    statement = parser.parse(line, expand=False)
-    assert statement.command == tokens[0]
-    assert shlex_split(statement) == tokens[1:]
-    assert statement.args == statement
-
 def test_parse_alias_on_multiline_command(parser):
     line = 'anothermultiline has > inside an unfinished command'
     statement = parser.parse(line)

pip install -e .[dev]
parse_log_pytest
Pipfile
pytest pytest-cov pytest-mock
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
anyio==4.9.0
attrs==25.3.0
babel==2.17.0
backports.tarfile==1.2.0
cachetools==5.5.2
certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
-e git+https://github.com/python-cmd2/cmd2.git@0f9fcfcd95ee7fd30ebddabbf2068d8ddb725364#egg=cmd2
codecov==2.1.13
colorama==0.4.6
coverage==7.8.0
cryptography==44.0.2
distlib==0.3.9
doc8==1.1.2
docutils==0.21.2
exceptiongroup==1.2.2
filelock==3.18.0
flake8==7.2.0
h11==0.14.0
id==1.5.0
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
invoke==2.2.0
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.6.0
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mccabe==0.7.0
mdurl==0.1.2
more-itertools==10.6.0
nh3==0.2.21
packaging==24.2
pbr==6.1.1
pipfile==0.0.2
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pycparser==2.22
pyflakes==3.3.1
Pygments==2.19.1
pyperclip==1.9.0
pyproject-api==1.9.0
pytest==8.3.5
pytest-cov==6.0.0
pytest-mock==3.14.0
readme_renderer==44.0
requests==2.32.3
requests-toolbelt==1.0.0
restructuredtext_lint==1.4.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
sniffio==1.3.1
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinx-autobuild==2024.10.3
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
starlette==0.46.1
stevedore==5.4.1
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==2.2.1
tox==4.25.0
twine==6.1.0
typing_extensions==4.13.0
urllib3==2.3.0
uvicorn==0.34.0
virtualenv==20.29.3
watchfiles==1.0.4
wcwidth==0.2.13
websockets==15.0.1
zipp==3.21.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
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - pipfile=0.0.2=py_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - anyio==4.9.0
      - attrs==25.3.0
      - babel==2.17.0
      - backports-tarfile==1.2.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - click==8.1.8
      - codecov==2.1.13
      - colorama==0.4.6
      - coverage==7.8.0
      - cryptography==44.0.2
      - distlib==0.3.9
      - doc8==1.1.2
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - filelock==3.18.0
      - flake8==7.2.0
      - h11==0.14.0
      - id==1.5.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - invoke==2.2.0
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.6.0
      - markdown-it-py==3.0.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mdurl==0.1.2
      - more-itertools==10.6.0
      - nh3==0.2.21
      - packaging==24.2
      - pbr==6.1.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pycparser==2.22
      - pyflakes==3.3.1
      - pygments==2.19.1
      - pyperclip==1.9.0
      - pyproject-api==1.9.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - restructuredtext-lint==1.4.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - sniffio==1.3.1
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinx-autobuild==2024.10.3
      - sphinx-rtd-theme==3.0.2
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - starlette==0.46.1
      - stevedore==5.4.1
      - tomli==2.2.1
      - tox==4.25.0
      - twine==6.1.0
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - uvicorn==0.34.0
      - virtualenv==20.29.3
      - watchfiles==1.0.4
      - wcwidth==0.2.13
      - websockets==15.0.1
      - zipp==3.21.0
prefix: /opt/conda/envs/cmd2

tests/test_cmd2.py::test_alias_create_with_command_name tests/test_cmd2.py::test_macro_create_with_command_name
tests/test_cmd2.py::test_not_in_main_thread tests/test_cmd2.py::test_which_editor_good
tests/test_cmd2.py::test_version 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_base_show_readonly 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_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_python_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_with_odd_file_names 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_disallow_redirection 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_odd_file_names 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_completion_items tests/test_cmd2.py::test_get_macro_completion_items tests/test_cmd2.py::test_get_settable_completion_items 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_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_onecmd_add_to_history 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_parsing.py::test_parse_empty_string tests/test_parsing.py::test_parse_empty_string_default tests/test_parsing.py::test_tokenize_default[command-tokens0] tests/test_parsing.py::test_tokenize_default[#comment-tokens1] tests/test_parsing.py::test_tokenize_default[not tests/test_parsing.py::test_tokenize_default[termbare tests/test_parsing.py::test_tokenize_default[termbare; tests/test_parsing.py::test_tokenize_default[termbare& tests/test_parsing.py::test_tokenize_default[help|less-tokens7] tests/test_parsing.py::test_tokenize[command-tokens0] tests/test_parsing.py::test_tokenize[# tests/test_parsing.py::test_tokenize[not tests/test_parsing.py::test_tokenize[42 tests/test_parsing.py::test_tokenize[l-tokens4] tests/test_parsing.py::test_tokenize[termbare tests/test_parsing.py::test_tokenize[termbare; tests/test_parsing.py::test_tokenize[termbare& tests/test_parsing.py::test_tokenize[help|less-tokens9] tests/test_parsing.py::test_tokenize[l|less-tokens10] tests/test_parsing.py::test_tokenize_unclosed_quotes tests/test_parsing.py::test_command_and_args[tokens0--] tests/test_parsing.py::test_command_and_args[tokens1-command-] tests/test_parsing.py::test_command_and_args[tokens2-command-arg1 tests/test_parsing.py::test_parse_single_word[plainword] tests/test_parsing.py::test_parse_single_word["one tests/test_parsing.py::test_parse_single_word['one tests/test_parsing.py::test_parse_word_plus_terminator[termbare;-;] tests/test_parsing.py::test_parse_word_plus_terminator[termbare tests/test_parsing.py::test_parse_word_plus_terminator[termbare&-&] tests/test_parsing.py::test_parse_suffix_after_terminator[termbare; tests/test_parsing.py::test_parse_suffix_after_terminator[termbare tests/test_parsing.py::test_parse_suffix_after_terminator[termbare& tests/test_parsing.py::test_parse_command_with_args tests/test_parsing.py::test_parse_command_with_quoted_args tests/test_parsing.py::test_parse_command_with_args_terminator_and_suffix tests/test_parsing.py::test_parse_comment tests/test_parsing.py::test_parse_embedded_comment_char tests/test_parsing.py::test_parse_simple_pipe[simple tests/test_parsing.py::test_parse_simple_pipe[simple|piped] tests/test_parsing.py::test_parse_double_pipe_is_not_a_pipe tests/test_parsing.py::test_parse_complex_pipe tests/test_parsing.py::test_parse_redirect[help tests/test_parsing.py::test_parse_redirect[help>out.txt->] tests/test_parsing.py::test_parse_redirect[help>>out.txt->>] tests/test_parsing.py::test_parse_redirect_with_args tests/test_parsing.py::test_parse_redirect_with_dash_in_path tests/test_parsing.py::test_parse_redirect_append tests/test_parsing.py::test_parse_pipe_then_redirect tests/test_parsing.py::test_parse_multiple_pipes tests/test_parsing.py::test_redirect_then_pipe tests/test_parsing.py::test_append_then_pipe tests/test_parsing.py::test_append_then_redirect tests/test_parsing.py::test_redirect_then_append tests/test_parsing.py::test_redirect_to_quoted_string tests/test_parsing.py::test_redirect_to_single_quoted_string tests/test_parsing.py::test_redirect_to_empty_quoted_string tests/test_parsing.py::test_redirect_to_empty_single_quoted_string tests/test_parsing.py::test_parse_output_to_paste_buffer tests/test_parsing.py::test_parse_redirect_inside_terminator tests/test_parsing.py::test_parse_multiple_terminators[multiline tests/test_parsing.py::test_parse_unfinished_multiliine_command tests/test_parsing.py::test_parse_basic_multiline_command tests/test_parsing.py::test_parse_multiline_command_ignores_redirectors_within_it[multiline tests/test_parsing.py::test_parse_multiline_terminated_by_empty_line tests/test_parsing.py::test_parse_multiline_with_embedded_newline[multiline tests/test_parsing.py::test_parse_multiline_ignores_terminators_in_quotes tests/test_parsing.py::test_parse_command_with_unicode_args tests/test_parsing.py::test_parse_unicode_command tests/test_parsing.py::test_parse_redirect_to_unicode_filename tests/test_parsing.py::test_parse_unclosed_quotes tests/test_parsing.py::test_empty_statement_raises_exception tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[helpalias-help-] tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[helpalias tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[42-theanswer-] tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[42 tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[!ls-shell-ls] tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[!ls tests/test_parsing.py::test_parse_alias_and_shortcut_expansion[l-shell-ls tests/test_parsing.py::test_parse_alias_on_multiline_command tests/test_parsing.py::test_parse_alias_redirection[helpalias tests/test_parsing.py::test_parse_alias_redirection[helpalias>out.txt->] tests/test_parsing.py::test_parse_alias_redirection[helpalias>>out.txt->>] tests/test_parsing.py::test_parse_alias_pipe[helpalias tests/test_parsing.py::test_parse_alias_pipe[helpalias|less] tests/test_parsing.py::test_parse_alias_terminator_no_whitespace[helpalias;] tests/test_parsing.py::test_parse_alias_terminator_no_whitespace[helpalias;;] tests/test_parsing.py::test_parse_alias_terminator_no_whitespace[helpalias;; tests/test_parsing.py::test_parse_alias_terminator_no_whitespace[helpalias tests/test_parsing.py::test_parse_command_only_command_and_args tests/test_parsing.py::test_parse_command_only_strips_line tests/test_parsing.py::test_parse_command_only_expands_alias tests/test_parsing.py::test_parse_command_only_expands_shortcuts tests/test_parsing.py::test_parse_command_only_quoted_args tests/test_parsing.py::test_parse_command_only_specialchars[helpalias tests/test_parsing.py::test_parse_command_only_specialchars[helpalias>out.txt->out.txt] tests/test_parsing.py::test_parse_command_only_specialchars[helpalias>>out.txt->>out.txt] tests/test_parsing.py::test_parse_command_only_specialchars[help|less-|less] tests/test_parsing.py::test_parse_command_only_specialchars[helpalias;-;] tests/test_parsing.py::test_parse_command_only_specialchars[help tests/test_parsing.py::test_parse_command_only_specialchars[help; tests/test_parsing.py::test_parse_command_only_empty[] tests/test_parsing.py::test_parse_command_only_empty[;] tests/test_parsing.py::test_parse_command_only_empty[;;] tests/test_parsing.py::test_parse_command_only_empty[;; tests/test_parsing.py::test_parse_command_only_empty[&] tests/test_parsing.py::test_parse_command_only_empty[& tests/test_parsing.py::test_parse_command_only_empty[ tests/test_parsing.py::test_parse_command_only_empty[>] tests/test_parsing.py::test_parse_command_only_empty['] tests/test_parsing.py::test_parse_command_only_empty["] tests/test_parsing.py::test_parse_command_only_empty[|] tests/test_parsing.py::test_parse_command_only_multiline tests/test_parsing.py::test_statement_initialization tests/test_parsing.py::test_statement_is_immutable tests/test_parsing.py::test_is_valid_command_invalid tests/test_parsing.py::test_is_valid_command_valid tests/test_parsing.py::test_macro_normal_arg_pattern tests/test_parsing.py::test_macro_escaped_arg_pattern
cmd2/cmd2.py cmd2/parsing.py docs/features/shortcuts_aliases_macros.rst CHANGELOG.md
cmd2/cmd2.py cmd2/parsing.py docs/features/shortcuts_aliases_macros.rst CHANGELOG.md