From cf9516e5d25df5e4e330f75ab0f800ba8b4217c8 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 10 Sep 2020 21:32:56 -0400 Subject: [PATCH 1/2] Added --silent flag to alias/macro create. Added --with_silent flag to alias/macro list. --- CHANGELOG.md | 4 ++++ cmd2/cmd2.py | 48 ++++++++++++++++++++++++++++++++++------------ tests/test_cmd2.py | 30 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df8fae73c..f29e43b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ not display hints even when this setting is True. * argparse tab completion now groups flag names which run the same action. Optional flags are wrapped in brackets like it is done in argparse usage text. + * Added `--silent` flag to `alias/macro create`. If used, then no confirmation message will be printed + when aliases and macros are created or overwritten. + * Added `--with_silent` flag to `alias/macro list`. Use this option when saving to a startup script + that should silently create aliases and macros. * Bug Fixes * Fixed issue where flag names weren't always sorted correctly in argparse tab completion diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 3e9ea9bcb..badae1d8c 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -2729,6 +2729,9 @@ def do_alias(self, args: argparse.Namespace) -> None: " alias create save_results print_results \">\" out.txt\n") alias_create_parser = DEFAULT_ARGUMENT_PARSER(description=alias_create_description, epilog=alias_create_epilog) + alias_create_parser.add_argument('-s', '--silent', action='store_true', + help='do not print message confirming alias was created or\n' + 'overwritten') alias_create_parser.add_argument('name', help='name of this alias') alias_create_parser.add_argument('command', help='what the alias resolves to', choices_method=_get_commands_aliases_and_macros_for_completion) @@ -2738,7 +2741,6 @@ def do_alias(self, args: argparse.Namespace) -> None: @as_subcommand_to('alias', 'create', alias_create_parser, help=alias_create_description.lower()) def _alias_create(self, args: argparse.Namespace) -> None: """Create or overwrite an alias""" - # Validate the alias name valid, errmsg = self.statement_parser.is_valid_command(args.name) if not valid: @@ -2766,16 +2768,18 @@ def _alias_create(self, args: argparse.Namespace) -> None: # Set the alias result = "overwritten" if args.name in self.aliases else "created" self.aliases[args.name] = value - self.poutput("Alias '{}' {}".format(args.name, result)) + + if not args.silent: + self.poutput("Alias '{}' {}".format(args.name, result)) # alias -> delete alias_delete_help = "delete aliases" alias_delete_description = "Delete specified aliases or all aliases if --all is used" alias_delete_parser = DEFAULT_ARGUMENT_PARSER(description=alias_delete_description) + alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases") alias_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to delete', choices_method=_get_alias_completion_items, descriptive_header='Value') - alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases") @as_subcommand_to('alias', 'delete', alias_delete_parser, help=alias_delete_help) def _alias_delete(self, args: argparse.Namespace) -> None: @@ -2801,21 +2805,29 @@ def _alias_delete(self, args: argparse.Namespace) -> None: "Without arguments, all aliases will be listed.") alias_list_parser = DEFAULT_ARGUMENT_PARSER(description=alias_list_description) + alias_list_parser.add_argument('-w', '--with_silent', action='store_true', + help="include --silent flag with listed aliases\n" + "Use this option when saving to a startup script that\n" + "should silently create aliases.") alias_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to list', choices_method=_get_alias_completion_items, descriptive_header='Value') @as_subcommand_to('alias', 'list', alias_list_parser, help=alias_delete_help) def _alias_list(self, args: argparse.Namespace) -> None: """List some or all aliases""" + create_cmd = "alias create" + if args.with_silent: + create_cmd += " --silent" + if args.names: for cur_name in utils.remove_duplicates(args.names): if cur_name in self.aliases: - self.poutput("alias create {} {}".format(cur_name, self.aliases[cur_name])) + self.poutput("{} {} {}".format(create_cmd, cur_name, self.aliases[cur_name])) else: self.perror("Alias '{}' not found".format(cur_name)) else: for cur_alias in sorted(self.aliases, key=self.default_sort_key): - self.poutput("alias create {} {}".format(cur_alias, self.aliases[cur_alias])) + self.poutput("{} {} {}".format(create_cmd, cur_alias, self.aliases[cur_alias])) ############################################################# # Parsers and functions for macro command and subcommands @@ -2879,6 +2891,9 @@ def do_macro(self, args: argparse.Namespace) -> None: " will only complete paths while typing a macro.") macro_create_parser = DEFAULT_ARGUMENT_PARSER(description=macro_create_description, epilog=macro_create_epilog) + macro_create_parser.add_argument('-s', '--silent', action='store_true', + help='do not print message confirming macro was created or\n' + 'overwritten') macro_create_parser.add_argument('name', help='name of this macro') macro_create_parser.add_argument('command', help='what the macro resolves to', choices_method=_get_commands_aliases_and_macros_for_completion) @@ -2888,7 +2903,6 @@ def do_macro(self, args: argparse.Namespace) -> None: @as_subcommand_to('macro', 'create', macro_create_parser, help=macro_create_help) def _macro_create(self, args: argparse.Namespace) -> None: """Create or overwrite a macro""" - # Validate the macro name valid, errmsg = self.statement_parser.is_valid_command(args.name) if not valid: @@ -2963,15 +2977,17 @@ def _macro_create(self, args: argparse.Namespace) -> None: # Set the macro result = "overwritten" if args.name in self.macros else "created" self.macros[args.name] = Macro(name=args.name, value=value, minimum_arg_count=max_arg_num, arg_list=arg_list) - self.poutput("Macro '{}' {}".format(args.name, result)) + + if not args.silent: + self.poutput("Macro '{}' {}".format(args.name, result)) # macro -> delete macro_delete_help = "delete macros" macro_delete_description = "Delete specified macros or all macros if --all is used" macro_delete_parser = DEFAULT_ARGUMENT_PARSER(description=macro_delete_description) + macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros") macro_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to delete', choices_method=_get_macro_completion_items, descriptive_header='Value') - macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros") @as_subcommand_to('macro', 'delete', macro_delete_parser, help=macro_delete_help) def _macro_delete(self, args: argparse.Namespace) -> None: @@ -2997,21 +3013,29 @@ def _macro_delete(self, args: argparse.Namespace) -> None: "Without arguments, all macros will be listed.") macro_list_parser = DEFAULT_ARGUMENT_PARSER(description=macro_list_description) + macro_list_parser.add_argument('-w', '--with_silent', action='store_true', + help="include --silent flag with listed macros\n" + "Use this option when saving to a startup script that\n" + "should silently create macros.") macro_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to list', choices_method=_get_macro_completion_items, descriptive_header='Value') @as_subcommand_to('macro', 'list', macro_list_parser, help=macro_list_help) def _macro_list(self, args: argparse.Namespace) -> None: """List some or all macros""" + create_cmd = "macro create" + if args.with_silent: + create_cmd += " --silent" + if args.names: for cur_name in utils.remove_duplicates(args.names): if cur_name in self.macros: - self.poutput("macro create {} {}".format(cur_name, self.macros[cur_name].value)) + self.poutput("{} {} {}".format(create_cmd, cur_name, self.macros[cur_name].value)) else: self.perror("Macro '{}' not found".format(cur_name)) else: for cur_macro in sorted(self.macros, key=self.default_sort_key): - self.poutput("macro create {} {}".format(cur_macro, self.macros[cur_macro].value)) + self.poutput("{} {} {}".format(create_cmd, cur_macro, self.macros[cur_macro].value)) def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: """Completes the command argument of help""" @@ -3046,12 +3070,12 @@ def complete_help_subcommands(self, text: str, line: str, begidx: int, endidx: i help_parser = DEFAULT_ARGUMENT_PARSER(description="List available commands or provide " "detailed help for a specific command") + help_parser.add_argument('-v', '--verbose', action='store_true', + help="print a list of all commands with descriptions of each") help_parser.add_argument('command', nargs=argparse.OPTIONAL, help="command to retrieve help for", completer_method=complete_help_command) help_parser.add_argument('subcommands', nargs=argparse.REMAINDER, help="subcommand(s) to retrieve help for", completer_method=complete_help_subcommands) - help_parser.add_argument('-v', '--verbose', action='store_true', - help="print a list of all commands with descriptions of each") # Get rid of cmd's complete_help() functions so ArgparseCompleter will complete the help command if getattr(cmd.Cmd, 'complete_help', None) is not None: diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index be6f52d15..1e4f48449 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -1624,6 +1624,21 @@ def test_alias_create(base_app): out, err = run_cmd(base_app, 'alias list fake') assert out == normalize('alias create fake run_pyscript') + # Overwrite alias + out, err = run_cmd(base_app, 'alias create fake help') + assert out == normalize("Alias 'fake' overwritten") + + # Look up the updated alias + out, err = run_cmd(base_app, 'alias list fake') + assert out == normalize('alias create fake help') + + # Test silent flag + out, err = run_cmd(base_app, 'alias create --silent fake set') + assert not out + + out, err = run_cmd(base_app, 'alias list --with_silent fake') + assert out == normalize('alias create --silent fake set') + def test_alias_create_with_quoted_value(base_app): """Demonstrate that quotes in alias value will be preserved (except for redirectors and terminators)""" @@ -1718,6 +1733,21 @@ def test_macro_create(base_app): out, err = run_cmd(base_app, 'macro list fake') assert out == normalize('macro create fake run_pyscript') + # Overwrite macro + out, err = run_cmd(base_app, 'macro create fake help') + assert out == normalize("Macro 'fake' overwritten") + + # Look up the updated macro + out, err = run_cmd(base_app, 'macro list fake') + assert out == normalize('macro create fake help') + + # Test silent flag + out, err = run_cmd(base_app, 'macro create --silent fake set') + assert not out + + out, err = run_cmd(base_app, 'macro list --with_silent fake') + assert out == normalize('macro create --silent fake set') + def test_macro_create_with_quoted_value(base_app): """Demonstrate that quotes in macro value will be preserved (except for redirectors and terminators)""" # Create the macro From 7d5836830d1943af4dfa71ca5b84255c98ef8482 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 17 Sep 2020 20:18:23 -0400 Subject: [PATCH 2/2] Small refactor --- cmd2/cmd2.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 9b561889a..6e1a38cae 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -2771,12 +2771,12 @@ def _alias_create(self, args: argparse.Namespace) -> None: value += ' ' + ' '.join(args.command_args) # Set the alias - result = "overwritten" if args.name in self.aliases else "created" - self.aliases[args.name] = value - if not args.silent: + result = "overwritten" if args.name in self.aliases else "created" self.poutput("Alias '{}' {}".format(args.name, result)) + self.aliases[args.name] = value + # alias -> delete alias_delete_help = "delete aliases" alias_delete_description = "Delete specified aliases or all aliases if --all is used" @@ -2980,12 +2980,12 @@ def _macro_create(self, args: argparse.Namespace) -> None: break # Set the macro - result = "overwritten" if args.name in self.macros else "created" - self.macros[args.name] = Macro(name=args.name, value=value, minimum_arg_count=max_arg_num, arg_list=arg_list) - if not args.silent: + result = "overwritten" if args.name in self.macros else "created" self.poutput("Macro '{}' {}".format(args.name, result)) + self.macros[args.name] = Macro(name=args.name, value=value, minimum_arg_count=max_arg_num, arg_list=arg_list) + # macro -> delete macro_delete_help = "delete macros" macro_delete_description = "Delete specified macros or all macros if --all is used"