diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rwxr-xr-x | README.md | 4 | ||||
-rw-r--r-- | cmd2/__init__.py | 2 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 6 | ||||
-rw-r--r-- | cmd2/cmd2.py | 32 | ||||
-rwxr-xr-x | examples/tab_autocompletion.py | 6 | ||||
-rwxr-xr-x | examples/table_display.py | 5 | ||||
-rw-r--r-- | tests/test_argparse_completer.py | 22 | ||||
-rw-r--r-- | tests/test_argparse_custom.py | 27 |
9 files changed, 52 insertions, 54 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb0dfc2..1c5690b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ * 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) * Completely overhauled the interface for adding tab completion to argparse arguments. See enhancements for more details. - * `ACArgumentParser` is now called `Cmd2ArgParser` + * `ACArgumentParser` is now called `ArgParser` * Moved `basic_complete` to utils.py * Made optional arguments on the following completer methods keyword-only: `delimiter_complete`, `flag_based_complete`, `index_based_complete`. `path_complete`, `shell_cmd_complete` @@ -150,8 +150,8 @@ Instructions for implementing each feature follow. See https://cmd2.readthedocs.io/en/latest/argument_processing.html for more details - NOTE: `cmd2` also provides the `Cmd2ArgParser` customization of `argparse.ArgumentParser` for prettier formatting - of help and RangeAction type + NOTE: `cmd2` also provides the `cmd2.ArgParser` customization of `argparse.ArgumentParser` for prettier formatting + of help and error messages. - `cmd2` applications function like a full-featured shell in many ways (and are cross-platform) - Run arbitrary shell commands by preceding them with `!` or `shell` diff --git a/cmd2/__init__.py b/cmd2/__init__.py index 3b149601..f05e29ec 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -11,7 +11,7 @@ except DistributionNotFound: pass from .ansi import style -from .argparse_custom import Cmd2ArgParser, CompletionItem +from .argparse_custom import ArgParser, CompletionItem from .cmd2 import Cmd, Statement, EmptyStatement, categorize from .cmd2 import with_argument_list, with_argparser, with_argparser_and_unknown_args, with_category from .constants import DEFAULT_SHORTCUTS diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 43099823..e902daf0 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -1,13 +1,13 @@ # coding=utf-8 """ This module adds capabilities to argparse by patching a few of its functions. It also defines a parser -class called Cmd2ArgParser which improves error and help output over normal argparse. All cmd2 code uses +class called ArgParser which improves error and help output over normal argparse. All cmd2 code uses this parser and it is recommended that developers of cmd2-based apps either use it or write their own parser that inherits from it. This will give a consistent look-and-feel between the help/error output of built-in cmd2 commands and the app-specific commands. Since the new capabilities are added by patching at the argparse API level, they are available whether or -not Cmd2ArgParser is used. However, the help output of Cmd2ArgParser is customized to notate nargs ranges +not ArgParser is used. However, the help output of ArgParser is customized to notate nargs ranges whereas any other parser class won't be as explicit in the usage statement. ############################################################################################################ @@ -575,7 +575,7 @@ class Cmd2HelpFormatter(argparse.RawTextHelpFormatter): # noinspection PyCompatibility -class Cmd2ArgParser(argparse.ArgumentParser): +class ArgParser(argparse.ArgumentParser): """Custom ArgumentParser class that improves error and help output""" def __init__(self, *args, **kwargs) -> None: diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index ee40d79d..ee7cc449 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -42,11 +42,11 @@ from collections import namedtuple from contextlib import redirect_stdout from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Type, Union +from . import ArgParser, CompletionItem from . import ansi from . import constants from . import plugin from . import utils -from .argparse_custom import Cmd2ArgParser, CompletionItem from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer from .history import History, HistoryItem from .parsing import StatementParser, Statement, Macro, MacroArg, shlex_split @@ -2334,7 +2334,7 @@ class Cmd(cmd.Cmd): "An alias is a command that enables replacement of a word by another string.") alias_epilog = ("See also:\n" " macro") - alias_parser = Cmd2ArgParser(description=alias_description, epilog=alias_epilog, prog='alias') + alias_parser = ArgParser(description=alias_description, epilog=alias_epilog, prog='alias') # Add sub-commands to alias alias_subparsers = alias_parser.add_subparsers() @@ -2515,7 +2515,7 @@ class Cmd(cmd.Cmd): "A macro is similar to an alias, but it can contain argument placeholders.") macro_epilog = ("See also:\n" " alias") - macro_parser = Cmd2ArgParser(description=macro_description, epilog=macro_epilog, prog='macro') + macro_parser = ArgParser(description=macro_description, epilog=macro_epilog, prog='macro') # Add sub-commands to macro macro_subparsers = macro_parser.add_subparsers() @@ -2647,7 +2647,7 @@ class Cmd(cmd.Cmd): return matches - help_parser = Cmd2ArgParser() + help_parser = ArgParser() help_parser.add_argument('command', nargs=argparse.OPTIONAL, help="command to retrieve help for", completer_method=complete_help_command) help_parser.add_argument('subcommand', nargs=argparse.REMAINDER, help="sub-command to retrieve help for", @@ -2811,19 +2811,19 @@ class Cmd(cmd.Cmd): command = '' self.stdout.write("\n") - @with_argparser(Cmd2ArgParser()) + @with_argparser(ArgParser()) 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)) self.poutput("Shortcuts for other commands:\n{}".format(result)) - @with_argparser(Cmd2ArgParser(epilog=INTERNAL_COMMAND_EPILOG)) + @with_argparser(ArgParser(epilog=INTERNAL_COMMAND_EPILOG)) def do_eof(self, _: argparse.Namespace) -> bool: """Called when <Ctrl>-D is pressed""" # Return True to stop the command loop return True - @with_argparser(Cmd2ArgParser()) + @with_argparser(ArgParser()) def do_quit(self, _: argparse.Namespace) -> bool: """Exit this application""" # Return True to stop the command loop @@ -2918,7 +2918,7 @@ class Cmd(cmd.Cmd): "Accepts abbreviated parameter names so long as there is no ambiguity.\n" "Call without arguments for a list of settable parameters with their values.") - set_parser = Cmd2ArgParser(description=set_description) + set_parser = ArgParser(description=set_description) set_parser.add_argument('-a', '--all', action='store_true', help='display read-only settings as well') set_parser.add_argument('-l', '--long', action='store_true', help='describe function of parameter') set_parser.add_argument('param', nargs=argparse.OPTIONAL, help='parameter to set or view', @@ -2963,7 +2963,7 @@ class Cmd(cmd.Cmd): if onchange_hook is not None: onchange_hook(old=orig_value, new=new_value) - shell_parser = Cmd2ArgParser() + shell_parser = ArgParser() shell_parser.add_argument('command', help='the command to run', completer_method=shell_cmd_complete) shell_parser.add_argument('command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer_method=path_complete) @@ -3026,7 +3026,7 @@ class Cmd(cmd.Cmd): "If you see strange parsing behavior, it's best to just open the Python shell\n" "by providing no arguments to py and run more complex statements there.") - py_parser = Cmd2ArgParser(description=py_description) + py_parser = ArgParser(description=py_description) py_parser.add_argument('command', nargs=argparse.OPTIONAL, help="command to run") py_parser.add_argument('remainder', nargs=argparse.REMAINDER, help="remainder of command") @@ -3212,7 +3212,7 @@ class Cmd(cmd.Cmd): return bridge.stop - run_pyscript_parser = Cmd2ArgParser() + run_pyscript_parser = ArgParser() run_pyscript_parser.add_argument('script_path', help='path to the script file', completer_method=path_complete) run_pyscript_parser.add_argument('script_arguments', nargs=argparse.REMAINDER, help='arguments to pass to script', completer_method=path_complete) @@ -3245,7 +3245,7 @@ class Cmd(cmd.Cmd): # Only include the do_ipy() method if IPython is available on the system if ipython_available: # pragma: no cover - @with_argparser(Cmd2ArgParser()) + @with_argparser(ArgParser()) def do_ipy(self, _: argparse.Namespace) -> None: """Enter an interactive IPython shell""" from .pyscript_bridge import PyscriptBridge @@ -3268,7 +3268,7 @@ class Cmd(cmd.Cmd): history_description = "View, run, edit, save, or clear previously entered commands" - history_parser = Cmd2ArgParser(description=history_description) + history_parser = ArgParser(description=history_description) history_action_group = history_parser.add_mutually_exclusive_group() history_action_group.add_argument('-r', '--run', action='store_true', help='run selected history items') history_action_group.add_argument('-e', '--edit', action='store_true', @@ -3567,7 +3567,7 @@ class Cmd(cmd.Cmd): "\n" " set editor (program-name)") - edit_parser = Cmd2ArgParser(description=edit_description) + edit_parser = ArgParser(description=edit_description) edit_parser.add_argument('file_path', nargs=argparse.OPTIONAL, help="path to a file to open in editor", completer_method=path_complete) @@ -3599,7 +3599,7 @@ class Cmd(cmd.Cmd): "If the -r/--record_transcript flag is used, this command instead records\n" "the output of the script commands to a transcript for testing purposes.\n") - run_script_parser = Cmd2ArgParser(description=run_script_description) + run_script_parser = ArgParser(description=run_script_description) run_script_parser.add_argument('-t', '--transcript', help='record the output of the script as a transcript file', completer_method=path_complete) run_script_parser.add_argument('script_path', help="path to the script file", completer_method=path_complete) @@ -3665,7 +3665,7 @@ class Cmd(cmd.Cmd): relative_run_script_epilog = ("Notes:\n" " This command is intended to only be used within text file scripts.") - relative_run_script_parser = Cmd2ArgParser(description=relative_run_script_description, + relative_run_script_parser = ArgParser(description=relative_run_script_description, epilog=relative_run_script_epilog) relative_run_script_parser.add_argument('file_path', help='a file path pointing to a script') diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py index c4fc6218..2e611b70 100755 --- a/examples/tab_autocompletion.py +++ b/examples/tab_autocompletion.py @@ -8,7 +8,7 @@ import functools from typing import List import cmd2 -from cmd2 import utils, Cmd2ArgParser, CompletionItem +from cmd2 import utils, CompletionItem actors = ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', 'Alec Guinness', 'Peter Mayhew', 'Anthony Daniels', 'Adam Driver', 'Daisy Ridley', 'John Boyega', 'Oscar Isaac', @@ -128,7 +128,7 @@ class TabCompleteExample(cmd2.Cmd): suggest_description = "Suggest command demonstrates argparse customizations.\n" suggest_description += "See hybrid_suggest and orig_suggest to compare the help output." - suggest_parser = Cmd2ArgParser(description=suggest_description) + suggest_parser = cmd2.ArgParser(description=suggest_description) suggest_parser.add_argument('-t', '--type', choices=['movie', 'show'], required=True) suggest_parser.add_argument('-d', '--duration', nargs=(1, 2), action='append', @@ -204,7 +204,7 @@ class TabCompleteExample(cmd2.Cmd): '\n '.join(ep_list))) print() - video_parser = Cmd2ArgParser(prog='media') + video_parser = cmd2.ArgParser(prog='media') video_types_subparsers = video_parser.add_subparsers(title='Media Types', dest='type') diff --git a/examples/table_display.py b/examples/table_display.py index 54d5b7a4..a5a5c830 100755 --- a/examples/table_display.py +++ b/examples/table_display.py @@ -15,7 +15,6 @@ and either the colored or colorama module from typing import Tuple import cmd2 -from cmd2.argparse_custom import Cmd2ArgParser import tableformatter as tf # Configure colors for when users chooses the "-c" flag to enable color in the table output @@ -143,14 +142,14 @@ def high_density_objs(row_obj: CityInfo) -> dict: return opts -def make_table_parser() -> Cmd2ArgParser: +def make_table_parser() -> cmd2.ArgParser: """Create a unique instance of an argparse Argument parser for processing table arguments. NOTE: The two cmd2 argparse decorators require that each parser be unique, even if they are essentially a deep copy of each other. For cases like that, you can create a function to return a unique instance of a parser, which is what is being done here. """ - table_parser = Cmd2ArgParser() + table_parser = cmd2.ArgParser() table_item_group = table_parser.add_mutually_exclusive_group() table_item_group.add_argument('-c', '--color', action='store_true', help='Enable color') table_item_group.add_argument('-f', '--fancy', action='store_true', help='Fancy Grid') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 2a30fc3b..3274ad0c 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -9,7 +9,7 @@ from typing import List import pytest import cmd2 -from cmd2 import with_argparser, Cmd2ArgParser, CompletionItem +from cmd2 import with_argparser, CompletionItem from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester @@ -63,7 +63,7 @@ class AutoCompleteTester(cmd2.Cmd): self.poutput('music create rock') # Top level parser for music command - music_parser = Cmd2ArgParser(description='Manage music', prog='music') + music_parser = cmd2.ArgParser(description='Manage music', prog='music') # Add sub-commands to music music_subparsers = music_parser.add_subparsers() @@ -100,7 +100,7 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Uses default flag prefix value (-) - flag_parser = Cmd2ArgParser() + flag_parser = cmd2.ArgParser() flag_parser.add_argument('-n', '--normal_flag', help='A normal flag', action='store_true') flag_parser.add_argument('-a', '--append_flag', help='Append flag', action='append') flag_parser.add_argument('-o', '--append_const_flag', help='Append const flag', action='append_const', const=True) @@ -113,7 +113,7 @@ class AutoCompleteTester(cmd2.Cmd): pass # Uses non-default flag prefix value (+) - plus_flag_parser = Cmd2ArgParser(prefix_chars='+') + plus_flag_parser = cmd2.ArgParser(prefix_chars='+') plus_flag_parser.add_argument('+n', '++normal_flag', help='A normal flag', action='store_true') @with_argparser(plus_flag_parser) @@ -135,7 +135,7 @@ class AutoCompleteTester(cmd2.Cmd): items.append(CompletionItem(main_str, desc='blah blah')) return items - choices_parser = Cmd2ArgParser() + choices_parser = cmd2.ArgParser() # Flag args for choices command. Include string and non-string arg types. choices_parser.add_argument("-l", "--list", help="a flag populated with a choices list", @@ -168,7 +168,7 @@ class AutoCompleteTester(cmd2.Cmd): """Tab completion method""" return basic_complete(text, line, begidx, endidx, completions_from_method) - completer_parser = Cmd2ArgParser() + completer_parser = cmd2.ArgParser() # Flag args for completer command completer_parser.add_argument("-f", "--function", help="a flag using a completer function", @@ -189,7 +189,7 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to nargs ############################################################################################################ - nargs_parser = Cmd2ArgParser() + nargs_parser = cmd2.ArgParser() # Flag args for nargs command nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, @@ -215,7 +215,7 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to testing tab hints ############################################################################################################ - hint_parser = Cmd2ArgParser() + hint_parser = cmd2.ArgParser() hint_parser.add_argument('-f', '--flag', help='a flag arg') hint_parser.add_argument('-s', '--suppressed_help', help=argparse.SUPPRESS) hint_parser.add_argument('-t', '--suppressed_hint', help='a flag arg', suppress_tab_hint=True) @@ -652,7 +652,7 @@ Hint: def test_starts_like_flag(): from cmd2.argparse_completer import starts_like_flag - parser = Cmd2ArgParser() + parser = cmd2.ArgParser() # Does not start like a flag assert not starts_like_flag('', parser) @@ -670,7 +670,7 @@ def test_starts_like_flag(): def test_complete_command_no_tokens(ac_app): from cmd2.argparse_completer import AutoCompleter - parser = Cmd2ArgParser() + parser = cmd2.ArgParser() ac = AutoCompleter(parser, ac_app) completions = ac.complete_command(tokens=[], text='', line='', begidx=0, endidx=0) @@ -680,7 +680,7 @@ def test_complete_command_no_tokens(ac_app): def test_complete_command_help_no_tokens(ac_app): from cmd2.argparse_completer import AutoCompleter - parser = Cmd2ArgParser() + parser = cmd2.ArgParser() ac = AutoCompleter(parser, ac_app) completions = ac.complete_command_help(tokens=[], text='', line='', begidx=0, endidx=0) diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 35d97974..b738efa3 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -7,7 +7,6 @@ import argparse import pytest import cmd2 -from cmd2.argparse_custom import Cmd2ArgParser from .conftest import run_cmd @@ -16,7 +15,7 @@ class ApCustomTestApp(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - range_parser = Cmd2ArgParser() + range_parser = cmd2.ArgParser() range_parser.add_argument('--arg1', nargs=(2, 3)) range_parser.add_argument('--arg2', nargs=argparse.ZERO_OR_MORE) range_parser.add_argument('--arg3', nargs=argparse.ONE_OR_MORE) @@ -47,7 +46,7 @@ def fake_func(): ({'choices_method': fake_func, 'completer_method': fake_func}, False), ]) def test_apcustom_invalid_args(args, is_valid): - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') try: parser.add_argument('name', **args) assert is_valid @@ -58,7 +57,7 @@ def test_apcustom_invalid_args(args, is_valid): def test_apcustom_usage(): usage = "A custom usage statement" - parser = Cmd2ArgParser(usage=usage) + parser = cmd2.ArgParser(usage=usage) help = parser.format_help() assert usage in help @@ -76,46 +75,46 @@ def test_apcustom_nargs_not_enough(cust_app): def test_apcustom_narg_empty_tuple(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=()) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_apcustom_narg_single_tuple(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1,)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_apcustom_narg_tuple_triple(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_apcustom_narg_tuple_order(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(2, 1)) assert 'Invalid nargs range. The first value must be less than the second' in str(excinfo.value) def test_apcustom_narg_tuple_negative(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(-1, 1)) assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) def test_apcustom_narg_tuple_zero_base(): - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 3)) def test_apcustom_narg_tuple_zero_to_one(): - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 1)) @@ -124,13 +123,13 @@ def test_apcustom_print_message(capsys): test_message = 'The test message' # Specify the file - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser._print_message(test_message, file=sys.stdout) out, err = capsys.readouterr() assert test_message in out # Make sure file defaults to sys.stderr - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser._print_message(test_message) out, err = capsys.readouterr() assert test_message in err @@ -138,7 +137,7 @@ def test_apcustom_print_message(capsys): def test_apcustom_required_options(): # Make sure a 'required arguments' section shows when a flag is marked required - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('--required_flag', required=True) help = parser.format_help() |