diff options
author | kotfu <kotfu@kotfu.net> | 2018-01-07 20:19:19 -0700 |
---|---|---|
committer | kotfu <kotfu@kotfu.net> | 2018-01-07 20:19:19 -0700 |
commit | 17d662b8a05febeda1320ea259a49d920350dad5 (patch) | |
tree | bb037cfd31ae25709643de4149dd92850b66c30a | |
parent | 5a28a87769e23578993c52cf9c5df1c9d9bb7f35 (diff) | |
download | cmd2-git-17d662b8a05febeda1320ea259a49d920350dad5.tar.gz |
Write documentation
-rw-r--r-- | docs/argument_processing.rst | 258 | ||||
-rw-r--r-- | docs/index.rst | 2 |
2 files changed, 147 insertions, 113 deletions
diff --git a/docs/argument_processing.rst b/docs/argument_processing.rst index 279dbe47..79c19d19 100644 --- a/docs/argument_processing.rst +++ b/docs/argument_processing.rst @@ -2,140 +2,174 @@ Argument Processing =================== -cmd2 currently includes code which makes it easier to add arguments to the -commands in your cmd2 subclass. This support utilizes the optparse library, -which has been deprecated since Python 2.7 (released on July 3rd 2010) and -Python 3.2 (released on February 20th, 2011). Optparse is still included in the -python standard library, but the documentation recommends using argparse -instead. It's time to modernize cmd2 to utilize argparse. - -I don't believe there is a way to change cmd2 to use argparse instead of -optparse without requiring subclasses of cmd2 to make some change. The long -recomended way to use optparse in cmd2 includes the use of the -optparse.make_option, which must be changed in order to use argparse. - -There are two potential ways to use argument parsing with cmd2: to parse -options given at the shell prompt when invoked, and to parse options given at -the cmd2 prompt prior to executing a command. - -optparse example -================ - -Here's an example of the current optparse support: - - opts = [make_option('-p', '--piglatin', action="store_true", help="atinLay"), - make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"), - make_option('-r', '--repeat', type="int", help="output [n] times")] - - @options(opts, arg_desc='(text to say)') - def do_speak(self, arg, opts=None): - """Repeats what you tell me to.""" - arg = ''.join(arg) - 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) +``cmd2`` makes it easy to add sophisticated argument processing to your commands using the ``argparse`` python module. ``cmd2`` handles the following for you: -The current optparse decorator performs the following key functions 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 +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 -1. Use `shlex` to split the arguments entered by the user. -2. Parse the arguments using the given optparse options. -3. Replace the `__doc__` string of the decorated function (i.e. do_speak) with -the help string generated by optparse. -4. Call the decorated function (i.e. do_speak) passing an additional parameter -which contains the parsed options. - -Here are several options for replacing this functionality with argparse. +These features are all provided by the ``@with_argument_parser`` decorator. +Using the decorator +=================== -No cmd2 support -=============== +For each command in the ``cmd2`` subclass which requires argument parsing, +create an instance of ``argparse.ArgumentParser()`` which can parse the +input appropriately for the command. Then decorate the command method with +the ``@with_argument_parser`` decorator, passing the argument parser as the +first parameter to the decorator. Add a third variable to the command method, which will contain the results of ``ArgumentParser.parse_args()``. -The easiest option would be to just remove the cmd2 specific support for -argument parsing. The above example would then look something like this: +Here's what it looks like:: - argparser = argparse.ArgumentParser( - prog='speak', - description='Repeats what you tell me to' - ) + 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('-r', '--repeat', type=int, help='output [n] times') argparser.add_argument('word', nargs='?', help='word to say') - def do_speak(self, argv) - """Repeats what you tell me to.""" - opts = argparser.parse_args(shlex.split(argv, posix=POSIX_SHLEX)) - arg = opts.word - if opts.piglatin: + @with_argument_parser(argparser) + def do_speak(self, argv, opts) + """Repeats what you tell me to.""" + arg = opts.word + if opts.piglatin: arg = '%s%say' % (arg[1:], arg[0]) - if opts.shout: + if opts.shout: arg = arg.upper() - repetitions = opts.repeat or 1 - for i in range(min(repetitions, self.maxrepeats)): + repetitions = opts.repeat or 1 + for i in range(min(repetitions, self.maxrepeats)): self.poutput(arg) -Using shlex in this example is technically not necessary because the `do_speak` -command only expects a single word argument. It is included here to show what -would be required to replicate the current optparse based functionality. +.. note:: + The ``@with_argument_parser`` 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. -A single argparse specific decorator -==================================== -In this approach, we would create one new decorator, perhaps called -`with_argument_parser`. This single decorator would take as it's argument a fully -defined `argparse.ArgumentParser`. This decorator would shelx the user input, -apply the ArgumentParser, and pass the resulting object to the decorated method, like so: +Help Messages +============= - argparser = argparse.ArgumentParser( - prog='speak', - description='Repeats what you tell me to' - ) - 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') +By default, cmd2 uses the docstring of the command method when a user asks +for help on the command. When you use the ``@with_argument_parser`` +decorator, the formatted help from the ``argparse.ArgumentParser`` is +appended to the docstring for the method of that command. With this code:: - @with_argument_parser(argparser) - def do_speak(self, argv, 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) + argparser = argparse.ArgumentParser() + argparser.add_argument('tag', nargs=1, help='tag') + argparser.add_argument('content', nargs='+', help='content to surround with tag') + @with_argument_parser(argparser) + def do_tag(self, cmdline, args=None): + """create a html tag""" + self.stdout.write('<{0}>{1}</{0}>'.format(args.tag[0], ' '.join(args.content))) + self.stdout.write('\n') -Compared to the no argparse support in cmd2 approach, this replaces a line of -code with a nested function with a decorator without a nested function. +The ``help tag`` command displays: +.. code-block:: none -A whole bunch of argparse specific decorators -============================================= + create a html tag + usage: tag [-h] tag content [content ...] -This approach would turn out something like the climax library -(https://github.com/miguelgrinberg/climax), which includes a decorator for each method available -on the `ArgumentParser()` object. Our `do_speak` command would look like this: + positional arguments: + tag tag + content content to surround with tag - @command() - @argument('-p', '--piglatin', action='store_true', help='atinLay') - @argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - @argument('r', '--repeat', type='int', help='output [n] times') - @add_argument('word', nargs='?', help='word to say') - def do_speak(self, argv, piglatin, shout, repeat, word) - """Repeats what you tell me to.""" - arg = word - if piglatin: - arg = '%s%say' % (arg[1:], arg[0]) - if shout: - arg = arg.upper() - repetitions = repeat or 1 - for i in range(min(repetitions, self.maxrepeats)): - self.poutput(arg) + optional arguments: + -h, --help show this help message and exit + + +If you would prefer the short description of your command to come after the usage message, leave the docstring on your method empty, but supply a ``description`` variable to the argument parser:: + + argparser = argparse.ArgumentParser(description='create an html tag') + argparser.add_argument('tag', nargs=1, help='tag') + argparser.add_argument('content', nargs='+', help='content to surround with tag') + @with_argument_parser(argparser) + def do_tag(self, cmdline, args=None): + self.stdout.write('<{0}>{1}</{0}>'.format(args.tag[0], ' '.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:: + + argparser = argparse.ArgumentParser( + description='create an html tag', + epilog='This command can not generate tags with no content, like <br/>.' + ) + argparser.add_argument('tag', nargs=1, help='tag') + argparser.add_argument('content', nargs='+', help='content to surround with tag') + @with_argument_parser(argparser) + def do_tag(self, cmdline, args=None): + self.stdout.write('<{0}>{1}</{0}>'.format(args.tag[0], ' '.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 <br/> + + +Deprecated optparse support +=========================== + +The ``optparse`` library has been deprecated since Python 2.7 (released on July +3rd 2010) and Python 3.2 (released on February 20th, 2011). ``optparse`` is +still included in the python standard library, but the documentation +recommends using ``argparse`` instead. + +``cmd2`` includes a decorator which can parse arguments using ``optparse``. This decorator is deprecated just like the ``optparse`` library. + +Here's an example:: + + opts = [make_option('-p', '--piglatin', action="store_true", help="atinLay"), + make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"), + make_option('-r', '--repeat', type="int", help="output [n] times")] + + @options(opts, arg_desc='(text to say)') + def do_speak(self, arg, opts=None): + """Repeats what you tell me to.""" + arg = ''.join(arg) + 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) + + +The optparse decorator performs the following key functions for you: +1. Use `shlex` to split the arguments entered by the user. +2. Parse the arguments using the given optparse options. +3. Replace the `__doc__` string of the decorated function (i.e. do_speak) with the help string generated by optparse. +4. Call the decorated function (i.e. do_speak) passing an additional parameter which contains the parsed options. diff --git a/docs/index.rst b/docs/index.rst index 2ef787e1..2f2a8dad 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -65,7 +65,7 @@ Contents: settingchanges unfreefeatures transcript - argument_parsing + argument_processing integrating hooks alternatives |