summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-05-28 07:45:33 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-05-28 07:45:33 +0200
commit745a1ff1859bd31b48dc13c0adddadcc6ca0228b (patch)
treed82ba931971e948939d7cc141f622a24add27c45
parent0abc5d2f13514a251186542f777a56a7105f3aec (diff)
downloadmicheles-745a1ff1859bd31b48dc13c0adddadcc6ca0228b.tar.gz
First version of the new and improved optionparser module
-rw-r--r--optionparser/doc.html187
-rw-r--r--optionparser/doc.txt77
-rw-r--r--optionparser/example.py20
-rw-r--r--optionparser/example.txt31
-rw-r--r--optionparser/optionparser.py181
-rw-r--r--optionparser/test_optionparser.py86
6 files changed, 582 insertions, 0 deletions
diff --git a/optionparser/doc.html b/optionparser/doc.html
new file mode 100644
index 0000000..8361c4c
--- /dev/null
+++ b/optionparser/doc.html
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
+<title>optionparser: a simplified interface to optparse</title>
+<style type="text/css">
+
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #808080 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0040D0 } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #008000; font-weight: bold } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
+</style>
+</head>
+<body>
+<div class="document" id="optionparser-a-simplified-interface-to-optparse">
+<h1 class="title">optionparser: a simplified interface to optparse</h1>
+
+<p>The Python standard library contains three different modules for the
+parsing of command line options: <tt class="docutils literal">getopt</tt> (from the stone age),
+<tt class="docutils literal">optparse</tt> (from Python 2.3) and <tt class="docutils literal">argparse</tt> (from Python 2.7).
+There are therefore plenty of industrial strength ways of parsing
+the command line in Python; nevertheless, in my opinion, we a <em>simple</em>
+way is still missing.</p>
+<p>I see two classes of users for command line scripts: on one side there
+are programmers, sysadmins, scientists and in general people writing throw-away
+scripts for themselves, choosing to use a command line interface
+because it is the quick and simple; on the other side there are programmers
+writing power user tools for other programmers or system administrators.
+I strongly believe that 99.9% of the users of command line interfaces
+fit in the first category. Such users are
+not interested in features, they just want to be able to write a simple
+command line tool from a simple specification, not to build a command line
+parser by hand. Unfortunately, the current modules in the standard
+library forces them to go the hard way.</p>
+<p><tt class="docutils literal">optionparser</tt> solves this problem. By design it is not intended to
+be an industrial strength command line parsing module. Its capabilities
+are limited <em>on purpose</em>. If you need more power, by all means use the
+parsing modules in the standard library. Still, I have been using Python for 8
+years and never once I had to use the full power of <tt class="docutils literal">optparse</tt>: the
+features provided by <tt class="docutils literal">optionparser</tt> have been more than enough for
+me and I guess that the same could be said for most people.</p>
+<p>The fundamental idea behind <tt class="docutils literal">optionparser</tt> is to extract the parser
+from the usage docstring. In this way the user does not need to write
+the parser by hand by duplicating the instructions which she has already
+encoded implicitly in the usage docstring.</p>
+<p>Here is a concrete example of a script using <tt class="docutils literal">optionparser</tt>:</p>
+<pre class="literal-block">
+&quot;&quot;&quot;\
+usage: %prog [options]
+-c, --color=black: set default color
+-d, --delete=: delete the given file
+-a, --delete-all: delete all files
+&quot;&quot;&quot;
+
+def print_(color, txt):
+ code = {'black': 30, 'red': 31}[color]
+ print '\x1b[%dm%s\x1b[0m' % (code, txt)
+
+if __name__=='__main__':
+ from optionparser import OptionParser
+ option, args = OptionParser(__doc__).parse_args()
+ color = option.color
+ if option.delete_all:
+ print_(color, &quot;Delete all files&quot;)
+ elif option.delete:
+ print_(color, &quot;Delete the file %s&quot; % option.delete)
+
+</pre>
+<p>Here are a few examples of usage:</p>
+<pre class="literal-block">
+$ python example.py
+&lt;no output&gt;
+
+$ python example.py -h
+Usage: example.py [options]
+
+Options:
+ -h, --help show this help message and exit
+ -c COLOR, --color=COLOR
+ set default color
+ -d DELETE, --delete=DELETE
+ delete the given file
+ -a, --delete_all delete all files
+
+$ python example.py -d
+Usage: example.py [options]
+
+example.py: error: -d option requires an argument
+
+$ python example.py --delete x.txt
+Delete the file x.txt
+
+$ python example.py --delete-all
+Delete all files
+
+$ python example.py -a --color=red
+Delete all files # this is printed in red on most terminals
+
+
+</pre>
+<p>Here is some explanation. First of all, <tt class="docutils literal">optionparser</tt> recognizes
+four classes of command line arguments:</p>
+<ul class="simple">
+<li>positional arguments (none in this example)</li>
+<li>options with non-trivial defaults (<tt class="docutils literal">color=black</tt> in this example)</li>
+<li>options with null defaults (<tt class="docutils literal">delete=</tt> in this example)</li>
+<li>flags (<tt class="docutils literal"><span class="pre">delete-all</span></tt> in this example)</li>
+</ul>
+<p>You can have a generic number of positional arguments, including no
+arguments. An option with default can be set on the command line
+(for instance you can pass <tt class="docutils literal"><span class="pre">--color=red</span></tt>) or not; if not, the default
+value is used. An option with null default is a special case of
+option with a default, where the default value is the empty string.
+A flag is similar to an option with null default: if the
+flag is not passed its value is False, otherwise it is True.
+The difference with respect to a null default option is that
+the flag value is a boolean, not a string, and that the syntax
+is different (there is no <tt class="docutils literal">=</tt>).</p>
+<p>The <tt class="docutils literal">OptionParser.parse_args</tt> method
+parses the usage docstring by looking at the
+characters &quot;,&quot;, &quot;:&quot;, &quot;=&quot;, &quot;\n&quot;, so be careful when using them. If
+the docstring is not correctly formatted you will get a SyntaxError.</p>
+<p>Options with dashes inside the name, such as
+<tt class="docutils literal"><span class="pre">--delete-all</span></tt>, will be translated with underscore (<tt class="docutils literal">delete_all</tt>), since
+<tt class="docutils literal"><span class="pre">delete-all</span></tt> is not a valid Python identifier.</p>
+</div>
+</body>
+</html>
diff --git a/optionparser/doc.txt b/optionparser/doc.txt
new file mode 100644
index 0000000..ea8dbce
--- /dev/null
+++ b/optionparser/doc.txt
@@ -0,0 +1,77 @@
+optionparser: a simplified interface to optparse
+-------------------------------------------------
+
+The Python standard library contains three different modules for the
+parsing of command line options: ``getopt`` (from the stone age),
+``optparse`` (from Python 2.3) and ``argparse`` (from Python 2.7).
+There are therefore plenty of industrial strength ways of parsing
+the command line in Python; nevertheless, in my opinion, we a *simple*
+way is still missing.
+
+I see two classes of users for command line scripts: on one side there
+are programmers, sysadmins, scientists and in general people writing throw-away
+scripts for themselves, choosing to use a command line interface
+because it is the quick and simple; on the other side there are programmers
+writing power user tools for other programmers or system administrators.
+I strongly believe that 99.9% of the users of command line interfaces
+fit in the first category. Such users are
+not interested in features, they just want to be able to write a simple
+command line tool from a simple specification, not to build a command line
+parser by hand. Unfortunately, the current modules in the standard
+library forces them to go the hard way.
+
+``optionparser`` solves this problem. By design it is not intended to
+be an industrial strength command line parsing module. Its capabilities
+are limited *on purpose*. If you need more power, by all means use the
+parsing modules in the standard library. Still, I have been using Python for 8
+years and never once I had to use the full power of ``optparse``: the
+features provided by ``optionparser`` have been more than enough for
+me and I guess that the same could be said for most people.
+
+The fundamental idea behind ``optionparser`` is to extract the parser
+from the usage docstring. In this way the user does not need to write
+the parser by hand by duplicating the instructions which she has already
+encoded implicitly in the usage docstring.
+
+Here is a concrete example of a script using ``optionparser``:
+
+.. include:: example.py
+ :literal:
+
+Here are a few examples of usage:
+
+.. include:: example.txt
+ :literal:
+
+Here are some explanations. First of all, ``optionparser`` recognizes
+four classes of command line arguments:
+
+- positional arguments (none in this example)
+- options with non-trivial defaults (``color=black`` in this example)
+- options with null defaults (``delete=`` in this example)
+- flags (``delete-all`` in this example)
+
+You can have a generic number of positional arguments, including no
+arguments. An option with default can be set on the command line
+(for instance you can pass ``--color=red``) or not; if not, the default
+value is used. An option with null default is a special case of
+option with a default, where the default value is the empty string.
+A flag is similar to an option with null default: if the
+flag is not passed its value is False, otherwise it is True.
+The difference with respect to a null default option is that
+the flag value is a boolean, not a string, and that the syntax
+is different (there is no ``=``).
+
+The ``OptionParser.parse_args`` method
+parses the usage docstring by looking at the
+characters ",", ":", "=", "\\n" (be careful when using them, if
+the docstring is not correctly formatted you will get a ``SyntaxError``).
+
+Options with dashes inside the name, such as
+``--delete-all``, are translated into with underscore (``delete_all``), since
+``delete-all`` is not a valid Python identifier.
+
+Limitations
+------------------------------------
+
+The default values cannot contain a ':' (or it should be escaped).
diff --git a/optionparser/example.py b/optionparser/example.py
new file mode 100644
index 0000000..882aa8e
--- /dev/null
+++ b/optionparser/example.py
@@ -0,0 +1,20 @@
+"""\
+usage: %prog [options]
+-c, --color=black: set default color
+-d, --delete=: delete the given file
+-a, --delete-all: delete all files
+"""
+from optionparser import OptionParser
+
+def print_(color, txt):
+ code = {'black': 30, 'red': 31}[color]
+ print '\x1b[%dm%s\x1b[0m' % (code, txt)
+
+if __name__=='__main__':
+ opt = OptionParser(__doc__).parse_args()
+ if not opt:
+ OptionParser.exit()
+ elif opt.delete_all:
+ print_(option.color, "Delete all files")
+ elif opt.delete:
+ print_(option.color, "Delete the file %s" % option.delete)
diff --git a/optionparser/example.txt b/optionparser/example.txt
new file mode 100644
index 0000000..4cf83ff
--- /dev/null
+++ b/optionparser/example.txt
@@ -0,0 +1,31 @@
+$ python example.py
+usage: example.py [options]
+-c, --color=black: set default color
+-d, --delete=: delete the given file
+-a, --delete-all: delete all files
+
+$ python example.py -h
+Usage: example.py [options]
+
+Options:
+ -h, --help show this help message and exit
+ -c COLOR, --color=COLOR
+ set default color
+ -d DELETE, --delete=DELETE
+ delete the given file
+ -a, --delete_all delete all files
+
+$ python example.py -d
+Usage: example.py [options]
+
+example.py: error: -d option requires an argument
+
+$ python example.py --delete x.txt
+Delete the file x.txt
+
+$ python example.py --delete-all
+Delete all files
+
+$ python example.py -a --color=red
+Delete all files # this is printed in red on most terminals
+
diff --git a/optionparser/optionparser.py b/optionparser/optionparser.py
new file mode 100644
index 0000000..d3958de
--- /dev/null
+++ b/optionparser/optionparser.py
@@ -0,0 +1,181 @@
+########################## LICENCE ###############################
+##
+## Copyright (c) 2010, Michele Simionato
+## All rights reserved.
+##
+## Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## Redistributions in bytecode form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+## DAMAGE.
+
+"""
+See optionparser/doc.html for the documentation.
+"""
+
+import optparse, re, sys, string
+
+class RegexContainer(object):
+ """
+ A regular expression container for named regexps.
+ All its regular attributes must be valid regular expression templates.
+ Notice that the termination character $ must be doubled in
+ order to avoid confusion with the template syntax.
+ """
+ def __init__(self):
+ self._dic = {} # a dictionary {name: interpolated regex pattern}
+ def __setattr__(self, name, value):
+ if not name.startswith('_'): # regular attribute
+ templ = string.Template('(?P<' + name + '>' + value + ')')
+ pattern = templ.substitute(self._dic)
+ object.__setattr__(self, name, re.compile(pattern))
+ self._dic[name] = pattern
+ else: # private or special attribute
+ object.__setattr__(self, name, value)
+
+rx = RegexContainer() # a few regular expressions to parse the usage string
+
+rx.argument = r'[a-zA-Z]\w*'
+rx.arguments = '(?:\s+$argument)*'
+rx.ellipsis = r'\.\.\.'
+rx.prog = r'%prog$arguments\s*$ellipsis?'
+
+rx.enddef = r'\n[ \t]*\n|$$'
+rx.lines = r'.*?'
+rx.short = r'\w'
+rx.long = r'[-\w]+'
+rx.default = r'[^:]*'
+rx.help = r'.*'
+
+rx.usage = r'(?is)\s*usage:$lines$enddef' # case-insensitive multiline rx
+rx.optiondef = r'\s*-$short\s*,\s*--$long\s*=\s*$default:\s*$help'
+rx.flagdef = r'\s*-$short\s*,\s*--$long\s*:\s*$help'
+
+def parse_usage(txt):
+ "An utility to extract the expected arguments and the rest argument, if any"
+ match = rx.prog.match(txt)
+ if match is None:
+ ParsingError.raise_(txt)
+ expected_args = match.group('arguments').split()
+ if match.group('ellipsis'):
+ return expected_args[:-1], match.group('argument')
+ else:
+ return expected_args, ''
+
+class ParsingError(Exception):
+ @classmethod
+ def raise_(cls, usage):
+ raise cls("""Wrong format for the usage message.\n\n%s\n
+ It should be '%%prog arguments ... [options]""" % usage)
+
+def make_get_default_values(defaults):
+ # the purpose of this trick is to allow the idiom
+ # if not arg: OptionParser.exit()
+ def __nonzero__(self):
+ "True if at least one option is set to a non-trivial value"
+ for k, v in vars(self).iteritems():
+ if v and v != defaults[k]: return True
+ return False
+ Values = type('Values', (optparse.Values, object),
+ dict(__nonzero__=__nonzero__))
+ return lambda : Values(defaults)
+
+class OptionParser(object):
+ """
+ There should be only one instance of it.
+ Attributes: all_options, expected_args, rest_arg, p
+ """
+ def __init__(self, optionstring):
+ "Populate the option parser."
+ assert optionstring is not None, \
+ "Missing usage string (maybe __doc__ is None)"
+ self.__class__.optionstring = optionstring
+
+ # parse the optionstring
+ match = rx.usage.search(optionstring)
+ if not match:
+ raise ParsingError("Could not find the option definitions")
+ optlines = match.group("lines").splitlines()
+ prog = optlines[0] # first line
+ match = rx.prog.search(prog)
+ if not match:
+ ParsingError.raise_(prog)
+ self.expected_args, self.rest_arg = parse_usage(match.group())
+ self.p = optparse.OptionParser(prog)
+
+ # manage the default values
+ df = self.p.defaults
+ for a in self.expected_args:
+ df[a] = None
+ if self.rest_arg:
+ df[self.rest_arg] = []
+ self.p.get_default_values = make_get_default_values(df)
+
+ # parse the options
+ for line in optlines[1:]:
+ # check if the line is an option definition
+ match_option = rx.optiondef.match(line)
+ if match_option:
+ action = 'store'
+ short, long_, help, default=match_option.group(
+ "short", "long", "help", "default")
+ else: # check if the line is a flag definition
+ match_flag = rx.flagdef.match(line)
+ if match_flag:
+ action = 'store_true'
+ short, long_, help, default=match_flag.group(
+ "short", "long", "help") + (False,)
+ else: # raise an error
+ raise ParsingError(
+ "Cannot parse the definition %r correctly" % line)
+ # add the options
+ long_ = long_.replace('-', '_')
+ self.p.add_option("-" + short, "--" + long_,
+ action=action, help=help, default=default)
+ # skip the help option, which destination is None
+ self.all_options = [o for o in self.p.option_list if o.dest]
+
+ def parse_args(self, arglist=None):
+ """
+ Parse the received arguments and returns an ``optparse.Values``
+ object containing both the options and the positional arguments.
+ """
+ option, args = self.p.parse_args(arglist)
+ n_expected_args = len(self.expected_args)
+ n_received_args = len(args)
+ if (n_received_args < n_expected_args) or (
+ n_received_args > n_expected_args and not self.rest_arg):
+ raise ParsingError(
+ 'Received %d arguments %s, expected %d %s' %
+ (n_received_args, args, n_expected_args, self.expected_args))
+ for name, value in zip(self.expected_args, args):
+ setattr(option, name, value)
+ if self.rest_arg:
+ setattr(option, self.rest_arg, args[n_expected_args:])
+ return option
+
+ @classmethod
+ def exit(cls, msg=None):
+ if msg is None:
+ msg = cls.optionstring.replace("%prog", sys.argv[0])
+ raise SystemExit(msg)
+
+if __name__ == '__main__':
+ arg = OptionParser('''\
+usage: %prog args ... [options]
+-d, --delete=: delete a file
+''').parse_args(['-d', 'foo', 'arg1', 'arg2'])
diff --git a/optionparser/test_optionparser.py b/optionparser/test_optionparser.py
new file mode 100644
index 0000000..9b34610
--- /dev/null
+++ b/optionparser/test_optionparser.py
@@ -0,0 +1,86 @@
+import sys
+from optionparser import OptionParser, ParsingError
+
+def expect_error(err, func, *args, **kw):
+ """
+ Calls a callable with the given arguments and expects a given
+ exception type to be raised, otherwise raises a RuntimeError.
+ """
+ try:
+ func(*args, **kw)
+ except Exception, e:
+ if str(e) != err:
+ raise e.__class__, e, sys.exc_info()[2]
+ else:
+ raise RuntimeError(
+ 'Exception %s expected, got none' % errclass.__name__)
+
+p1 = OptionParser('''\
+usage: %prog args ... [options]
+-d, --delete=: delete a file
+''')
+
+def test_p1():
+ arg = p1.parse_args(['-d', 'foo', 'arg1', 'arg2'])
+ assert arg.delete == 'foo'
+ assert arg.args == ['arg1', 'arg2']
+
+ arg = p1.parse_args([])
+ assert arg.delete == '', arg.delete
+ assert arg.args == [], arg.args
+ assert not arg, arg
+
+p2 = OptionParser('''
+ usage: %prog arg1 args ... [options]
+-d, --delete=: delete a file
+''')
+
+def test_p2():
+ arg = p2.parse_args(['-d', 'foo', 'arg1', 'arg2'])
+ assert arg.delete == 'foo', arg.delete
+ assert arg.arg1 == 'arg1', arg.arg1
+ assert arg.args == ['arg2'], arg.args
+
+ arg = p2.parse_args(['arg1'])
+ assert arg.delete == '', arg.delete
+ assert arg.args == [], arg.args
+ assert arg, arg
+
+ expect_error("Received 0 arguments [], expected 1 ['arg1']",
+ p2.parse_args, [])
+
+p3 = OptionParser('''\
+USAGE: %prog arg1 [options]
+-d, --delete=: delete a file
+''')
+
+def test_p3():
+ arg = p3.parse_args(['arg1'])
+ assert arg.delete == '', arg.delete
+ assert arg.arg1 == 'arg1', arg.args
+
+ expect_error("Received 2 arguments ['arg1', 'arg2'], expected 1 ['arg1']",
+ p3.parse_args, ['arg1', 'arg2'])
+
+ expect_error("Received 0 arguments [], expected 1 ['arg1']",
+ p3.parse_args, [])
+
+p4 = OptionParser('''\
+Usage: %prog [options]
+-c, --color=black: set default color
+-d, --delete=: delete the given file
+-a, --delete-all: delete all files
+''')
+
+def test_p4():
+ arg = p4.parse_args(['-a'])
+ assert arg.delete_all is True, arg.delete_all
+
+ arg = p4.parse_args([])
+ assert not arg, arg
+
+ arg = p4.parse_args(['--color=black'])
+ assert not arg, arg
+
+ arg = p4.parse_args(['--color=red'])
+ assert arg, arg