summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-05-31 15:42:49 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-05-31 15:42:49 +0200
commitbaa78b50ebcccd789b89f083b36ab2e9a6a389d5 (patch)
tree7dbe43a5ead3ad2594513b2ee87a085595d1d166
parent0ccbfa6c34e0a423e7d0788563988765eab6d44b (diff)
downloadmicheles-baa78b50ebcccd789b89f083b36ab2e9a6a389d5.tar.gz
clap 0.2, based on function annotations
-rw-r--r--clap/README.txt52
-rw-r--r--clap/article/annotations.py8
-rw-r--r--clap/article/article.html410
-rw-r--r--clap/article/article.pdf2418
-rw-r--r--clap/article/article.txt442
l---------clap/article/clap.py1
-rw-r--r--clap/article/example1.py13
-rw-r--r--clap/article/example10.py14
-rw-r--r--clap/article/example11.py12
-rw-r--r--clap/article/example2.py10
-rw-r--r--clap/article/example3.py6
-rw-r--r--clap/article/example4.py14
-rw-r--r--clap/article/example4_.py8
-rw-r--r--clap/article/example5.py8
-rw-r--r--clap/article/example6.py12
-rw-r--r--clap/article/example7.py9
-rw-r--r--clap/article/example8.py7
-rw-r--r--clap/article/example9.py7
-rw-r--r--clap/clap.py265
-rw-r--r--clap/doc.txt5
-rw-r--r--clap/test_clap.py82
21 files changed, 3595 insertions, 208 deletions
diff --git a/clap/README.txt b/clap/README.txt
new file mode 100644
index 0000000..06c9ce3
--- /dev/null
+++ b/clap/README.txt
@@ -0,0 +1,52 @@
+clap, the easiest command line arguments parser in the world
+============================================================
+
+:Author: Michele Simionato
+:E-mail: michele.simionato@gmail.com
+:Requires: Python 2.3+
+:Download page: http://pypi.python.org/pypi/clap
+:Installation: ``easy_install clap``
+:License: BSD license
+
+Installation
+-------------
+
+If you are lazy, just perform
+
+$ easy_install clap
+
+which will install just the module on your system. Notice that
+Python 3 requires the easy_install version of the distribute_ project.
+
+If you prefer to install the full distribution from source, including
+the documentation, download the tarball_, unpack it and run
+
+$ python setup.py install
+
+in the main directory, possibly as superuser.
+
+.. _tarball: http://pypi.python.org/pypi/clap
+.. _distribute: http://packages.python.org/distribute/
+
+Testing
+--------
+
+Run
+
+$ python test_clap.py
+
+or
+
+$ nosetests test_clap
+
+or
+
+$ py.test test_clap
+
+Documentation
+--------------
+
+You can choose between the `HTML version`_ and the `PDF version`_ .
+
+.. _HTML version: http://micheles.googlecode.com/hg/clap/documentation.html
+.. _PDF version: http://micheles.googlecode.com/hg/clap/documentation.pdf
diff --git a/clap/article/annotations.py b/clap/article/annotations.py
new file mode 100644
index 0000000..c84cc04
--- /dev/null
+++ b/clap/article/annotations.py
@@ -0,0 +1,8 @@
+class Positional(object):
+ def __init__(self, help='', type=None, choices=None, metavar=None):
+ self.help = help
+ self.kind = 'positional'
+ self.abbrev = None
+ self.type = type
+ self.choices = choices
+ self.metavar = metavar
diff --git a/clap/article/article.html b/clap/article/article.html
new file mode 100644
index 0000000..65d0c68
--- /dev/null
+++ b/clap/article/article.html
@@ -0,0 +1,410 @@
+<?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></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">
+
+
+<div class="section" id="the-easiest-command-line-arguments-parser-in-the-python-world">
+<h1>The easiest command line arguments parser in the Python world</h1>
+<p>Today I want to announce to the general public the birth of my latest
+project, which aims to be the easiest command line arguments
+parser in the Python world: <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a>.</p>
+<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> is designed to be <a class="reference external" href="http://www.welton.it/articles/scalable_systems">downwardly scalable</a>, i.e. to be trivially simple
+to use for trivial use cases, while being surprisingly scalable upwards
+for less trivial use cases. Still, <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> is not intended to
+be an industrial strength command line parsing module. Its
+capabilities are limited by design. 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 the
+standard library modules.</p>
+<p>Actually I am prettu much convinced that features provided by <tt class="docutils literal">clap</tt>
+aee more than enough for 99.9% of the typical use cases of a scripter
+working in a Unix-like environment. I am targetting here 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. 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. They are designed to implement
+power user tools for programmers or system administrators, and
+they have a non-trivial learning curve.</p>
+<p>This is why, even if there is no want of command line arguments
+parsers in Python world, sometime like <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> was still lacking.</p>
+<p>The standard library alone contains three different modules for the
+parsing of command line options: <a class="reference external" href="http://docs.python.org/library/getopt.html">getopt</a> (from the stone age),
+<a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> (from Python 2.3) and <a class="reference external" href="http://argparse.googlecode.com">argparse</a> (from Python 2.7). All of
+them are quite powerful and especially <a class="reference external" href="http://argparse.googlecode.com">argparse</a> is an industrial
+strength solution; <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> is just a nice and simple wrapper over
+<a class="reference external" href="http://argparse.googlecode.com">argparse</a>, hiding most of the complexity while retaining most of
+the power.</p>
+</div>
+<div class="section" id="the-importance-of-scaling-down">
+<h1>The importance of scaling down</h1>
+<p>An ex-coworket of mine, David Welton, once wrote a nice article about
+the importance of <a class="reference external" href="http://www.welton.it/articles/scalable_systems">scaling down</a>: most people are concerned with the
+possibility of scaling up, but we should also be concerned with the
+issue of scaling down: in other worlds, simple things should be kept
+simple. To be concrete, let me start with the simplest possible
+thing: a script that takes a single argument and does something to it.
+It cannot get more trivial than that (discarding the possibility of
+a script without command line arguments, where there is nothing to parse),
+nevertheless it is a use case <em>extremely important</em>:
+I need to write scripts like that nearly every day, I wrote hundreds
+of them in the last few years and I have never been happy. Here is
+a typical example:</p>
+<blockquote>
+<pre class="literal-block">
+def main(dsn):
+ &quot;Do something with the database&quot;
+ print(dsn)
+
+if __name__ == '__main__':
+ import sys
+ n = len(sys.argv[1:])
+ if n == 0:
+ sys.exit('usage: python %s dsn' % sys.argv[0])
+ elif n == 1:
+ main(sys.argv[1])
+ else:
+ sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:]))
+
+</pre>
+</blockquote>
+<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (10 lines
+counting an empty line) is essentially boilerplate that should not exists.
+Actually I think the Python language should recognize the
+main function and perform trivial arguments parsing behind the
+scenes; unfortunaly this is unlikely to happen. Therefore I have
+been writing boilerplate like this for years, and every time
+I <em>hate</em> having to check for the <tt class="docutils literal">IndexError</tt> by hand, and I hate having
+to write always the same usage message. The purpose of using a
+scripting language is convenience and trivial things should be
+trivial. Unfortunately the standard library modules do not help
+for this use case, which may be trivial, but it is still
+incredibly common. Using <a class="reference external" href="http://docs.python.org/library/getopt.html">getopt</a> and <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> does not help,
+since they are intended to manage options and not positional arguments;
+the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module helps a bit and it is able to reduce the
+boilerplate from 10 lines to 7 lines:</p>
+<blockquote>
+<pre class="literal-block">
+def main(dsn):
+ &quot;Do something on the database&quot;
+ print(dsn)
+
+if __name__ == '__main__':
+ import argparse
+ p = argparse.ArgumentParser()
+ p.add_argument('dsn')
+ arg = p.parse_args()
+ main(arg.dsn)
+
+</pre>
+</blockquote>
+<p>However saving a three lines does not justify introducing the external
+dependency (most people will not switch Python 2.7, currenctly in beta
+for many years).
+Moreover, it just feels too complex to instantiate a class and to
+define a parser by hand for such a trivial task.</p>
+<p>The <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> module works pretty well when scaling down, and it is able
+to reduce the ten lines of boiler plate to two lines. With the
+<a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> module all you need to write is</p>
+<blockquote>
+<pre class="literal-block">
+def main(dsn):
+ &quot;Do something with the database&quot;
+ print(dsn)
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
+
+</pre>
+</blockquote>
+<p>As an additional bonus she <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> module automatically recognizes the help flag:</p>
+<blockquote>
+# python example1.py -h</blockquote>
+<p>This is only the tip of the iceberg, as you will see.</p>
+</div>
+<div class="section" id="optional-arguments">
+<h1>Optional arguments</h1>
+<p>I have encountered this use case at work hundreds of times:</p>
+<blockquote>
+<pre class="literal-block">
+def main(dsn):
+ &quot;Do something on the database&quot;
+ print(dsn)
+
+if __name__ == '__main__':
+ import argparse
+ p = argparse.ArgumentParser()
+ p.add_argument('dsn')
+ arg = p.parse_args()
+ main(arg.dsn)
+
+</pre>
+</blockquote>
+<p>9 lines of boilerplate are removed.</p>
+<p>Finally, often you want to pass a variable number of arguments to
+your command line script. Here is an example, a script which runs
+on the database a series of .sql scripts:</p>
+<blockquote>
+<pre class="literal-block">
+from datetime import datetime
+
+def main(dsn, *scripts):
+ &quot;Run the given scripts on the database&quot;
+ for script in scripts:
+ print('executing %s' % script)
+
+if __name__ == '__main__':
+ if len(sys.argv) &lt; 2:
+ sys.exit('usage: python %s dsn script.sql ...' % sys.argv[0])
+ main(sys.argv[1:])
+
+</pre>
+</blockquote>
+<p>Using <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a>, you can just replace the <tt class="docutils literal">__main__</tt> block with the usual
+<tt class="docutils literal">import clap; clap.call(main)</tt>. The importanting is that you get a
+much nicer usage message:</p>
+<pre class="literal-block">
+usage: example7.py [-h] dsn [scripts [scripts ...]]
+
+positional arguments:
+ dsn
+ scripts
+
+optional arguments:
+ -h, --help show this help message and exit
+</pre>
+</div>
+<div class="section" id="options-and-flags">
+<h1>Options and flags</h1>
+<p>It is surprising how little command line scripts with options
+I have written over the years (probably less than a hundred),
+compared to the number of scripts with positional arguments
+(I certainly have written more than a thousand of them).
+Still, this use case is quite common and cannot be neglected.
+The standard library modules (all of them) are quite verbose
+when it comes to specifying the options and frankly I have
+never used them directly. Instead, I have always relied on
+an old recipe of mine, the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, which provides a
+convenient wrapper over <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> or I have just performed the
+parsing by hand in the simplest cases.</p>
+<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> is inspired to the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, in the sense that
+it delivers the programmer from the burden of writing the
+parser for the options by hand, but is less of a hack: instead
+of extracting the parser from the docstring of the module, it
+extracts it from the signature of the <tt class="docutils literal">main</tt> function.</p>
+<p>The idea is to leverage on the <cite>function annotations</cite> concept, a new
+feature of Python 3. An example is worth a thousand words, so here
+it is:</p>
+<blockquote>
+<pre class="literal-block">
+def main(command: (&quot;SQL query&quot;, 'option-c'), dsn):
+ if command:
+ print('executing %s on %s' % (command, dsn))
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
+
+</pre>
+</blockquote>
+<p>As you see, the argument <tt class="docutils literal">command</tt> has been annotated with the
+tuple <tt class="docutils literal">(&quot;SQL query&quot;, <span class="pre">'option-c')</span></tt>: the first string is the
+help string which will appear in the usage message, whereas the
+second string tells <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> that <tt class="docutils literal">command</tt> is an option and that
+it can be abbreviated with the letter <tt class="docutils literal">c</tt>. Of course, it also
+possible to use the long option format, by prefixing the option
+with <tt class="docutils literal"><span class="pre">--command=</span></tt>. The resulting usage message is the following:</p>
+<pre class="literal-block">
+$ python3 example8.py -h
+usage: example8.py [-h] [-c COMMAND] dsn
+
+positional arguments:
+ dsn
+
+optional arguments:
+ -h, --help show this help message and exit
+ -c COMMAND, --command COMMAND
+ SQL query
+</pre>
+<p>Here are two examples of usage:</p>
+<pre class="literal-block">
+$ python3 example8.py -c&quot;select * from table&quot; dsn
+executing select * from table on dsn
+
+$ python3 example8.py --command=&quot;select * from table&quot; dsn
+executing select * from table on dsn
+</pre>
+<p>Notice that if the option is not passed, the variable <tt class="docutils literal">command</tt>
+will get the value <tt class="docutils literal">None</tt>.</p>
+<p>Even positional argument can be annotated, but it makes no sense to
+specify the &quot;option-c&quot; string, so you can skip it and write:</p>
+<pre class="literal-block">
+def main(command: (&quot;SQL query&quot;, 'option-c'), dsn: (&quot;Database dsn&quot;,)):
+ ...
+</pre>
+<p>Alternatively, you can write <tt class="docutils literal">(&quot;Database dsn&quot;, None)</tt>. In both cases
+the usage message now will show a nice help string on the right hand side
+of the <tt class="docutils literal">dsn</tt> positional argument. varargs (starred-arguments) can also
+be annotated:</p>
+<pre class="literal-block">
+def main(dsn: (&quot;Database dsn&quot;,), *scripts: (&quot;SQL scripts&quot;,)):
+ ...
+</pre>
+<p>is a valid signature for <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a>, which will recognize the help strings
+for both <tt class="docutils literal">dsn</tt> and <tt class="docutils literal">scripts</tt>.</p>
+<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> also recognizes flags, i.e. boolean options which are
+True if they are passed to the command line and False if they are absent.
+Here is an example:</p>
+<pre class="literal-block">
+$ python3 example9.py -v dsn
+connecting to dsn
+</pre>
+<p>For consistency with the way the usage message is printed, I suggest to
+follow the FOP convention: in the <tt class="docutils literal">main</tt> function write first
+the flag arguments, then the option arguments and finally the positional
+arguments. This is a convention and you are not forced to use it, but
+it makes sense to put the position arguments at the end, since they
+may be default arguments and varargs. In this document I will always use
+the FOP convention.</p>
+</div>
+<div class="section" id="clap-is-also-for-people-not-using-python-3">
+<h1>clap is also for people not using Python 3</h1>
+<p>I do not use Python 3. At work we are just starting to think about
+migrating to Python 2.6. I think it will take years before we even
+think to migrate to Python 3. I am pretty much sure most Pythonistas
+are in the same situation. Therefore <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a> provides a way to work
+with function annotations even in Python 2.X (including Python 2.3).
+There is no magic involved; you just need to add the annotations
+by hand. For instance</p>
+<pre class="literal-block">
+def main(dsn: (&quot;Database dsn&quot;,), *scripts: (&quot;SQL scripts&quot;,)):
+</pre>
+<p>becomes:</p>
+<pre class="literal-block">
+def main(dsn, *scripts):
+ ...
+main.__annotations__ = dict(
+dsn= (&quot;Database dsn&quot;,),
+scripts=(&quot;SQL scripts&quot;,))
+</pre>
+<p>One should be careful to much the keys of the annotations dictionary
+with the names of the arguments in the annotated function; for lazy
+people with Python 2.4 available the simplest way is to use the
+<tt class="docutils literal">clap.annotations</tt> decorator that performs the check for you.</p>
+<pre class="literal-block">
+&#64;annotations(
+dsn= (&quot;Database dsn&quot;,),
+scripts=(&quot;SQL scripts&quot;,))
+def main(dsn, *scripts):
+ ...
+</pre>
+<p>In the rest of this article I will assume that you are using Python 2.X with
+X &gt;= 4 and I will use the <tt class="docutils literal">clap.annotations</tt> decorator.</p>
+</div>
+<div class="section" id="advanced-usage">
+<h1>Advanced usage</h1>
+<p>One of the goals of clap is to have a learning curve of <em>minutes</em>, compared
+to the learning curve of <em>hours</em> of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. That does not mean
+that I have removed all the advanced features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually
+a lot of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> power persists in <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</a>: in particular, the
+<tt class="docutils literal">type</tt>, <tt class="docutils literal">choices</tt> and <tt class="docutils literal">metavar</tt> concepts are there.
+Here is an example showing all of them:</p>
+<blockquote>
+<pre class="literal-block">
+import clap
+
+&#64;clap.annotations(
+operator=(&quot;The name of an operator&quot;, None, str, ['add', 'mul']),
+numbers=(&quot;A number&quot;, None, float, None, &quot;n&quot;))
+def main(operator, *numbers):
+ op = getattr(float, '__%s__' % operator)
+ result = dict(add=0.0, mul=1.0)[operator]
+ for n in numbers:
+ result = op(result, n)
+ print(result)
+
+if __name__ == '__main__':
+ clap.call(main)
+
+</pre>
+</blockquote>
+<p>Let me begin by discussing the <tt class="docutils literal">type</tt> feature: given any callable
+taking a string in input a returning any Python object, it is possible
+to automagically convert the parsed arguments with the callable, simply
+by listing it in the annotation</p>
+</div>
+</div>
+</body>
+</html>
diff --git a/clap/article/article.pdf b/clap/article/article.pdf
new file mode 100644
index 0000000..cb3f3dc
--- /dev/null
+++ b/clap/article/article.pdf
@@ -0,0 +1,2418 @@
+%PDF-1.3
+%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
+% 'BasicFonts': class PDFDictionary
+1 0 obj
+% The standard fonts dictionary
+<< /F1 2 0 R
+ /F2 3 0 R
+ /F3 14 0 R
+ /F4 16 0 R
+ /F5 17 0 R >>
+endobj
+% 'F1': class PDFType1Font
+2 0 obj
+% Font Helvetica
+<< /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'F2': class PDFType1Font
+3 0 obj
+% Font Helvetica-Bold
+<< /BaseFont /Helvetica-Bold
+ /Encoding /WinAnsiEncoding
+ /Name /F2
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER1': class PDFDictionary
+4 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/getopt.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 361.1228
+ 686.5936
+ 392.9767
+ 698.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER2': class PDFDictionary
+5 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 493.6727
+ 686.5936
+ 531.3087
+ 698.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER3': class PDFDictionary
+6 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 164.0504
+ 674.5936
+ 206.7572
+ 686.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER4': class PDFDictionary
+7 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 493.1227
+ 674.5936
+ 532.1158
+ 686.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER5': class PDFDictionary
+8 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 95.71512
+ 632.5936
+ 114.0551
+ 644.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER6': class PDFDictionary
+9 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 120.9573
+ 632.5936
+ 143.4195
+ 644.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER7': class PDFDictionary
+10 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 227.1684
+ 632.5936
+ 320.7606
+ 644.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER8': class PDFDictionary
+11 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 337.6822
+ 620.5936
+ 359.0965
+ 632.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER9': class PDFDictionary
+12 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 490.3427
+ 620.5936
+ 529.8027
+ 632.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER10': class PDFDictionary
+13 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 346.3284
+ 608.5936
+ 367.4652
+ 620.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F3': class PDFType1Font
+14 0 obj
+% Font Courier
+<< /BaseFont /Courier
+ /Encoding /WinAnsiEncoding
+ /Name /F3
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER11': class PDFDictionary
+15 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 471.0489
+ 407.5936
+ 529.8027
+ 419.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F4': class PDFType1Font
+16 0 obj
+% Font Helvetica-Oblique
+<< /BaseFont /Helvetica-Oblique
+ /Encoding /WinAnsiEncoding
+ /Name /F4
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'F5': class PDFType1Font
+17 0 obj
+% Font Times-Roman
+<< /BaseFont /Times-Roman
+ /Encoding /WinAnsiEncoding
+ /Name /F5
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Page1': class PDFPage
+18 0 obj
+% Page dictionary
+<< /Annots [ 4 0 R
+ 5 0 R
+ 6 0 R
+ 7 0 R
+ 8 0 R
+ 9 0 R
+ 10 0 R
+ 11 0 R
+ 12 0 R
+ 13 0 R
+ 15 0 R ]
+ /Contents 69 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER12': class PDFDictionary
+19 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/getopt.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 504.7827
+ 756.5936
+ 532.1006
+ 768.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER13': class PDFDictionary
+20 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 82.40665
+ 744.5936
+ 124.3504
+ 756.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER14': class PDFDictionary
+21 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 732.5936
+ 104.9329
+ 744.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER15': class PDFDictionary
+22 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.82606
+ 533.3936
+ 106.0692
+ 545.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER16': class PDFDictionary
+23 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 243.8829
+ 521.3936
+ 265.0029
+ 533.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER17': class PDFDictionary
+24 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.6329
+ 412.1936
+ 105.6829
+ 424.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER18': class PDFDictionary
+25 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 421.9727
+ 412.1936
+ 465.1427
+ 424.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER19': class PDFDictionary
+26 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 211.6529
+ 262.9936
+ 232.7729
+ 274.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page2': class PDFPage
+27 0 obj
+% Page dictionary
+<< /Annots [ 19 0 R
+ 20 0 R
+ 21 0 R
+ 22 0 R
+ 23 0 R
+ 24 0 R
+ 25 0 R
+ 26 0 R ]
+ /Contents 70 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER20': class PDFDictionary
+28 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.47291
+ 665.3936
+ 106.5929
+ 677.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER21': class PDFDictionary
+29 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 450.9936
+ 84.20915
+ 462.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER22': class PDFDictionary
+30 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 97.95597
+ 257.7936
+ 116.296
+ 269.7936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER23': class PDFDictionary
+31 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 446.6187
+ 96.59362
+ 464.9587
+ 108.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page3': class PDFPage
+32 0 obj
+% Page dictionary
+<< /Annots [ 28 0 R
+ 29 0 R
+ 30 0 R
+ 31 0 R ]
+ /Contents 71 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER24': class PDFDictionary
+33 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 446.8103
+ 681.5936
+ 502.5727
+ 693.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER25': class PDFDictionary
+34 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 260.18
+ 669.5936
+ 312.43
+ 681.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER26': class PDFDictionary
+35 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 639.5936
+ 84.28901
+ 651.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER27': class PDFDictionary
+36 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 161.7834
+ 639.5936
+ 217.2895
+ 651.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER28': class PDFDictionary
+37 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 133.1479
+ 440.3936
+ 154.4129
+ 452.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page4': class PDFPage
+38 0 obj
+% Page dictionary
+<< /Annots [ 33 0 R
+ 34 0 R
+ 35 0 R
+ 36 0 R
+ 37 0 R ]
+ /Contents 72 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER29': class PDFDictionary
+39 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 185.8554
+ 711.3936
+ 207.616
+ 723.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER30': class PDFDictionary
+40 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 162.7329
+ 556.9936
+ 181.0729
+ 568.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER31': class PDFDictionary
+41 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 479.7936
+ 84.57878
+ 491.7936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER32': class PDFDictionary
+42 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 185.1229
+ 150.3936
+ 208.3329
+ 162.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page5': class PDFPage
+43 0 obj
+% Page dictionary
+<< /Annots [ 39 0 R
+ 40 0 R
+ 41 0 R
+ 42 0 R ]
+ /Contents 73 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER33': class PDFDictionary
+44 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 455.1936
+ 102.1529
+ 467.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER34': class PDFDictionary
+45 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 418.9683
+ 455.1936
+ 458.4283
+ 467.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER35': class PDFDictionary
+46 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 443.1936
+ 106.4622
+ 455.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER36': class PDFDictionary
+47 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 188.85
+ 443.1936
+ 207.19
+ 455.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER37': class PDFDictionary
+48 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 407.8229
+ 263.1936
+ 450.0629
+ 275.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page6': class PDFPage
+49 0 obj
+% Page dictionary
+<< /Annots [ 44 0 R
+ 45 0 R
+ 46 0 R
+ 47 0 R
+ 48 0 R ]
+ /Contents 74 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER38': class PDFDictionary
+50 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 475.0549
+ 84.7414
+ 487.0549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER39': class PDFDictionary
+51 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 136.4369
+ 475.0549
+ 179.6054
+ 487.0549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER40': class PDFDictionary
+52 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 463.0549
+ 138.3111
+ 475.0549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER41': class PDFDictionary
+53 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 127.9872
+ 325.8549
+ 149.3819
+ 337.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER42': class PDFDictionary
+54 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 480.5388
+ 313.8549
+ 524.2427
+ 325.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER43': class PDFDictionary
+55 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 225.5228
+ 283.8549
+ 301.2571
+ 295.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER44': class PDFDictionary
+56 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 116.6549
+ 84.35423
+ 128.6549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page7': class PDFPage
+57 0 obj
+% Page dictionary
+<< /Annots [ 50 0 R
+ 51 0 R
+ 52 0 R
+ 53 0 R
+ 54 0 R
+ 55 0 R
+ 56 0 R ]
+ /Contents 75 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Page8': class PDFPage
+58 0 obj
+% Page dictionary
+<< /Contents 76 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 68 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'R59': class PDFCatalog
+59 0 obj
+% Document Root
+<< /Outlines 61 0 R
+ /PageLabels 77 0 R
+ /PageMode /UseNone
+ /Pages 68 0 R
+ /Type /Catalog >>
+endobj
+% 'R60': class PDFInfo
+60 0 obj
+<< /Author ()
+ /CreationDate (D:20100531105058-01'00')
+ /Keywords ()
+ /Producer (ReportLab http://www.reportlab.com)
+ /Subject (\(unspecified\))
+ /Title (The Easiest Command Line Arguments Parser in the World) >>
+endobj
+% 'R61': class PDFOutlines
+61 0 obj
+<< /Count 6
+ /First 62 0 R
+ /Last 67 0 R
+ /Type /Outlines >>
+endobj
+% 'Outline.0': class OutlineEntryObject
+62 0 obj
+<< /Dest [ 18 0 R
+ /XYZ
+ 62.69291
+ 443.0236
+ 0 ]
+ /Next 63 0 R
+ /Parent 61 0 R
+ /Title (The importance of scaling down) >>
+endobj
+% 'Outline.1': class OutlineEntryObject
+63 0 obj
+<< /Dest [ 27 0 R
+ /XYZ
+ 62.69291
+ 247.4236
+ 0 ]
+ /Next 64 0 R
+ /Parent 61 0 R
+ /Prev 62 0 R
+ /Title (Positional default arguments) >>
+endobj
+% 'Outline.2': class OutlineEntryObject
+64 0 obj
+<< /Dest [ 38 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Next 65 0 R
+ /Parent 61 0 R
+ /Prev 63 0 R
+ /Title (Options and flags) >>
+endobj
+% 'Outline.3': class OutlineEntryObject
+65 0 obj
+<< /Dest [ 43 0 R
+ /XYZ
+ 62.69291
+ 209.8236
+ 0 ]
+ /Next 66 0 R
+ /Parent 61 0 R
+ /Prev 64 0 R
+ /Title (clap for people not using Python 3) >>
+endobj
+% 'Outline.4': class OutlineEntryObject
+66 0 obj
+<< /Dest [ 49 0 R
+ /XYZ
+ 62.69291
+ 502.6236
+ 0 ]
+ /Next 67 0 R
+ /Parent 61 0 R
+ /Prev 65 0 R
+ /Title (Advanced usage) >>
+endobj
+% 'Outline.5': class OutlineEntryObject
+67 0 obj
+<< /Dest [ 57 0 R
+ /XYZ
+ 62.69291
+ 510.4849
+ 0 ]
+ /Parent 61 0 R
+ /Prev 66 0 R
+ /Title (A few notes on the underlying implementation) >>
+endobj
+% 'R68': class PDFPages
+68 0 obj
+% page tree
+<< /Count 8
+ /Kids [ 18 0 R
+ 27 0 R
+ 32 0 R
+ 38 0 R
+ 43 0 R
+ 49 0 R
+ 57 0 R
+ 58 0 R ]
+ /Type /Pages >>
+endobj
+% 'R69': class PDFStream
+69 0 obj
+% page stream
+<< /Length 6154 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 717.0236 cm
+q
+BT 1 0 0 1 0 33.64 Tm 3.214882 0 Td 24 TL /F2 20 Tf 0 0 0 rg (The Easiest Command Line Arguments Parser in) Tj T* 185.62 0 Td (the World) Tj T* -188.8349 0 Td ET
+Q
+Q
+q
+1 0 0 1 62.69291 647.0236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .05061 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is no want of command line arguments parsers in Python world. The standard library alone contains) Tj T* 0 Tw 1.273984 Tw (three different modules for the parsing of command line options: ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (\(from the stone age\), ) Tj 0 0 .501961 rg (optparse) Tj T* 0 Tw .46686 Tw 0 0 0 rg (\(from Python 2.3\) and ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (\(from Python 2.7\). All of them are quite powerful and especially ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw .847485 Tw 0 0 0 rg (is an industrial strength solution; unfortunately, all of them have a non-zero learning curve and a certain) Tj T* 0 Tw (verbosity.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 557.0236 cm
+q
+BT 1 0 0 1 0 76.82 Tm 1.342209 Tw 12 TL /F1 10 Tf 0 0 0 rg (Enters ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (. ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is designed to be ) Tj 0 0 .501961 rg (downwardly scalable) Tj 0 0 0 rg (, i.e. to be trivially simple to use for trivial use) Tj T* 0 Tw .29436 Tw (cases, and to have a next-to-zero learning curve. Technically ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is just a simple wrapper over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (,) Tj T* 0 Tw .01686 Tw (hiding most of the complexity while retaining most of the power. ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is surprisingly scalable upwards even) Tj T* 0 Tw .569398 Tw (for non-trivial use cases, but it is not intended to be an industrial strength command line parsing module.) Tj T* 0 Tw .07104 Tw (Its capabilities are limited by design. If you need more power, by all means use the parsing modules in the) Tj T* 0 Tw .51237 Tw (standard library. Still, I have been using Python for 8 years and never once I had to use the full power of) Tj T* 0 Tw (the standard library modules.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 455.0236 cm
+q
+BT 1 0 0 1 0 88.82 Tm .536457 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually I am pretty much convinced that features provided by ) Tj /F3 10 Tf (clap ) Tj /F1 10 Tf (are more than enough for 99.9% of) Tj T* 0 Tw .60811 Tw (the typical use cases of a scripter working in a Unix-like environment. I am targetting here programmers,) Tj T* 0 Tw .337126 Tw (sys-admins, scientists and in general people writing throw-away scripts for themselves, choosing to use a) Tj T* 0 Tw .242339 Tw (command line interface because it is the quick and simple. Such users are not interested in features, they) Tj T* 0 Tw 2.177882 Tw (just want to be able to write a simple command line tool from a simple specification, not to build a) Tj T* 0 Tw .014985 Tw (command line parser by hand. Unfortunately, the current modules in the standard library forces them to go) Tj T* 0 Tw 4.664269 Tw (the hard way. They are designed to implement power user tools for programmers or system) Tj T* 0 Tw (administrators, and they have a non-trivial learning curve.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 422.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The importance of scaling down) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 320.0236 cm
+q
+BT 1 0 0 1 0 88.82 Tm .953735 Tw 12 TL /F1 10 Tf 0 0 0 rg (An ex-coworker of mine, David Welton, once wrote a nice article about the importance of ) Tj 0 0 .501961 rg (scaling down) Tj 0 0 0 rg (:) Tj T* 0 Tw 1.026457 Tw (most people are concerned with the possibility of scaling up, but we should also be concerned with the) Tj T* 0 Tw .754987 Tw (issue of scaling down: in other worlds, simple things should be kept simple. To be concrete, let me start) Tj T* 0 Tw .567765 Tw (with the simplest possible thing: a script that takes a single argument and does something to it. It cannot) Tj T* 0 Tw 1.314651 Tw (get more trivial than that \(discarding the possibility of a script without command line arguments, where) Tj T* 0 Tw .188935 Tw (there is nothing to parse\), nevertheless it is a use case ) Tj /F4 10 Tf (extremely common) Tj /F1 10 Tf (: I need to write scripts like that) Tj T* 0 Tw .539513 Tw (nearly every day, I wrote hundreds of them in the last few years and I have never been happy. Here is a) Tj T* 0 Tw (typical example of code I have been writing by hand for years:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 314.0236 cm
+Q
+q
+1 0 0 1 62.69291 144.8236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 168 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 149.71 Tm /F3 10 Tf 12 TL (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( n = len\(sys.argv[1:]\)) Tj T* ( if n == 0:) Tj T* ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif n == 1:) Tj T* ( main\(sys.argv[1]\)) Tj T* ( else:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(sys.argv[2:]\)\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 144.8236 cm
+Q
+q
+1 0 0 1 62.69291 78.82362 cm
+q
+BT 1 0 0 1 0 52.82 Tm .880651 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see the whole ) Tj /F3 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block \(nine lines\) is essentially boilerplate that ) Tj T* 0 Tw 1.125318 Tw (should not exists. Actually I think the Python language should recognize the main function and perform ) Tj T* 0 Tw 1.385984 Tw (trivial arguments parsing behind the scenes; unfortunaly this is unlikely to happen. I have been writing ) Tj T* 0 Tw 1.767356 Tw (boilerplate like this in hundreds of scripts for years, and every time I ) Tj /F4 10 Tf (hate ) Tj /F1 10 Tf (it. The purpose of using a ) Tj T* 0 Tw 1.47229 Tw (scripting language is convenience and trivial things should be trivial. Unfortunately the standard library) Tj T* 0 Tw ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R70': class PDFStream
+70 0 obj
+% page stream
+<< /Length 4633 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 729.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .482093 Tw 12 TL /F1 10 Tf 0 0 0 rg (modules do not help for this use case, which may be trivial, but it is still incredibly common. Using ) Tj 0 0 .501961 rg (getopt) Tj T* 0 Tw .253735 Tw 0 0 0 rg (and ) Tj 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (does not help, since they are intended to manage options and not positional arguments; the) Tj T* 0 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module helps a bit and it is able to reduce the boilerplate from nine lines to six lines:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 723.0236 cm
+Q
+q
+1 0 0 1 62.69291 589.8236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 132 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (def main\(dsn\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import argparse) Tj T* ( p = argparse.ArgumentParser\(\)) Tj T* ( p.add_argument\('dsn'\)) Tj T* ( arg = p.parse_args\(\)) Tj T* ( main\(arg.dsn\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 589.8236 cm
+Q
+q
+1 0 0 1 62.69291 547.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 28.82 Tm /F1 10 Tf 12 TL 1.644269 Tw (However saving three lines does not justify introducing the external dependency: most people will not) Tj T* 0 Tw .276303 Tw (switch Python 2.7, which at the time of this writing is just about to be released, for many years. Moreover,) Tj T* 0 Tw (it just feels too complex to instantiate a class and to define a parser by hand for such a trivial task.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 517.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.123145 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (module is designed to manage well such use cases, and it is able to reduce the original nine) Tj T* 0 Tw (lines of boiler plate to two lines. With the ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (module all you need to write is) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 511.8236 cm
+Q
+q
+1 0 0 1 62.69291 426.6236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 84 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 65.71 Tm /F3 10 Tf 12 TL (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 426.6236 cm
+Q
+q
+1 0 0 1 62.69291 396.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .929986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (module provides for free \(actually the work is done by the underlying ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module\) a nice) Tj T* 0 Tw (usage message:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 279.4236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL ($ python example3.py -h) Tj T* (usage: example3.py [-h] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 259.4236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (This is only the tip of the iceberg: ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is able to do much more than that.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 226.4236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Positional default arguments) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 208.4236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (I have encountered this use case at work hundreds of times:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 202.4236 cm
+Q
+q
+1 0 0 1 62.69291 76.86614 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 442.6898 108 re B*
+Q
+q
+BT 1 0 0 1 0 89.71 Tm 12 TL /F3 10 Tf 0 0 0 rg (from datetime import datetime) Tj T* T* (def main\(dsn, table='product', today=datetime.today\(\)\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn, table, today\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+
+endstream
+
+endobj
+% 'R71': class PDFStream
+71 0 obj
+% page stream
+<< /Length 4390 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 679.8236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 442.6898 84 re B*
+Q
+q
+BT 1 0 0 1 0 65.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ( args = sys.argv[1:]) Tj T* ( if not args:) Tj T* ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif len\(args\) ) Tj (>) Tj ( 2:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(argv[2:]\)\)) Tj T* ( main\(*args\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 679.8236 cm
+Q
+q
+1 0 0 1 62.69291 661.8236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (With ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (the entire ) Tj /F3 10 Tf (__main__ ) Tj /F1 10 Tf (block reduces to the usual two lines:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 616.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 596.6236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (In other words, six lines of boilerplate have been removed, and I have the usage message for free:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 467.4236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 120 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL (usage: example4_.py [-h] dsn [table] [today]) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( table) Tj T* ( today) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 435.4236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .396235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (manages transparently even the case when you want to pass a variable number of arguments. Here) Tj T* 0 Tw (is an example, a script running on a database a series of ) Tj /F3 10 Tf (.sql ) Tj /F1 10 Tf (scripts:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 429.4236 cm
+Q
+q
+1 0 0 1 62.69291 272.2236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 156 re B*
+Q
+q
+BT 1 0 0 1 0 137.71 Tm 12 TL /F3 10 Tf 0 0 0 rg (from datetime import datetime) Tj T* T* (def main\(dsn, *scripts\):) Tj T* ( "Run the given scripts on the database") Tj T* ( for script in scripts:) Tj T* ( print\('executing %s' % script\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( if len\(sys.argv\) ) Tj (<) Tj ( 2:) Tj T* ( sys.exit\('usage: python %s dsn script.sql ...' % sys.argv[0]\)) Tj T* ( main\(sys.argv[1:]\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 272.2236 cm
+Q
+q
+1 0 0 1 62.69291 242.2236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 6.923059 Tw 12 TL /F1 10 Tf 0 0 0 rg (Using ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (, you can just replace the ) Tj /F3 10 Tf (__main__ ) Tj /F1 10 Tf (block with the usual ) Tj /F3 10 Tf (import clap;) Tj T* 0 Tw (clap.call\(main\) ) Tj /F1 10 Tf (and you get the following usage message:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 125.0236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL (usage: example7.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( scripts) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 81.02362 cm
+q
+BT 1 0 0 1 0 28.82 Tm .92881 Tw 12 TL /F1 10 Tf 0 0 0 rg (The examples here should have made clear that ) Tj /F4 10 Tf (clap is able to figure out the command line arguments) Tj T* 0 Tw .928488 Tw (parser to use from the signature of the main function) Tj /F1 10 Tf (. This is the whole idea behind ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (: if my intent is) Tj T* 0 Tw (clear, let's the machine takes care of the details.) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R72': class PDFStream
+72 0 obj
+% page stream
+<< /Length 5415 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 744.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Options and flags) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 654.0236 cm
+q
+BT 1 0 0 1 0 76.82 Tm .046098 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is surprising how few command line scripts with options I have written over the years \(probably less than) Tj T* 0 Tw 1.165984 Tw (a hundred\), compared to the number of scripts with positional arguments \(I certainly have written more) Tj T* 0 Tw 1.221163 Tw (than a thousand of them\). Still, this use case is quite common and cannot be neglected. The standard) Tj T* 0 Tw .446098 Tw (library modules \(all of them\) are quite verbose when it comes to specifying the options and frankly I have) Tj T* 0 Tw .732339 Tw (never used them directly. Instead, I have always relied on an old recipe of mine, the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw 1.32784 Tw (which provides a convenient wrapper over ) Tj 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (. Alternatively, in the simplest cases, I have just) Tj T* 0 Tw (performed the parsing by hand, instead of manually building a suitable OptionParser.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .476098 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is inspired to the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe, in the sense that it delivers the programmer from the burden of) Tj T* 0 Tw .011488 Tw (writing the parser, but is less of a hack: instead of extracting the parser from the docstring of the module, it) Tj T* 0 Tw (extracts it from the signature of the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 582.0236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .319987 Tw 12 TL /F1 10 Tf 0 0 0 rg (The idea comes from the ) Tj /F4 10 Tf (function annotations ) Tj /F1 10 Tf (concept, a new feature of Python 3. An example is worth a) Tj T* 0 Tw (thousand words, so here it is:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 576.0236 cm
+Q
+q
+1 0 0 1 62.69291 478.8236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 96 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F3 10 Tf 12 TL (def main\(command: \("SQL query", 'option', 'c'\), dsn\):) Tj T* ( if command:) Tj T* ( print\('executing %s on %s' % \(command, dsn\)\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 478.8236 cm
+Q
+q
+1 0 0 1 62.69291 412.8236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .789983 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see, the argument ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (has been annotated with the tuple ) Tj /F3 10 Tf (\("SQL query", 'option',) Tj T* 0 Tw .593876 Tw ('c'\)) Tj /F1 10 Tf (: the first string is the help string which will appear in the usage message, whereas the second and) Tj T* 0 Tw .144988 Tw (third strings tell ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (that ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (is an option and that it can be abbreviated with the letter ) Tj /F3 10 Tf (c) Tj /F1 10 Tf (. Of course,) Tj T* 0 Tw 1.543735 Tw (it also possible to use the long option format, by prefixing the option with ) Tj /F3 10 Tf (--command=) Tj /F1 10 Tf (. The resulting) Tj T* 0 Tw (usage message is the following:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 271.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 132 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL ($ python3 example8.py -h) Tj T* (usage: example8.py [-h] [-c COMMAND] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -c COMMAND, --command COMMAND) Tj T* ( SQL query) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 251.6236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here are two examples of usage:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 170.4236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 72 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 53.71 Tm /F3 10 Tf 12 TL ($ python3 example8.py -c"select * from table" dsn) Tj T* (executing select * from table on dsn) Tj T* T* ($ python3 example8.py --command="select * from table" dsn) Tj T* (executing select * from table on dsn) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 150.4236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that if the option is not passed, the variable ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (will get the value ) Tj /F3 10 Tf (None) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 132.4236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Even positional argument can be annotated:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 87.22362 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (def main\(command: \("SQL query", 'option', 'c'\),) Tj T* ( dsn: \("Database dsn", 'positional', None\)\):) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+
+endstream
+
+endobj
+% 'R73': class PDFStream
+73 0 obj
+% page stream
+<< /Length 5431 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 739.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 24 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL ( ...) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 695.8236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 3.203318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Of course explicit is better than implicit, an no special cases are special enough, but sometimes) Tj T* 0 Tw .64061 Tw (practicality beats purity, so ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is smart enough to convert help messages into tuples internally; in other) Tj T* 0 Tw (words, you can just write "Database dsn" instead of ) Tj /F3 10 Tf (\("Database dsn", 'positional', None\)) Tj /F1 10 Tf (:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 650.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (def main\(command: \("SQL query", 'option', 'c'\), dsn: "Database dsn"\):) Tj T* ( ...) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 618.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .171988 Tw 12 TL /F1 10 Tf 0 0 0 rg (In both cases the usage message will show a nice help string on the right hand side of the ) Tj /F3 10 Tf (dsn ) Tj /F1 10 Tf (positional) Tj T* 0 Tw (argument. varargs \(starred-arguments\) can also be annotated:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 573.4236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ( ...) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 553.4236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (is a valid signature for ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (, which will recognize the help strings for both ) Tj /F3 10 Tf (dsn ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (scripts) Tj /F1 10 Tf (:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 496.2236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 48 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 29.71 Tm /F3 10 Tf 12 TL (positional arguments:) Tj T* ( dsn Database dsn) Tj T* ( scripts SQL scripts) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 464.2236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .765868 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (also recognizes flags, i.e. boolean options which are ) Tj /F3 10 Tf (True ) Tj /F1 10 Tf (if they are passed to the command line) Tj T* 0 Tw (and ) Tj /F3 10 Tf (False ) Tj /F1 10 Tf (if they are absent. Here is an example:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 419.0236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL ($ python3 example9.py -v dsn) Tj T* (connecting to dsn) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 289.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 120 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL ($ python3 example9.py -h) Tj T* (usage: example9.py [-h] [-v] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn connection string) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -v, --verbose prints more info) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 221.8236 cm
+q
+BT 1 0 0 1 0 52.82 Tm 5.832651 Tw 12 TL /F1 10 Tf 0 0 0 rg (For consistency with the way the usage message is printed, I suggest you to follow the) Tj T* 0 Tw 1.686905 Tw (Flag-Option-Positional \(FOP\) convention: in the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function write first the flag arguments, then the) Tj T* 0 Tw .404692 Tw (option arguments and finally the positional arguments. This is just a convention and you are not forced to) Tj T* 0 Tw .478409 Tw (use it, but it makes sense to put the position arguments at the end, since they may be default arguments) Tj T* 0 Tw (and varargs.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 188.8236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (clap for people not using Python 3) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 122.8236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .12999 Tw 12 TL /F1 10 Tf 0 0 0 rg (I do not use Python 3. At work we are just starting to think about migrating to Python 2.6. I think it will take) Tj T* 0 Tw 1.269988 Tw (years before we even think to migrate to Python 3. I am pretty much sure most Pythonistas are in the) Tj T* 0 Tw 2.089984 Tw (same situation. Therefore ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (provides a way to work with function annotations even in Python 2.X) Tj T* 0 Tw 1.352339 Tw (\(including Python 2.3\). There is no magic involved; you just need to add the annotations by hand. For) Tj T* 0 Tw (instance) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 89.62362 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 24 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+
+endstream
+
+endobj
+% 'R74': class PDFStream
+74 0 obj
+% page stream
+<< /Length 5574 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 753.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (becomes:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 671.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 72 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 53.71 Tm /F3 10 Tf 12 TL (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* (main.__annotations__ = dict\() Tj T* (dsn="Database dsn",) Tj T* (scripts="SQL scripts"\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 627.8236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .412765 Tw 12 TL /F1 10 Tf 0 0 0 rg (One should be careful to much the keys of the annotations dictionary with the names of the arguments in) Tj T* 0 Tw 3.347485 Tw (the annotated function; for lazy people with Python 2.4 available the simplest way is to use the) Tj T* 0 Tw /F3 10 Tf (clap.annotations ) Tj /F1 10 Tf (decorator that performs the check for you.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 546.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 72 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 53.71 Tm /F3 10 Tf 12 TL (@annotations\() Tj T* (dsn="Database dsn",) Tj T* (scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 514.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.422164 Tw 12 TL /F1 10 Tf 0 0 0 rg (In the rest of this article I will assume that you are using Python 2.X with ) Tj /F3 10 Tf (X >) Tj (= 4 ) Tj /F1 10 Tf (and I will use the) Tj T* 0 Tw /F3 10 Tf (clap.annotations ) Tj /F1 10 Tf (decorator.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 481.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Advanced usage) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 427.6236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .115703 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the goals of clap is to have a learning curve of ) Tj /F4 10 Tf (minutes) Tj /F1 10 Tf (, compared to the learning curve of ) Tj /F4 10 Tf (hours ) Tj /F1 10 Tf (of) Tj T* 0 Tw .196098 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. That does not mean that I have removed all the advanced features of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually a lot of) Tj T* 0 Tw 1.529269 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (power persists in ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (: in particular, the ) Tj /F3 10 Tf (type) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (choices ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (metavar ) Tj /F1 10 Tf (concepts are there.) Tj T* 0 Tw (Until now, I have only showed simple annotations, but in general an annotation is a 5-tuple of the form) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 421.6236 cm
+Q
+q
+1 0 0 1 62.69291 409.6236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL (\(help, kind, abbrev, type, choices, metavar\)) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 409.6236 cm
+Q
+q
+1 0 0 1 62.69291 367.6236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 3.38811 Tw 12 TL /F1 10 Tf 0 0 0 rg (where ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (is the help message, ) Tj /F3 10 Tf (kind ) Tj /F1 10 Tf (is one of {"flag", "option ", "positional"}, ) Tj /F3 10 Tf (abbrev ) Tj /F1 10 Tf (is a) Tj T* 0 Tw 2.203735 Tw (one-character string, ) Tj /F3 10 Tf (type ) Tj /F1 10 Tf (is callable taking a string in input, choices is a sequence of values and) Tj T* 0 Tw /F3 10 Tf (metavar ) Tj /F1 10 Tf (is a string.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 337.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .006654 Tw 12 TL /F3 10 Tf 0 0 0 rg (type ) Tj /F1 10 Tf (is used to automagically convert the arguments from string to any Python type; by default there is no) Tj T* 0 Tw (convertion i.e. ) Tj /F3 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 307.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.904692 Tw 12 TL /F3 10 Tf 0 0 0 rg (choices ) Tj /F1 10 Tf (is used to restrict the number of the valid options; by default there is no restriction i.e.) Tj T* 0 Tw /F3 10 Tf (choices=None) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 277.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.071751 Tw 12 TL /F3 10 Tf 0 0 0 rg (metavar ) Tj /F1 10 Tf (is used to change the argument name in the usage message \(and only there\); by default the) Tj T* 0 Tw (metavar is equal to the name of the argument.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 259.6236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Here is an example showing all of such features \(shamelessly stolen from the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation\):) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 253.6236 cm
+Q
+q
+1 0 0 1 62.69291 84.21376 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+.934933 0 0 .934933 0 0 cm
+q
+1 0 0 1 6.6 7.059329 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 480 180 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 161.71 Tm /F3 10 Tf 12 TL (import clap) Tj T* T* (@clap.annotations\() Tj T* (operator=\("The name of an operator", 'positional', None, str, ['add', 'mul']\),) Tj T* (numbers=\("A number", 'positional', None, float, None, "n"\)\)) Tj T* (def main\(operator, *numbers\):) Tj T* ( op = getattr\(float, '__%s__' % operator\)) Tj T* ( result = dict\(add=0.0, mul=1.0\)[operator]) Tj T* ( for n in numbers:) Tj T* ( result = op\(result, n\)) Tj T* ( print\(result\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( clap.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 84.21376 cm
+Q
+
+endstream
+
+endobj
+% 'R75': class PDFStream
+75 0 obj
+% page stream
+<< /Length 5160 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 753.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage for the script:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 635.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* T* (positional arguments:) Tj T* ( {add,mul} The name of an operator) Tj T* ( n A number) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 615.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here are a couple of examples of use:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 522.4849 cm
+q
+q
+.87797 0 0 .87797 0 0 cm
+q
+1 0 0 1 6.6 7.517338 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 534 96 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F3 10 Tf 12 TL ($ python example10.py add 1 2 3 4) Tj T* (10.0) Tj T* ($ python example10.py mul 1 2 3 4) Tj T* (24.0) Tj T* ($ python example10.py ad 1 2 3 4 # a mispelling error) Tj T* (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* (example10.py: error: argument operator: invalid choice: 'ad' \(choose from 'add', 'mul'\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 489.4849 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A few notes on the underlying implementation) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 423.4849 cm
+q
+BT 1 0 0 1 0 52.82 Tm .928488 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (relies on a ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (for all of the heavy lifting work. It is possible to pass options to the underlying) Tj T* 0 Tw .03816 Tw 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object \(currently it accepts the default arguments ) Tj /F3 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (epilog) Tj /F1 10 Tf (,) Tj T* 0 Tw 18.21744 Tw /F3 10 Tf (version) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (formatter_class) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.035976 Tw /F3 10 Tf (argument_default) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (add_help) Tj /F1 10 Tf (\) simply by setting such attributes on the ) Tj /F3 10 Tf (main) Tj T* 0 Tw /F1 10 Tf (function. For instance) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 354.2849 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 60 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F3 10 Tf 12 TL (def main\(...\):) Tj T* ( pass) Tj T* T* (main.add_help = False) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 298.2849 cm
+q
+BT 1 0 0 1 0 40.82 Tm 1.256457 Tw 12 TL /F1 10 Tf 0 0 0 rg (disable the recognition of the help flag ) Tj /F3 10 Tf (-h, --help) Tj /F1 10 Tf (. This is not particularly elegant, but I assume the) Tj T* 0 Tw .274751 Tw (typical user of ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (will be happy with the default message and would not want to go at this level of detail;) Tj T* 0 Tw 1.463876 Tw (still it is possible if she wants to. I redirect the interested readers to the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to) Tj T* 0 Tw (understand the meaning of the various options.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 268.2849 cm
+q
+BT 1 0 0 1 0 16.82 Tm .154269 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you want to access the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, you can use the ) Tj /F3 10 Tf (clap.parser_from ) Tj /F1 10 Tf (utility) Tj T* 0 Tw (function:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 151.0849 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+BT 1 0 0 1 0 89.71 Tm 12 TL /F3 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import clap) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print clap.parser_from\(main\)) Tj T* (ArgumentParser\(prog='', usage=None, description=None, version=None,) Tj T* (formatter_class=) Tj (<) Tj (class 'argparse.HelpFormatter') Tj (>) Tj (, conflict_handler='error',) Tj T* (add_help=True\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 131.0849 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F3 10 Tf (clap.parser_from ) Tj /F1 10 Tf (in the unit tests of the module, but regular users should never need to use it.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 101.0849 cm
+q
+BT 1 0 0 1 0 16.82 Tm .541318 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (uses an ) Tj /F3 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the raw annotations in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F3 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R76': class PDFStream
+76 0 obj
+% page stream
+<< /Length 3056 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 717.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 40.82 Tm /F1 10 Tf 12 TL 1.682126 Tw (Advanced users can implement their own annotation objects. Since the special case of no annotation) Tj T* 0 Tw 1.088735 Tw (must be taken care of, the annotation factory must return a suitable default annotation object where no) Tj T* 0 Tw .18811 Tw (arguments are passed in input. Here is an example of how you could implement annotations for positional) Tj T* 0 Tw (arguments:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 711.0236 cm
+Q
+q
+1 0 0 1 62.69291 601.8236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 108 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL (class Positional\(object\):) Tj T* ( def __init__\(self, help='', type=None, choices=None, metavar=None\):) Tj T* ( self.help = help) Tj T* ( self.kind = 'positional') Tj T* ( self.abbrev = None) Tj T* ( self.type = type) Tj T* ( self.choices = choices) Tj T* ( self.metavar = metavar) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 601.8236 cm
+Q
+q
+1 0 0 1 62.69291 583.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 577.8236 cm
+Q
+q
+1 0 0 1 62.69291 420.6236 cm
+0 0 0 rg
+BT /F5 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 448.6898 156 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 137.71 Tm /F3 10 Tf 12 TL (import clap) Tj T* (from annotations import Positional) Tj T* T* (@clap.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* ( n=Positional\("This is a float", float\),) Tj T* ( rest=Positional\("Other arguments"\)\)) Tj T* (def main\(i, n, *rest\):) Tj T* ( print\(i, n, rest\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 420.6236 cm
+Q
+q
+1 0 0 1 62.69291 402.6236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 273.4236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 120 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 253.4236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can go on and define Option and Flag classes, if you like.) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R77': class PDFPageLabels
+77 0 obj
+% Document Root
+<< /Nums [ 0
+ 78 0 R
+ 1
+ 79 0 R
+ 2
+ 80 0 R
+ 3
+ 81 0 R
+ 4
+ 82 0 R
+ 5
+ 83 0 R
+ 6
+ 84 0 R
+ 7
+ 85 0 R ] >>
+endobj
+% 'R78': class PDFPageLabel
+78 0 obj
+% None
+<< /S /D
+ /St 1 >>
+endobj
+% 'R79': class PDFPageLabel
+79 0 obj
+% None
+<< /S /D
+ /St 2 >>
+endobj
+% 'R80': class PDFPageLabel
+80 0 obj
+% None
+<< /S /D
+ /St 3 >>
+endobj
+% 'R81': class PDFPageLabel
+81 0 obj
+% None
+<< /S /D
+ /St 4 >>
+endobj
+% 'R82': class PDFPageLabel
+82 0 obj
+% None
+<< /S /D
+ /St 5 >>
+endobj
+% 'R83': class PDFPageLabel
+83 0 obj
+% None
+<< /S /D
+ /St 6 >>
+endobj
+% 'R84': class PDFPageLabel
+84 0 obj
+% None
+<< /S /D
+ /St 7 >>
+endobj
+% 'R85': class PDFPageLabel
+85 0 obj
+% None
+<< /S /D
+ /St 8 >>
+endobj
+xref
+0 86
+0000000000 65535 f
+0000000113 00000 n
+0000000260 00000 n
+0000000425 00000 n
+0000000612 00000 n
+0000000872 00000 n
+0000001134 00000 n
+0000001382 00000 n
+0000001630 00000 n
+0000001894 00000 n
+0000002158 00000 n
+0000002423 00000 n
+0000002688 00000 n
+0000002938 00000 n
+0000003191 00000 n
+0000003366 00000 n
+0000003619 00000 n
+0000003801 00000 n
+0000003969 00000 n
+0000004369 00000 n
+0000004631 00000 n
+0000004895 00000 n
+0000005145 00000 n
+0000005411 00000 n
+0000005677 00000 n
+0000005942 00000 n
+0000006192 00000 n
+0000006443 00000 n
+0000006822 00000 n
+0000007088 00000 n
+0000007354 00000 n
+0000007619 00000 n
+0000007870 00000 n
+0000008213 00000 n
+0000008501 00000 n
+0000008785 00000 n
+0000009051 00000 n
+0000009339 00000 n
+0000009590 00000 n
+0000009942 00000 n
+0000010207 00000 n
+0000010473 00000 n
+0000010739 00000 n
+0000010990 00000 n
+0000011333 00000 n
+0000011583 00000 n
+0000011833 00000 n
+0000012083 00000 n
+0000012345 00000 n
+0000012580 00000 n
+0000012932 00000 n
+0000013197 00000 n
+0000013447 00000 n
+0000013734 00000 n
+0000014000 00000 n
+0000014250 00000 n
+0000014537 00000 n
+0000014788 00000 n
+0000015143 00000 n
+0000015424 00000 n
+0000015583 00000 n
+0000015849 00000 n
+0000015974 00000 n
+0000016165 00000 n
+0000016369 00000 n
+0000016562 00000 n
+0000016772 00000 n
+0000016962 00000 n
+0000017151 00000 n
+0000017322 00000 n
+0000023577 00000 n
+0000028311 00000 n
+0000032802 00000 n
+0000038318 00000 n
+0000043850 00000 n
+0000049525 00000 n
+0000054786 00000 n
+0000057947 00000 n
+0000058131 00000 n
+0000058208 00000 n
+0000058285 00000 n
+0000058362 00000 n
+0000058439 00000 n
+0000058516 00000 n
+0000058593 00000 n
+0000058670 00000 n
+trailer
+<< /ID
+ % ReportLab generated PDF document -- digest (http://www.reportlab.com)
+ [(\261\274,t\312\324D[\217\346f\222\0103;\021) (\261\274,t\312\324D[\217\346f\222\0103;\021)]
+
+ /Info 60 0 R
+ /Root 59 0 R
+ /Size 86 >>
+startxref
+58717
+%%EOF
diff --git a/clap/article/article.txt b/clap/article/article.txt
new file mode 100644
index 0000000..79cb834
--- /dev/null
+++ b/clap/article/article.txt
@@ -0,0 +1,442 @@
+The Easiest Command Line Arguments Parser in the World
+================================================================
+
+There is no want of command line arguments parsers in Python
+world. The standard library alone 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). All of
+them are quite powerful and especially argparse_ is an industrial
+strength solution; unfortunately, all of them have a non-zero learning
+curve and a certain verbosity.
+
+Enters clap_. clap_ is designed to be `downwardly scalable`_, i.e. to
+be trivially simple to use for trivial use cases, and to have a
+next-to-zero learning curve. Technically clap_ is just a simple
+wrapper over argparse_, hiding most of the complexity while retaining
+most of the power. clap_ is surprisingly scalable upwards even for
+non-trivial use cases, but it is not intended to be an industrial
+strength command line parsing module. Its capabilities are limited by
+design. 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 the standard library
+modules.
+
+Actually I am pretty much convinced that features provided by ``clap``
+are more than enough for 99.9% of the typical use cases of a scripter
+working in a Unix-like environment. I am targetting here programmers,
+sys-admins, 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. 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. They are designed to implement
+power user tools for programmers or system administrators, and
+they have a non-trivial learning curve.
+
+The importance of scaling down
+------------------------------------
+
+An ex-coworker of mine, David Welton, once wrote a nice article about
+the importance of `scaling down`_: most people are concerned with the
+possibility of scaling up, but we should also be concerned with the
+issue of scaling down: in other worlds, simple things should be kept
+simple. To be concrete, let me start with the simplest possible
+thing: a script that takes a single argument and does something to it.
+It cannot get more trivial than that (discarding the possibility of
+a script without command line arguments, where there is nothing to parse),
+nevertheless it is a use case *extremely common*:
+I need to write scripts like that nearly every day, I wrote hundreds
+of them in the last few years and I have never been happy. Here is
+a typical example of code I have been writing by hand for years:
+
+ .. include:: example1.py
+ :literal:
+
+As you see the whole ``if __name__ == '__main__'`` block (nine lines) is
+essentially boilerplate that should not exists. Actually I think the
+Python language should recognize the main function and perform trivial
+arguments parsing behind the scenes; unfortunaly this is unlikely to
+happen. I have been writing boilerplate like this in hundreds of
+scripts for years, and every time I *hate* it. The purpose of using a
+scripting language is convenience and trivial things should be
+trivial. Unfortunately the standard library modules do not help for
+this use case, which may be trivial, but it is still incredibly
+common. Using getopt_ and optparse_ does not help, since they are
+intended to manage options and not positional arguments; the argparse_
+module helps a bit and it is able to reduce the boilerplate from nine
+lines to six lines:
+
+ .. include:: example2.py
+ :literal:
+
+However saving three lines does not justify introducing the external
+dependency: most people will not switch Python 2.7, which at the time of
+this writing is just about to be released, for many years.
+Moreover, it just feels too complex to instantiate a class and to
+define a parser by hand for such a trivial task.
+
+The clap_ module is designed to manage well such use cases, and it is able
+to reduce the original nine lines of boiler plate to two lines. With the
+clap_ module all you need to write is
+
+ .. include:: example3.py
+ :literal:
+
+The clap_ module provides for free (actually the work is done by the
+underlying argparse_ module) a nice usage message::
+
+ $ python example3.py -h
+ usage: example3.py [-h] dsn
+
+ positional arguments:
+ dsn
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+This is only the tip of the iceberg: clap_ is able to do much more than that.
+
+Positional default arguments
+--------------------------------------------------
+
+I have encountered this use case at work hundreds of times:
+
+ .. include:: example4.py
+ :literal:
+
+With clap_ the entire ``__main__`` block reduces to the usual two lines::
+
+ if __name__ == '__main__':
+ import clap; clap.call(main)
+
+In other words, six lines of boilerplate have been removed, and I have
+the usage message for free::
+
+ usage: example4_.py [-h] dsn [table] [today]
+
+ positional arguments:
+ dsn
+ table
+ today
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+clap_ manages transparently even the case when you want to pass a
+variable number of arguments. Here is an example, a script running
+on a database a series of ``.sql`` scripts:
+
+ .. include:: example6.py
+ :literal:
+
+Using clap_, you can just replace the ``__main__`` block with the usual
+``import clap; clap.call(main)`` and you get the following usage message::
+
+ usage: example7.py [-h] dsn [scripts [scripts ...]]
+
+ positional arguments:
+ dsn
+ scripts
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+The examples here should have made clear that *clap is able to figure out
+the command line arguments parser to use from the signature of the main
+function*. This is the whole idea behind clap_: if my intent is clear,
+let's the machine takes care of the details.
+
+Options and flags
+---------------------------------------
+
+It is surprising how few command line scripts with options
+I have written over the years (probably less than a hundred),
+compared to the number of scripts with positional arguments
+(I certainly have written more than a thousand of them).
+Still, this use case is quite common and cannot be neglected.
+The standard library modules (all of them) are quite verbose
+when it comes to specifying the options and frankly I have
+never used them directly. Instead, I have always relied on
+an old recipe of mine, the optionparse_ recipe, which provides a
+convenient wrapper over optionparse_. Alternatively, in the simplest cases,
+I have just performed the parsing by hand, instead of manually building
+a suitable OptionParser.
+
+clap_ is inspired to the optionparse_ recipe, in the sense that
+it delivers the programmer from the burden of writing the
+parser, but is less of a hack: instead
+of extracting the parser from the docstring of the module, it
+extracts it from the signature of the ``main`` function.
+
+The idea comes from the `function annotations` concept, a new
+feature of Python 3. An example is worth a thousand words, so here
+it is:
+
+ .. include:: example8.py
+ :literal:
+
+As you see, the argument ``command`` has been annotated with the
+tuple ``("SQL query", 'option', 'c')``: the first string is the
+help string which will appear in the usage message, whereas the
+second and third strings tell clap_ that ``command`` is an option and that
+it can be abbreviated with the letter ``c``. Of course, it also
+possible to use the long option format, by prefixing the option
+with ``--command=``. The resulting usage message is the following::
+
+ $ python3 example8.py -h
+ usage: example8.py [-h] [-c COMMAND] dsn
+
+ positional arguments:
+ dsn
+
+ optional arguments:
+ -h, --help show this help message and exit
+ -c COMMAND, --command COMMAND
+ SQL query
+
+Here are two examples of usage::
+
+ $ python3 example8.py -c"select * from table" dsn
+ executing select * from table on dsn
+
+ $ python3 example8.py --command="select * from table" dsn
+ executing select * from table on dsn
+
+Notice that if the option is not passed, the variable ``command``
+will get the value ``None``.
+
+Even positional argument can be annotated::
+
+ def main(command: ("SQL query", 'option', 'c'),
+ dsn: ("Database dsn", 'positional', None)):
+ ...
+
+Of course explicit is better than implicit, an no special cases are
+special enough, but sometimes practicality beats purity, so clap_ is
+smart enough to convert help messages into tuples internally; in other
+words, you can just write "Database dsn" instead of ``("Database dsn",
+'positional', None)``::
+
+
+ def main(command: ("SQL query", 'option', 'c'), dsn: "Database dsn"):
+ ...
+
+In both cases
+the usage message will show a nice help string on the right hand side
+of the ``dsn`` positional argument. varargs (starred-arguments) can also
+be annotated::
+
+ def main(dsn: "Database dsn", *scripts: "SQL scripts"):
+ ...
+
+is a valid signature for clap_, which will recognize the help strings
+for both ``dsn`` and ``scripts``::
+
+ positional arguments:
+ dsn Database dsn
+ scripts SQL scripts
+
+clap_ also recognizes flags, i.e. boolean options which are
+``True`` if they are passed to the command line and ``False``
+if they are absent. Here is an example::
+
+ $ python3 example9.py -v dsn
+ connecting to dsn
+
+::
+
+ $ python3 example9.py -h
+ usage: example9.py [-h] [-v] dsn
+
+ positional arguments:
+ dsn connection string
+
+ optional arguments:
+ -h, --help show this help message and exit
+ -v, --verbose prints more info
+
+For consistency with the way the usage message is printed, I suggest you to
+follow the Flag-Option-Positional (FOP) convention: in the ``main``
+function write first the flag arguments, then the option arguments and
+finally the positional arguments. This is just a convention and you are
+not forced to use it, but
+it makes sense to put the position arguments at the end, since they
+may be default arguments and varargs.
+
+clap for people not using Python 3
+--------------------------------------------------
+
+I do not use Python 3. At work we are just starting to think about
+migrating to Python 2.6. I think it will take years before we even
+think to migrate to Python 3. I am pretty much sure most Pythonistas
+are in the same situation. Therefore clap_ provides a way to work
+with function annotations even in Python 2.X (including Python 2.3).
+There is no magic involved; you just need to add the annotations
+by hand. For instance
+
+::
+
+ def main(dsn: "Database dsn", *scripts: "SQL scripts"):
+
+becomes::
+
+ def main(dsn, *scripts):
+ ...
+ main.__annotations__ = dict(
+ dsn="Database dsn",
+ scripts="SQL scripts")
+
+One should be careful to much the keys of the annotations dictionary
+with the names of the arguments in the annotated function; for lazy
+people with Python 2.4 available the simplest way is to use the
+``clap.annotations`` decorator that performs the check for you.
+
+::
+
+ @annotations(
+ dsn="Database dsn",
+ scripts="SQL scripts")
+ def main(dsn, *scripts):
+ ...
+
+In the rest of this article I will assume that you are using Python 2.X with
+``X >= 4`` and I will use the ``clap.annotations`` decorator.
+
+Advanced usage
+--------------------------------------------------
+
+One of the goals of clap is to have a learning curve of *minutes*, compared
+to the learning curve of *hours* of argparse_. That does not mean
+that I have removed all the advanced features of argparse_. Actually
+a lot of argparse_ power persists in clap_: in particular, the
+``type``, ``choices`` and ``metavar`` concepts are there.
+Until now, I have only showed simple annotations, but in general
+an annotation is a 5-tuple of the form
+
+ ``(help, kind, abbrev, type, choices, metavar)``
+
+where ``help`` is the help message, ``kind`` is one of {"flag",
+"option ", "positional"}, ``abbrev`` is a one-character string,
+``type`` is callable taking a string in input, choices is a sequence
+of values and ``metavar`` is a string.
+
+``type`` is used to automagically convert the arguments from string
+to any Python type; by default there is no convertion i.e. ``type=None``.
+
+``choices`` is used to restrict the number of the valid
+options; by default there is no restriction i.e. ``choices=None``.
+
+``metavar`` is used to change the argument name in the usage
+message (and only there); by default the metavar is equal to the name of the
+argument.
+
+Here is an example showing all of such features (shamelessly stolen
+from the argparse_ documentation):
+
+ .. include:: example10.py
+ :literal:
+
+Here is the usage for the script::
+
+ usage: example10.py [-h] {add,mul} [n [n ...]]
+
+ positional arguments:
+ {add,mul} The name of an operator
+ n A number
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+Here are a couple of examples of use::
+
+ $ python example10.py add 1 2 3 4
+ 10.0
+ $ python example10.py mul 1 2 3 4
+ 24.0
+ $ python example10.py ad 1 2 3 4 # a mispelling error
+ usage: example10.py [-h] {add,mul} [n [n ...]]
+ example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul')
+
+A few notes on the underlying implementation
+----------------------------------------------------
+
+clap_ relies on a argparse_ for all of the heavy lifting work. It is possible
+to pass options to the underlying ArgumentParser_ object (currently it
+accepts the default arguments ``description``, ``epilog``, ``prog``, ``usage``,
+``add_help``, ``argument_default``, ``parents``, ``prefix_chars``,
+``conflict_handler``, ``formatter_class``) simply by setting such attributes
+on the ``main`` function.
+For instance
+
+::
+
+ def main(...):
+ pass
+
+ main.add_help = False
+
+disable the recognition of the help flag ``-h, --help``. This is not
+particularly elegant, but I assume the typical user of clap_ will be
+happy with the default message and would not want to go at this level
+of detail; still it is possible if she wants to. I redirect the interested
+readers to the documentation of argparse_ to understand the meaning of
+the various options.
+
+If you want to access the underlying ArgumentParser_ object, you can
+use the ``clap.parser_from`` utility function:
+
+>>> import clap
+>>> def main(arg):
+... pass
+...
+>>> print clap.parser_from(main)
+ArgumentParser(prog='', usage=None, description=None, version=None,
+formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error',
+add_help=True)
+
+I use ``clap.parser_from`` in the unit tests of the module, but regular
+users should never need to use it.
+
+clap_ uses an ``Annotation`` class to convert the tuples
+in the function signature into annotation objects, i.e. objects with
+six attributes ``help, kind, short, type, choices, metavar``.
+
+Advanced users can implement their own annotation objects.
+For instance, here is an example of how you could implement annotations for
+positional arguments:
+
+ .. include:: annotations.py
+ :literal:
+
+You can use such annotations objects as follows:
+
+ .. include:: example11.py
+ :literal:
+
+Here is the usage message::
+
+ usage: example11.py [-h] i n [rest [rest ...]]
+
+ positional arguments:
+ i This is an int
+ n This is a float
+ rest Other arguments
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+You can go on and define ``Option`` and ``Flag`` classes, if you like.
+Using custom annotation objects you could do advanced things like extracting the
+annotations from a configuration file or from a database, but I expect such
+use cases to be quite rare: the default mechanism should work
+pretty well for most users.
+
+.. _argparse: http://argparse.googlecode.com
+.. _optparse: http://docs.python.org/library/optparse.html
+.. _getopt: http://docs.python.org/library/getopt.html
+.. _optionparse: http://code.activestate.com/recipes/278844-parsing-the-command-line/
+.. _clap:
+.. _scaling down: http://www.welton.it/articles/scalable_systems
+.. _downwardly scalable: http://www.welton.it/articles/scalable_systems
+.. _ArgumentParser: http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html
diff --git a/clap/article/clap.py b/clap/article/clap.py
new file mode 120000
index 0000000..f85cf0d
--- /dev/null
+++ b/clap/article/clap.py
@@ -0,0 +1 @@
+../clap.py \ No newline at end of file
diff --git a/clap/article/example1.py b/clap/article/example1.py
new file mode 100644
index 0000000..59d4ef1
--- /dev/null
+++ b/clap/article/example1.py
@@ -0,0 +1,13 @@
+def main(dsn):
+ "Do something with the database"
+ print(dsn)
+
+if __name__ == '__main__':
+ import sys
+ n = len(sys.argv[1:])
+ if n == 0:
+ sys.exit('usage: python %s dsn' % sys.argv[0])
+ elif n == 1:
+ main(sys.argv[1])
+ else:
+ sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:]))
diff --git a/clap/article/example10.py b/clap/article/example10.py
new file mode 100644
index 0000000..3797c3f
--- /dev/null
+++ b/clap/article/example10.py
@@ -0,0 +1,14 @@
+import clap
+
+@clap.annotations(
+operator=("The name of an operator", 'positional', None, str, ['add', 'mul']),
+numbers=("A number", 'positional', None, float, None, "n"))
+def main(operator, *numbers):
+ op = getattr(float, '__%s__' % operator)
+ result = dict(add=0.0, mul=1.0)[operator]
+ for n in numbers:
+ result = op(result, n)
+ print(result)
+
+if __name__ == '__main__':
+ clap.call(main)
diff --git a/clap/article/example11.py b/clap/article/example11.py
new file mode 100644
index 0000000..9c078b7
--- /dev/null
+++ b/clap/article/example11.py
@@ -0,0 +1,12 @@
+import clap
+from annotations import Positional
+
+@clap.annotations(
+ i=Positional("This is an int", int),
+ n=Positional("This is a float", float),
+ rest=Positional("Other arguments"))
+def main(i, n, *rest):
+ print(i, n, rest)
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/article/example2.py b/clap/article/example2.py
new file mode 100644
index 0000000..69981ee
--- /dev/null
+++ b/clap/article/example2.py
@@ -0,0 +1,10 @@
+def main(dsn):
+ "Do something on the database"
+ print(dsn)
+
+if __name__ == '__main__':
+ import argparse
+ p = argparse.ArgumentParser()
+ p.add_argument('dsn')
+ arg = p.parse_args()
+ main(arg.dsn)
diff --git a/clap/article/example3.py b/clap/article/example3.py
new file mode 100644
index 0000000..94b756f
--- /dev/null
+++ b/clap/article/example3.py
@@ -0,0 +1,6 @@
+def main(dsn):
+ "Do something with the database"
+ print(dsn)
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/article/example4.py b/clap/article/example4.py
new file mode 100644
index 0000000..08a7dd2
--- /dev/null
+++ b/clap/article/example4.py
@@ -0,0 +1,14 @@
+from datetime import datetime
+
+def main(dsn, table='product', today=datetime.today()):
+ "Do something on the database"
+ print(dsn, table, today)
+
+if __name__ == '__main__':
+ import sys
+ args = sys.argv[1:]
+ if not args:
+ sys.exit('usage: python %s dsn' % sys.argv[0])
+ elif len(args) > 2:
+ sys.exit('Unrecognized arguments: %s' % ' '.join(argv[2:]))
+ main(*args)
diff --git a/clap/article/example4_.py b/clap/article/example4_.py
new file mode 100644
index 0000000..9ae2d46
--- /dev/null
+++ b/clap/article/example4_.py
@@ -0,0 +1,8 @@
+from datetime import datetime
+
+def main(dsn, table='product', today=datetime.today()):
+ "Do something on the database"
+ print(dsn, table, today)
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/article/example5.py b/clap/article/example5.py
new file mode 100644
index 0000000..59f88c7
--- /dev/null
+++ b/clap/article/example5.py
@@ -0,0 +1,8 @@
+from datetime import datetime
+
+def main(dsn, today=datetime.today()):
+ "Do something on the database"
+ print(dsn, today)
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/article/example6.py b/clap/article/example6.py
new file mode 100644
index 0000000..9c7b5fd
--- /dev/null
+++ b/clap/article/example6.py
@@ -0,0 +1,12 @@
+from datetime import datetime
+
+def main(dsn, *scripts):
+ "Run the given scripts on the database"
+ for script in scripts:
+ print('executing %s' % script)
+
+if __name__ == '__main__':
+ import sys
+ if len(sys.argv) < 2:
+ sys.exit('usage: python %s dsn script.sql ...' % sys.argv[0])
+ main(sys.argv[1:])
diff --git a/clap/article/example7.py b/clap/article/example7.py
new file mode 100644
index 0000000..ab3f2f7
--- /dev/null
+++ b/clap/article/example7.py
@@ -0,0 +1,9 @@
+from datetime import datetime
+
+def main(dsn, *scripts):
+ "Run the given scripts on the database"
+ for script in scripts:
+ print('executing %s' % script)
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/article/example8.py b/clap/article/example8.py
new file mode 100644
index 0000000..2f675f5
--- /dev/null
+++ b/clap/article/example8.py
@@ -0,0 +1,7 @@
+def main(command: ("SQL query", 'option', 'c'), dsn):
+ if command:
+ print('executing %s on %s' % (command, dsn))
+ # ...
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/article/example9.py b/clap/article/example9.py
new file mode 100644
index 0000000..6613345
--- /dev/null
+++ b/clap/article/example9.py
@@ -0,0 +1,7 @@
+def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'):
+ if verbose:
+ print('connecting to %s' % dsn)
+ # ...
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/clap/clap.py b/clap/clap.py
index 74d1d34..625092b 100644
--- a/clap/clap.py
+++ b/clap/clap.py
@@ -24,174 +24,121 @@
## DAMAGE.
"""
-CLAP, the smart and simple Command Line Arguments Parser.
+clap, the easiest Command Line Arguments Parser in the world.
See clap/doc.html for the documentation.
"""
+__version__ = '0.2.0'
-import optparse, re, sys, string
+import re, sys, inspect, argparse
-class RegexContainer(object):
+if sys.version >= '3':
+ from inspect import getfullargspec
+else:
+ class getfullargspec(object):
+ "A quick and dirty replacement for getfullargspec for Python 2.X"
+ def __init__(self, f):
+ self.args, self.varargs, self.keywords, self.defaults = \
+ inspect.getargspec(f)
+ self.annotations = getattr(f, '__annotations__', {})
+
+def annotations(**ann):
"""
- 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.
+ Returns a decorator annotating a function with the given annotations.
+ This is a trick to support function annotations in Python 2.X.
"""
- 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)
-
-optionstring = None # singleton
-
-class OptionParser(object):
+ def annotate(f):
+ fas = getfullargspec(f)
+ args = fas.args
+ if fas.varargs:
+ args.append(fas.varargs)
+ for argname in ann:
+ if argname not in args:
+ raise NameError(
+ 'Annotating non-existing argument: %s' % argname)
+ f.__annotations__ = ann
+ return f
+ return annotate
+
+def is_annotation(obj):
"""
- There should be only one instance of it.
- Attributes: all_options, expected_args, rest_arg, p
+ An object is an annotation object if it has the attributes
+ help, kind, abbrev, type, choices, metavar.
"""
- def __init__(self, doc):
- "Populate the option parser."
- global optionstring
- assert doc is not None, \
- "Missing usage string (maybe __doc__ is None)"
- optionstring = doc.replace('%prog', sys.argv[0])
-
- # parse the doc
- match = rx.usage.search(doc)
- 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: # cannot parse the definition correctly
- continue
- # 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
+ return (hasattr(obj, 'help') and hasattr(obj, 'kind') and
+ hasattr(obj, 'abbrev') and hasattr(obj, 'type')
+ and hasattr(obj, 'choices') and hasattr(obj, 'metavar'))
+
+class Annotation(object):
+ def __init__(self, help="", kind="positional", abbrev=None, type=str,
+ choices=None, metavar=None):
+ if kind == "positional":
+ assert abbrev is None, abbrev
+ else:
+ assert isinstance(abbrev, str) and len(abbrev) == 1, abbrev
+ self.help = help
+ self.kind = kind
+ self.abbrev = abbrev
+ self.type = type
+ self.choices = choices
+ self.metavar = metavar
@classmethod
- def exit(cls, msg=None):
- exit(msg)
-
-def call(func, args=None, doc=None):
+ def from_(cls, obj):
+ "Helper to convert an object into an annotation, if needed"
+ if is_annotation(obj):
+ return obj # do nothing
+ elif hasattr(obj, '__iter__') and not isinstance(obj, str):
+ return cls(*obj)
+ return cls(obj)
+
+NONE = object() # sentinel use to signal the absence of a default
+
+valid_attrs = getfullargspec(argparse.ArgumentParser.__init__).args[1:]
+
+def parser_from(func):
+ # extract the ArgumentParser arguments from the attributes of func
+ attrs = dict([(n, v) for n, v in vars(func).items() if n in valid_attrs])
+ p = argparse.ArgumentParser(**attrs)
+ f = p.argspec = getfullargspec(func)
+ defaults = f.defaults or ()
+ n_args = len(f.args)
+ n_defaults = len(defaults)
+ alldefaults = (NONE,) * (n_args - n_defaults) + defaults
+ for name, default in zip(f.args, alldefaults):
+ a = Annotation.from_(f.annotations.get(name, ()))
+ if a.kind in ('option', 'flag'):
+ short = '-' + a.abbrev
+ long = '--' + name
+ elif default is NONE: # mandatory positional argument
+ p.add_argument(name, help=a.help, type=a.type, choices=a.choices,
+ metavar=a.metavar)
+ else: # regular default argument
+ p.add_argument(name, nargs='?', help=a.help, default=default,
+ type=a.type, choices=a.choices, metavar=a.metavar)
+ if a.kind == 'option':
+ if default is not NONE:
+ raise TypeError('Option %r does not want a default' % name)
+ p.add_argument(short, long, help=a.help, type=a.type,
+ choices=a.choices, metavar=a.metavar)
+ elif a.kind == 'flag':
+ if default is not NONE:
+ raise TypeError('Flag %r does not want a default' % name)
+ p.add_argument(short, long, action='store_true', help=a.help)
+ if f.varargs:
+ a = Annotation.from_(f.annotations.get(f.varargs, ()))
+ p.add_argument(f.varargs, nargs='*', help=a.help, default=[],
+ type=a.type, metavar=a.metavar)
+ return p
+
+def call(func, arglist=sys.argv[1:]):
"""
- Magically calls func by passing to it the command lines arguments,
- parsed according to the docstring of func.
+ Parse the given arglist by using an argparser inferred from the
+ annotations of the given function (the main function of the script)
+ and call that function with the parsed arguments. The user can
+ provide a custom parse_annotation hook or replace the default one.
"""
- if args is None:
- args = sys.argv[1:]
- if doc is None:
- doc = func.__doc__
- try:
- arg = OptionParser(doc).parse_args(args)
- except ParsingError, e:
- print 'ParsingError:', e
- OptionParser.exit()
- return func(**vars(arg))
-
-def exit(msg=None):
- if msg is None:
- msg = optionstring
- raise SystemExit(msg)
+ p = parser_from(func)
+ argdict = vars(p.parse_args(arglist))
+ args = [argdict[a] for a in p.argspec.args]
+ varargs = argdict.get(p.argspec.varargs, [])
+ func(*(args + varargs))
diff --git a/clap/doc.txt b/clap/doc.txt
index 84d254a..c84d8da 100644
--- a/clap/doc.txt
+++ b/clap/doc.txt
@@ -75,3 +75,8 @@ Limitations
------------------------------------
The default values cannot contain a ':' (or it should be escaped).
+
+
+.. Today I want to announce to the general public the birth of my latest
+.. project, which aims to be the easiest command line arguments
+.. parser in the Python world: clap_.
diff --git a/clap/test_clap.py b/clap/test_clap.py
index 8763ac6..04a313d 100644
--- a/clap/test_clap.py
+++ b/clap/test_clap.py
@@ -3,97 +3,81 @@ The tests are runnable with nose, with py.test, or even as standalone script
"""
import sys
-from clap import OptionParser, ParsingError
+import clap3
-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.
- """
+def expect_exit(func, *args, **kw):
try:
func(*args, **kw)
- except Exception, e:
- if str(e) != err:
- raise e.__class__, e, sys.exc_info()[2]
+ except SystemExit:
+ pass
else:
- raise RuntimeError(
- 'Exception %s expected, got none' % errclass.__name__)
+ raise RuntimeError('SystemExit expected, got none!')
-p1 = OptionParser('''\
-usage: %prog args ... [options]
--d, --delete=: delete a file
-''')
+def f1(delete: "d: delete a file", args=()):
+ pass
def test_p1():
+ p1 = clap3.parser_from(f1)
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
+ assert arg.delete is None, arg.delete
+ assert arg.args is (), arg.args
-p2 = OptionParser('''
- usage: %prog arg1 args ... [options]
--d, --delete=: delete a file
-''')
+def f2(arg1, delete: "d: delete a file", args=()):
+ pass
def test_p2():
+ p2 = clap3.parser_from(f2)
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.delete is None, arg.delete
+ assert arg.args is (), arg.args
assert arg, arg
- expect_error("Received 0 arguments [], expected 1 ['arg1']",
- p2.parse_args, [])
+ expect_exit(p2.parse_args, [])
-p3 = OptionParser('''\
-USAGE: %prog arg1 [options]
--d, --delete=: delete a file
-''')
+def f3(arg1, delete: "d: delete a file"):
+ pass
def test_p3():
+ p3 = clap3.parser_from(f3)
arg = p3.parse_args(['arg1'])
- assert arg.delete == '', arg.delete
+ assert arg.delete is None, 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, [])
+ expect_exit(p3.parse_args, ['arg1', 'arg2'])
+ expect_exit(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 f4(delete: "d: delete a file",
+ delete_all: "a, delete all files",
+ color: "c: set default color"="black"):
+ pass
def test_p4():
+ p4 = clap3.parser_from(f4)
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
+ assert arg.color == 'black'
arg = p4.parse_args(['--color=red'])
- assert arg, arg
+ assert arg.color == 'red'
if __name__ == '__main__':
n = 0
- for name, test in globals().items():
+ for name, test in list(globals().items()):
if name.startswith('test_'):
- print 'Running', name
+ print('Running', name)
test()
n +=1
- print 'Executed %d tests OK' % n
+ print('Executed %d tests OK' % n)