Repository: python-cmd2/cmd2
Pull request #999 (2020-09-11 01:34:44)

Added way to silence alias/macro creation
Added `--silent` flag to `alias/macro create`.
Added `--with_silent` flag to `alias/macro list`.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7938be1..d1f33931 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 1.4.0 (TBD, 2020)
+## 1.3.10 (September 17, 2020)
 * Enhancements
     * Added user-settable option called `always_show_hint`. If True, then tab completion hints will always
     display even when tab completion suggestions print. Arguments whose help or hint text is suppressed will
@@ -7,6 +7,10 @@
     in brackets like it is done in argparse usage text.
     * default category decorators are now heritable by default and will propagate the category down the
     class hierarchy until overridden. There's a new optional flag to set heritable to false.
+    * 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 97378bef..6e1a38ca 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -2734,6 +2734,9 @@ class Cmd(cmd.Cmd):
                            "  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)
@@ -2743,7 +2746,6 @@ class Cmd(cmd.Cmd):
     @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:
@@ -2769,18 +2771,20 @@ class Cmd(cmd.Cmd):
             value += ' ' + ' '.join(args.command_args)
 
         # Set the alias
-        result = "overwritten" if args.name in self.aliases else "created"
+        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
-        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:
@@ -2806,21 +2810,29 @@ class Cmd(cmd.Cmd):
                               "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
@@ -2884,6 +2896,9 @@ class Cmd(cmd.Cmd):
                            "  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)
@@ -2893,7 +2908,6 @@ class Cmd(cmd.Cmd):
     @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:
@@ -2966,17 +2980,19 @@ class Cmd(cmd.Cmd):
                 break
 
         # Set the macro
-        result = "overwritten" if args.name in self.macros else "created"
+        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)
-        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:
@@ -3002,21 +3018,29 @@ class Cmd(cmd.Cmd):
                               "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"""
@@ -3051,12 +3075,12 @@ class Cmd(cmd.Cmd):
 
     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:


Test patch:
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index be6f52d1..1e4f4844 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