Skip to content

Commit 17d662b

Browse files
committed
Write documentation
1 parent 5a28a87 commit 17d662b

File tree

2 files changed

+147
-113
lines changed

2 files changed

+147
-113
lines changed

docs/argument_processing.rst

Lines changed: 146 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -2,140 +2,174 @@
22
Argument Processing
33
===================
44

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:
416

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
4312

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.
5214

15+
Using the decorator
16+
===================
5317

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()``.
5623

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::
5925

60-
argparser = argparse.ArgumentParser(
61-
prog='speak',
62-
description='Repeats what you tell me to'
63-
)
26+
argparser = argparse.ArgumentParser()
6427
argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
6528
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')
6730
argparser.add_argument('word', nargs='?', help='word to say')
6831

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:
7437
arg = '%s%say' % (arg[1:], arg[0])
75-
if opts.shout:
38+
if opts.shout:
7639
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)):
7942
self.poutput(arg)
8043

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::
8445

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.
8550

86-
A single argparse specific decorator
87-
====================================
8851

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+
=============
9354

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::
10259

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')
11468

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:
11770

71+
.. code-block:: none
11872
119-
A whole bunch of argparse specific decorators
120-
=============================================
73+
create a html tag
74+
usage: tag [-h] tag content [content ...]
12175
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
12579
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:
141171

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.

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Contents:
6565
settingchanges
6666
unfreefeatures
6767
transcript
68-
argument_parsing
68+
argument_processing
6969
integrating
7070
hooks
7171
alternatives

0 commit comments

Comments
 (0)