.. _decorators: =================== Argument Processing =================== ``cmd2`` makes it easy to add sophisticated argument processing to your commands using the ``argparse`` python module. ``cmd2`` handles the following for you: 1. Parsing input and quoted strings like the Unix shell 2. Parse the resulting argument list using an instance of ``argparse.ArgumentParser`` that you provide 3. Passes the resulting ``argparse.Namespace`` object to your command function. The ``Namespace`` includes the ``Statement`` object that was parsed from the command line. It is stored in the ``__statement__`` attribute of the ``Namespace``. 4. Adds the usage message from the argument parser to your command. 5. Checks if the ``-h/--help`` option is present, and if so, display the help message for the command These features are all provided by the ``@with_argparser`` decorator which is importable from ``cmd2``. See the either the argprint_ or decorator_ example to learn more about how to use the various ``cmd2`` argument processing decorators in your ``cmd2`` applications. .. _argprint: https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py .. _decorator: https://github.com/python-cmd2/cmd2/blob/master/examples/decorator_example.py Decorators provided by cmd2 for argument processing =================================================== ``cmd2`` provides the following decorators for assisting with parsing arguments passed to commands: .. automethod:: cmd2.cmd2.with_argument_list .. automethod:: cmd2.cmd2.with_argparser .. automethod:: cmd2.cmd2.with_argparser_and_unknown_args All of these decorators accept an optional **preserve_quotes** argument which defaults to ``False``. Setting this argument to ``True`` is useful for cases where you are passing the arguments to another command which might have its own argument parsing. Using the argument parser decorator =================================== For each command in the ``cmd2`` subclass which requires argument parsing, create a unique instance of ``argparse.ArgumentParser()`` which can parse the input appropriately for the command. Then decorate the command method with the ``@with_argparser`` decorator, passing the argument parser as the first parameter to the decorator. This changes the second argument to the command method, which will contain the results of ``ArgumentParser.parse_args()``. Here's what it looks like:: import argparse from cmd2 import with_argparser argparser = argparse.ArgumentParser() argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') argparser.add_argument('-r', '--repeat', type=int, help='output [n] times') argparser.add_argument('word', nargs='?', help='word to say') @with_argparser(argparser) def do_speak(self, opts) """Repeats what you tell me to.""" arg = opts.word if opts.piglatin: arg = '%s%say' % (arg[1:], arg[0]) if opts.shout: arg = arg.upper() repetitions = opts.repeat or 1 for i in range(min(repetitions, self.maxrepeats)): self.poutput(arg) .. warning:: It is important that each command which uses the ``@with_argparser`` decorator be passed a unique instance of a parser. This limitation is due to bugs in CPython prior to Python 3.7 which make it impossible to make a deep copy of an instance of a ``argparse.ArgumentParser``. See the table_display_ example for a work-around that demonstrates how to create a function which returns a unique instance of the parser you want. .. note:: The ``@with_argparser`` decorator sets the ``prog`` variable in the argument parser based on the name of the method it is decorating. This will override anything you specify in ``prog`` variable when creating the argument parser. .. _table_display: https://github.com/python-cmd2/cmd2/blob/master/examples/table_display.py Help Messages ============= By default, cmd2 uses the docstring of the command method when a user asks for help on the command. When you use the ``@with_argparser`` decorator, the docstring for the ``do_*`` method is used to set the description for the ``argparse.ArgumentParser``. With this code:: import argparse from cmd2 import with_argparser argparser = argparse.ArgumentParser() argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): """create a html tag""" self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') the ``help tag`` command displays: .. code-block:: none usage: tag [-h] tag content [content ...] create a html tag positional arguments: tag tag content content to surround with tag optional arguments: -h, --help show this help message and exit If you would prefer you can set the ``description`` while instantiating the ``argparse.ArgumentParser`` and leave the docstring on your method empty:: import argparse from cmd2 import with_argparser argparser = argparse.ArgumentParser(description='create an html tag') argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') Now when the user enters ``help tag`` they see: .. code-block:: none usage: tag [-h] tag content [content ...] create an html tag positional arguments: tag tag content content to surround with tag optional arguments: -h, --help show this help message and exit To add additional text to the end of the generated help message, use the ``epilog`` variable:: import argparse from cmd2 import with_argparser argparser = argparse.ArgumentParser(description='create an html tag', epilog='This command can not generate tags with no content, like
.') argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) def do_tag(self, args): self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) self.stdout.write('\n') Which yields: .. code-block:: none usage: tag [-h] tag content [content ...] create an html tag positional arguments: tag tag content content to surround with tag optional arguments: -h, --help show this help message and exit This command can not generate tags with no content, like
.. warning:: If a command **foo** is decorated with one of cmd2's argparse decorators, then **help_foo** will not be invoked when ``help foo`` is called. The argparse_ module provides a rich API which can be used to tweak every aspect of the displayed help and we encourage ``cmd2`` developers to utilize that. .. _argparse: https://docs.python.org/3/library/argparse.html Receiving an argument list ========================== The default behavior of ``cmd2`` is to pass the user input directly to your ``do_*`` methods as a string. The object passed to your method is actually a ``Statement`` object, which has additional attributes that may be helpful, including ``arg_list`` and ``argv``:: class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ def do_say(self, statement): # statement contains a string self.poutput(statement) def do_speak(self, statement): # statement also has a list of arguments # quoted arguments remain quoted for arg in statement.arg_list: self.poutput(arg) def do_articulate(self, statement): # statement.argv contains the command # and the arguments, which have had quotes # stripped for arg in statement.argv: self.poutput(arg) If you don't want to access the additional attributes on the string passed to you``do_*`` method you can still have ``cmd2`` apply shell parsing rules to the user input and pass you a list of arguments instead of a string. Apply the ``@with_argument_list`` decorator to those methods that should receive an argument list instead of a string:: from cmd2 import with_argument_list class CmdLineApp(cmd2.Cmd): """ Example cmd2 application. """ def do_say(self, cmdline): # cmdline contains a string pass @with_argument_list def do_speak(self, arglist): # arglist contains a list of arguments pass Using the argument parser decorator and also receiving a a list of unknown positional arguments =============================================================================================== If you want all unknown arguments to be passed to your command as a list of strings, then decorate the command method with the ``@with_argparser_and_unknown_args`` decorator. Here's what it looks like:: import argparse from cmd2 import with_argparser_and_unknown_args dir_parser = argparse.ArgumentParser() dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line") @with_argparser_and_unknown_args(dir_parser) def do_dir(self, args, unknown): """List contents of current directory.""" # No arguments for this command if unknown: self.perror("dir does not take any positional arguments:", traceback_war=False) self.do_help('dir') self._last_result = CommandResult('', 'Bad arguments') return # Get the contents as a list contents = os.listdir(self.cwd) ... Sub-commands ============ Sub-commands are supported for commands using either the ``@with_argparser`` or ``@with_argparser_and_unknown_args`` decorator. The syntax for supporting them is based on argparse sub-parsers. You may add multiple layers of sub-commands for your command. Cmd2 will automatically traverse and tab-complete sub-commands for all commands using argparse. See the subcommands_ and tab_autocompletion_ example to learn more about how to use sub-commands in your ``cmd2`` application. .. _subcommands: https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py .. _tab_autocompletion: https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py