diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/argument_processing.rst | 141 | ||||
-rw-r--r-- | docs/index.rst | 1 |
2 files changed, 142 insertions, 0 deletions
diff --git a/docs/argument_processing.rst b/docs/argument_processing.rst new file mode 100644 index 00000000..279dbe47 --- /dev/null +++ b/docs/argument_processing.rst @@ -0,0 +1,141 @@ +=================== +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) + +The current 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. + +Here are several options for replacing this functionality with argparse. + + +No cmd2 support +=============== + +The easiest option would be to just remove the cmd2 specific support for +argument parsing. The above example would then look something like this: + + 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') + + 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: + 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) + +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. + + +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: + + 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') + + @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) + +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. + + +A whole bunch of argparse specific decorators +============================================= + +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: + + @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) + diff --git a/docs/index.rst b/docs/index.rst index 1f2bf96c..ae7ea300 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -67,6 +67,7 @@ Contents: settingchanges unfreefeatures transcript + argument_parsing integrating hooks alternatives |