|
2 | 2 | Argument Processing |
3 | 3 | =================== |
4 | 4 |
|
5 | | -cmd2 currently includes code which makes it easier to add arguments to the |
6 | | -commands in your cmd2 subclass. This support utilizes the optparse library, |
7 | | -which has been deprecated since Python 2.7 (released on July 3rd 2010) and |
8 | | -Python 3.2 (released on February 20th, 2011). Optparse is still included in the |
9 | | -python standard library, but the documentation recommends using argparse |
10 | | -instead. It's time to modernize cmd2 to utilize argparse. |
11 | | - |
12 | | -I don't believe there is a way to change cmd2 to use argparse instead of |
13 | | -optparse without requiring subclasses of cmd2 to make some change. The long |
14 | | -recomended way to use optparse in cmd2 includes the use of the |
15 | | -optparse.make_option, which must be changed in order to use argparse. |
16 | | - |
17 | | -There are two potential ways to use argument parsing with cmd2: to parse |
18 | | -options given at the shell prompt when invoked, and to parse options given at |
19 | | -the cmd2 prompt prior to executing a command. |
20 | | - |
21 | | -optparse example |
22 | | -================ |
23 | | - |
24 | | -Here's an example of the current optparse support: |
25 | | - |
26 | | - opts = [make_option('-p', '--piglatin', action="store_true", help="atinLay"), |
27 | | - make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"), |
28 | | - make_option('-r', '--repeat', type="int", help="output [n] times")] |
29 | | - |
30 | | - @options(opts, arg_desc='(text to say)') |
31 | | - def do_speak(self, arg, opts=None): |
32 | | - """Repeats what you tell me to.""" |
33 | | - arg = ''.join(arg) |
34 | | - if opts.piglatin: |
35 | | - arg = '%s%say' % (arg[1:], arg[0]) |
36 | | - if opts.shout: |
37 | | - arg = arg.upper() |
38 | | - repetitions = opts.repeat or 1 |
39 | | - for i in range(min(repetitions, self.maxrepeats)): |
40 | | - self.poutput(arg) |
| 5 | +``cmd2`` makes it easy to add sophisticated argument processing to your commands using the ``argparse`` python module. ``cmd2`` handles the following for you: |
41 | 6 |
|
42 | | -The current optparse decorator performs the following key functions for you: |
| 7 | +1. Parsing input and quoted strings like the Unix shell |
| 8 | +2. Parse the resulting argument list using an instance of ``argparse.ArgumentParser`` that you provide |
| 9 | +3. Passes the resulting ``argparse.Namespace`` object to your command function |
| 10 | +4. Adds the usage message from the argument parser to your command. |
| 11 | +5. Checks if the ``-h/--help`` option is present, and if so, display the help message for the command |
43 | 12 |
|
44 | | -1. Use `shlex` to split the arguments entered by the user. |
45 | | -2. Parse the arguments using the given optparse options. |
46 | | -3. Replace the `__doc__` string of the decorated function (i.e. do_speak) with |
47 | | -the help string generated by optparse. |
48 | | -4. Call the decorated function (i.e. do_speak) passing an additional parameter |
49 | | -which contains the parsed options. |
50 | | - |
51 | | -Here are several options for replacing this functionality with argparse. |
| 13 | +These features are all provided by the ``@with_argument_parser`` decorator. |
52 | 14 |
|
| 15 | +Using the decorator |
| 16 | +=================== |
53 | 17 |
|
54 | | -No cmd2 support |
55 | | -=============== |
| 18 | +For each command in the ``cmd2`` subclass which requires argument parsing, |
| 19 | +create an instance of ``argparse.ArgumentParser()`` which can parse the |
| 20 | +input appropriately for the command. Then decorate the command method with |
| 21 | +the ``@with_argument_parser`` decorator, passing the argument parser as the |
| 22 | +first parameter to the decorator. Add a third variable to the command method, which will contain the results of ``ArgumentParser.parse_args()``. |
56 | 23 |
|
57 | | -The easiest option would be to just remove the cmd2 specific support for |
58 | | -argument parsing. The above example would then look something like this: |
| 24 | +Here's what it looks like:: |
59 | 25 |
|
60 | | - argparser = argparse.ArgumentParser( |
61 | | - prog='speak', |
62 | | - description='Repeats what you tell me to' |
63 | | - ) |
| 26 | + argparser = argparse.ArgumentParser() |
64 | 27 | argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') |
65 | 28 | argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') |
66 | | - argparser.add_argument('r', '--repeat', type='int', help='output [n] times') |
| 29 | + argparser.add_argument('-r', '--repeat', type=int, help='output [n] times') |
67 | 30 | argparser.add_argument('word', nargs='?', help='word to say') |
68 | 31 |
|
69 | | - def do_speak(self, argv) |
70 | | - """Repeats what you tell me to.""" |
71 | | - opts = argparser.parse_args(shlex.split(argv, posix=POSIX_SHLEX)) |
72 | | - arg = opts.word |
73 | | - if opts.piglatin: |
| 32 | + @with_argument_parser(argparser) |
| 33 | + def do_speak(self, argv, opts) |
| 34 | + """Repeats what you tell me to.""" |
| 35 | + arg = opts.word |
| 36 | + if opts.piglatin: |
74 | 37 | arg = '%s%say' % (arg[1:], arg[0]) |
75 | | - if opts.shout: |
| 38 | + if opts.shout: |
76 | 39 | arg = arg.upper() |
77 | | - repetitions = opts.repeat or 1 |
78 | | - for i in range(min(repetitions, self.maxrepeats)): |
| 40 | + repetitions = opts.repeat or 1 |
| 41 | + for i in range(min(repetitions, self.maxrepeats)): |
79 | 42 | self.poutput(arg) |
80 | 43 |
|
81 | | -Using shlex in this example is technically not necessary because the `do_speak` |
82 | | -command only expects a single word argument. It is included here to show what |
83 | | -would be required to replicate the current optparse based functionality. |
| 44 | +.. note:: |
84 | 45 |
|
| 46 | + The ``@with_argument_parser`` decorator sets the ``prog`` variable in |
| 47 | + the argument parser based on the name of the method it is decorating. |
| 48 | + This will override anything you specify in ``prog`` variable when |
| 49 | + creating the argument parser. |
85 | 50 |
|
86 | | -A single argparse specific decorator |
87 | | -==================================== |
88 | 51 |
|
89 | | -In this approach, we would create one new decorator, perhaps called |
90 | | -`with_argument_parser`. This single decorator would take as it's argument a fully |
91 | | -defined `argparse.ArgumentParser`. This decorator would shelx the user input, |
92 | | -apply the ArgumentParser, and pass the resulting object to the decorated method, like so: |
| 52 | +Help Messages |
| 53 | +============= |
93 | 54 |
|
94 | | - argparser = argparse.ArgumentParser( |
95 | | - prog='speak', |
96 | | - description='Repeats what you tell me to' |
97 | | - ) |
98 | | - argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') |
99 | | - argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') |
100 | | - argparser.add_argument('-r', '--repeat', type=int, help='output [n] times') |
101 | | - argparser.add_argument('word', nargs='?', help='word to say') |
| 55 | +By default, cmd2 uses the docstring of the command method when a user asks |
| 56 | +for help on the command. When you use the ``@with_argument_parser`` |
| 57 | +decorator, the formatted help from the ``argparse.ArgumentParser`` is |
| 58 | +appended to the docstring for the method of that command. With this code:: |
102 | 59 |
|
103 | | - @with_argument_parser(argparser) |
104 | | - def do_speak(self, argv, opts) |
105 | | - """Repeats what you tell me to.""" |
106 | | - arg = opts.word |
107 | | - if opts.piglatin: |
108 | | - arg = '%s%say' % (arg[1:], arg[0]) |
109 | | - if opts.shout: |
110 | | - arg = arg.upper() |
111 | | - repetitions = opts.repeat or 1 |
112 | | - for i in range(min(repetitions, self.maxrepeats)): |
113 | | - self.poutput(arg) |
| 60 | + argparser = argparse.ArgumentParser() |
| 61 | + argparser.add_argument('tag', nargs=1, help='tag') |
| 62 | + argparser.add_argument('content', nargs='+', help='content to surround with tag') |
| 63 | + @with_argument_parser(argparser) |
| 64 | + def do_tag(self, cmdline, args=None): |
| 65 | + """create a html tag""" |
| 66 | + self.stdout.write('<{0}>{1}</{0}>'.format(args.tag[0], ' '.join(args.content))) |
| 67 | + self.stdout.write('\n') |
114 | 68 |
|
115 | | -Compared to the no argparse support in cmd2 approach, this replaces a line of |
116 | | -code with a nested function with a decorator without a nested function. |
| 69 | +The ``help tag`` command displays: |
117 | 70 |
|
| 71 | +.. code-block:: none |
118 | 72 |
|
119 | | -A whole bunch of argparse specific decorators |
120 | | -============================================= |
| 73 | + create a html tag |
| 74 | + usage: tag [-h] tag content [content ...] |
121 | 75 |
|
122 | | -This approach would turn out something like the climax library |
123 | | -(https://github.com/miguelgrinberg/climax), which includes a decorator for each method available |
124 | | -on the `ArgumentParser()` object. Our `do_speak` command would look like this: |
| 76 | + positional arguments: |
| 77 | + tag tag |
| 78 | + content content to surround with tag |
125 | 79 |
|
126 | | - @command() |
127 | | - @argument('-p', '--piglatin', action='store_true', help='atinLay') |
128 | | - @argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') |
129 | | - @argument('r', '--repeat', type='int', help='output [n] times') |
130 | | - @add_argument('word', nargs='?', help='word to say') |
131 | | - def do_speak(self, argv, piglatin, shout, repeat, word) |
132 | | - """Repeats what you tell me to.""" |
133 | | - arg = word |
134 | | - if piglatin: |
135 | | - arg = '%s%say' % (arg[1:], arg[0]) |
136 | | - if shout: |
137 | | - arg = arg.upper() |
138 | | - repetitions = repeat or 1 |
139 | | - for i in range(min(repetitions, self.maxrepeats)): |
140 | | - self.poutput(arg) |
| 80 | + optional arguments: |
| 81 | + -h, --help show this help message and exit |
| 82 | +
|
| 83 | + |
| 84 | +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:: |
| 85 | + |
| 86 | + argparser = argparse.ArgumentParser(description='create an html tag') |
| 87 | + argparser.add_argument('tag', nargs=1, help='tag') |
| 88 | + argparser.add_argument('content', nargs='+', help='content to surround with tag') |
| 89 | + @with_argument_parser(argparser) |
| 90 | + def do_tag(self, cmdline, args=None): |
| 91 | + self.stdout.write('<{0}>{1}</{0}>'.format(args.tag[0], ' '.join(args.content))) |
| 92 | + self.stdout.write('\n') |
| 93 | + |
| 94 | +Now when the user enters ``help tag`` they see: |
| 95 | + |
| 96 | +.. code-block:: none |
| 97 | +
|
| 98 | + usage: tag [-h] tag content [content ...] |
| 99 | +
|
| 100 | + create an html tag |
| 101 | +
|
| 102 | + positional arguments: |
| 103 | + tag tag |
| 104 | + content content to surround with tag |
| 105 | +
|
| 106 | + optional arguments: |
| 107 | + -h, --help show this help message and exit |
| 108 | +
|
| 109 | +
|
| 110 | +To add additional text to the end of the generated help message, use the ``epilog`` variable:: |
| 111 | + |
| 112 | + argparser = argparse.ArgumentParser( |
| 113 | + description='create an html tag', |
| 114 | + epilog='This command can not generate tags with no content, like <br/>.' |
| 115 | + ) |
| 116 | + argparser.add_argument('tag', nargs=1, help='tag') |
| 117 | + argparser.add_argument('content', nargs='+', help='content to surround with tag') |
| 118 | + @with_argument_parser(argparser) |
| 119 | + def do_tag(self, cmdline, args=None): |
| 120 | + self.stdout.write('<{0}>{1}</{0}>'.format(args.tag[0], ' '.join(args.content))) |
| 121 | + self.stdout.write('\n') |
| 122 | + |
| 123 | +Which yields: |
| 124 | + |
| 125 | +.. code-block:: none |
| 126 | +
|
| 127 | + usage: tag [-h] tag content [content ...] |
| 128 | +
|
| 129 | + create an html tag |
| 130 | +
|
| 131 | + positional arguments: |
| 132 | + tag tag |
| 133 | + content content to surround with tag |
| 134 | +
|
| 135 | + optional arguments: |
| 136 | + -h, --help show this help message and exit |
| 137 | +
|
| 138 | + This command can not generate tags with no content, like <br/> |
| 139 | +
|
| 140 | +
|
| 141 | +Deprecated optparse support |
| 142 | +=========================== |
| 143 | + |
| 144 | +The ``optparse`` library has been deprecated since Python 2.7 (released on July |
| 145 | +3rd 2010) and Python 3.2 (released on February 20th, 2011). ``optparse`` is |
| 146 | +still included in the python standard library, but the documentation |
| 147 | +recommends using ``argparse`` instead. |
| 148 | + |
| 149 | +``cmd2`` includes a decorator which can parse arguments using ``optparse``. This decorator is deprecated just like the ``optparse`` library. |
| 150 | + |
| 151 | +Here's an example:: |
| 152 | + |
| 153 | + opts = [make_option('-p', '--piglatin', action="store_true", help="atinLay"), |
| 154 | + make_option('-s', '--shout', action="store_true", help="N00B EMULATION MODE"), |
| 155 | + make_option('-r', '--repeat', type="int", help="output [n] times")] |
| 156 | + |
| 157 | + @options(opts, arg_desc='(text to say)') |
| 158 | + def do_speak(self, arg, opts=None): |
| 159 | + """Repeats what you tell me to.""" |
| 160 | + arg = ''.join(arg) |
| 161 | + if opts.piglatin: |
| 162 | + arg = '%s%say' % (arg[1:], arg[0]) |
| 163 | + if opts.shout: |
| 164 | + arg = arg.upper() |
| 165 | + repetitions = opts.repeat or 1 |
| 166 | + for i in range(min(repetitions, self.maxrepeats)): |
| 167 | + self.poutput(arg) |
| 168 | + |
| 169 | + |
| 170 | +The optparse decorator performs the following key functions for you: |
141 | 171 |
|
| 172 | +1. Use `shlex` to split the arguments entered by the user. |
| 173 | +2. Parse the arguments using the given optparse options. |
| 174 | +3. Replace the `__doc__` string of the decorated function (i.e. do_speak) with the help string generated by optparse. |
| 175 | +4. Call the decorated function (i.e. do_speak) passing an additional parameter which contains the parsed options. |
0 commit comments