summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-06-01 13:12:27 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-06-01 13:12:27 +0200
commit97dcd8ebb064319da3060d9795d4943b88870cd3 (patch)
treec0e4feb38d35fc8534f25176dd3f46494966338e
parentbaa78b50ebcccd789b89f083b36ab2e9a6a389d5 (diff)
downloadmicheles-97dcd8ebb064319da3060d9795d4943b88870cd3.tar.gz
Renamed clap to plac (version 0.3)
-rw-r--r--clap/calc.py15
-rw-r--r--clap/example2.py20
-rw-r--r--plac/doc/annotations.py8
-rw-r--r--plac/doc/article.html410
-rw-r--r--plac/doc/article.pdf4690
-rw-r--r--plac/doc/article.txt618
-rw-r--r--plac/doc/dbcli.py25
-rw-r--r--plac/doc/example1.py13
-rw-r--r--plac/doc/example10.py15
-rw-r--r--plac/doc/example11.py12
-rw-r--r--plac/doc/example2.py10
-rw-r--r--plac/doc/example3.py6
-rw-r--r--plac/doc/example4.py14
-rw-r--r--plac/doc/example4_.py8
-rw-r--r--plac/doc/example5.py8
-rw-r--r--plac/doc/example6.py12
-rw-r--r--plac/doc/example7.py9
-rw-r--r--plac/doc/example8.py7
-rw-r--r--plac/doc/example8_.py7
-rw-r--r--plac/doc/example9.py7
20 files changed, 5879 insertions, 35 deletions
diff --git a/clap/calc.py b/clap/calc.py
deleted file mode 100644
index 8d761cd..0000000
--- a/clap/calc.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import operator
-
-def calc(op, numbers):
- """
- usage: %prog op numbers ... where op can be sum or mul
- """
- if op == 'sum':
- print sum(map(float, numbers))
- elif op == 'mul':
- print reduce(operator.__mul__, map(float, numbers), 1)
- else:
- raise ValueError('Invalid operator %r' % op)
-
-if __name__ == '__main__':
- import clap; clap.call(calc)
diff --git a/clap/example2.py b/clap/example2.py
deleted file mode 100644
index 842d9a9..0000000
--- a/clap/example2.py
+++ /dev/null
@@ -1,20 +0,0 @@
-def print_(color, txt):
- code = {'black': 30, 'red': 31}[color]
- print '\x1b[%dm%s\x1b[0m' % (code, txt)
-
-def main(delete, delete_all, color):
- """usage: %prog
- -c, --color=black: set default color
- -d, --delete=: delete the given file
- -a, --delete-all: delete all files
- """
- if delete_all:
- print_(option.color, "Delete all files")
- elif delete:
- print_(option.color, "Delete the file %s" % option.delete)
- else:
- clap.exit()
-
-if __name__=='__main__':
- import clap; clap.call(main)
-
diff --git a/plac/doc/annotations.py b/plac/doc/annotations.py
new file mode 100644
index 0000000..c84cc04
--- /dev/null
+++ b/plac/doc/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/plac/doc/article.html b/plac/doc/article.html
new file mode 100644
index 0000000..59feb07
--- /dev/null
+++ b/plac/doc/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">plac</a>.</p>
+<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</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">plac</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">plac</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">plac</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">plac</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">plac</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">plac</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 plac; plac.call(main)
+
+</pre>
+</blockquote>
+<p>As an additional bonus she <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</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">plac</a>, you can just replace the <tt class="docutils literal">__main__</tt> block with the usual
+<tt class="docutils literal">import plac; plac.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">plac</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 plac; plac.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">plac</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">plac</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">plac</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="plac-is-also-for-people-not-using-python-3">
+<h1>plac 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">plac</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">plac.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">plac.annotations</tt> decorator.</p>
+</div>
+<div class="section" id="advanced-usage">
+<h1>Advanced usage</h1>
+<p>One of the goals of plac 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">plac</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 plac
+
+&#64;plac.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__':
+ plac.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/plac/doc/article.pdf b/plac/doc/article.pdf
new file mode 100644
index 0000000..e3cf4d7
--- /dev/null
+++ b/plac/doc/article.pdf
@@ -0,0 +1,4690 @@
+%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 4 0 R
+ /F4 7 0 R
+ /F5 45 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
+% 'F3': class PDFType1Font
+4 0 obj
+% Font Times-Roman
+<< /BaseFont /Times-Roman
+ /Encoding /WinAnsiEncoding
+ /Name /F3
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER1': class PDFDictionary
+5 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (mailto:michele.simionato@gmail.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 153.7323
+ 683.5936
+ 526.5827
+ 695.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER2': class PDFDictionary
+6 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 153.7323
+ 653.5936
+ 526.5827
+ 665.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F4': class PDFType1Font
+7 0 obj
+% Font Courier
+<< /BaseFont /Courier
+ /Encoding /WinAnsiEncoding
+ /Name /F4
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER3': class LinkAnnotation
+8 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 338.0236
+ 0 ]
+ /Rect [ 62.69291
+ 554.5936
+ 121.0229
+ 566.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER4': class LinkAnnotation
+9 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 338.0236
+ 0 ]
+ /Rect [ 527.0227
+ 554.5936
+ 532.5827
+ 566.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER5': class LinkAnnotation
+10 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 49 0 R
+ /XYZ
+ 62.69291
+ 729.0236
+ 0 ]
+ /Rect [ 62.69291
+ 536.5936
+ 215.5029
+ 548.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER6': class LinkAnnotation
+11 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 49 0 R
+ /XYZ
+ 62.69291
+ 729.0236
+ 0 ]
+ /Rect [ 527.0227
+ 536.5936
+ 532.5827
+ 548.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER7': class LinkAnnotation
+12 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 56 0 R
+ /XYZ
+ 62.69291
+ 470.6236
+ 0 ]
+ /Rect [ 62.69291
+ 518.5936
+ 199.9429
+ 530.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER8': class LinkAnnotation
+13 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 56 0 R
+ /XYZ
+ 62.69291
+ 470.6236
+ 0 ]
+ /Rect [ 527.0227
+ 518.5936
+ 532.5827
+ 530.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER9': class LinkAnnotation
+14 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 64 0 R
+ /XYZ
+ 62.69291
+ 305.4236
+ 0 ]
+ /Rect [ 62.69291
+ 500.5936
+ 147.1529
+ 512.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER10': class LinkAnnotation
+15 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 64 0 R
+ /XYZ
+ 62.69291
+ 305.4236
+ 0 ]
+ /Rect [ 527.0227
+ 500.5936
+ 532.5827
+ 512.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER11': class LinkAnnotation
+16 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 71 0 R
+ /XYZ
+ 62.69291
+ 201.0236
+ 0 ]
+ /Rect [ 62.69291
+ 482.5936
+ 182.7329
+ 494.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER12': class LinkAnnotation
+17 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 71 0 R
+ /XYZ
+ 62.69291
+ 201.0236
+ 0 ]
+ /Rect [ 527.0227
+ 482.5936
+ 532.5827
+ 494.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER13': class LinkAnnotation
+18 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 78 0 R
+ /XYZ
+ 62.69291
+ 490.6236
+ 0 ]
+ /Rect [ 62.69291
+ 464.5936
+ 128.2629
+ 476.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER14': class LinkAnnotation
+19 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 78 0 R
+ /XYZ
+ 62.69291
+ 490.6236
+ 0 ]
+ /Rect [ 527.0227
+ 464.5936
+ 532.5827
+ 476.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER15': class LinkAnnotation
+20 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 83 0 R
+ /XYZ
+ 62.69291
+ 407.2849
+ 0 ]
+ /Rect [ 62.69291
+ 446.5936
+ 282.1729
+ 458.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER16': class LinkAnnotation
+21 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 83 0 R
+ /XYZ
+ 62.69291
+ 407.2849
+ 0 ]
+ /Rect [ 527.0227
+ 446.5936
+ 532.5827
+ 458.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER17': class LinkAnnotation
+22 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 90 0 R
+ /XYZ
+ 62.69291
+ 480.6236
+ 0 ]
+ /Rect [ 62.69291
+ 428.5936
+ 191.5929
+ 440.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER18': class LinkAnnotation
+23 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 90 0 R
+ /XYZ
+ 62.69291
+ 480.6236
+ 0 ]
+ /Rect [ 527.0227
+ 428.5936
+ 532.5827
+ 440.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER19': class LinkAnnotation
+24 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 113 0 R
+ /XYZ
+ 62.69291
+ 587.8236
+ 0 ]
+ /Rect [ 62.69291
+ 410.5936
+ 141.6229
+ 422.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER20': class LinkAnnotation
+25 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 113 0 R
+ /XYZ
+ 62.69291
+ 587.8236
+ 0 ]
+ /Rect [ 521.4627
+ 410.5936
+ 532.5827
+ 422.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER21': class LinkAnnotation
+26 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 113 0 R
+ /XYZ
+ 62.69291
+ 188.8236
+ 0 ]
+ /Rect [ 62.69291
+ 392.5936
+ 136.0529
+ 404.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER22': class LinkAnnotation
+27 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 113 0 R
+ /XYZ
+ 62.69291
+ 188.8236
+ 0 ]
+ /Rect [ 521.4627
+ 392.5936
+ 532.5827
+ 404.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER23': class LinkAnnotation
+28 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 118 0 R
+ /XYZ
+ 62.69291
+ 256.6236
+ 0 ]
+ /Rect [ 62.69291
+ 374.5936
+ 111.5829
+ 386.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER24': class LinkAnnotation
+29 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 118 0 R
+ /XYZ
+ 62.69291
+ 256.6236
+ 0 ]
+ /Rect [ 521.4627
+ 374.5936
+ 532.5827
+ 386.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER25': class LinkAnnotation
+30 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 129 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Rect [ 62.69291
+ 356.5936
+ 219.9529
+ 368.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER26': class LinkAnnotation
+31 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 129 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Rect [ 521.4627
+ 356.5936
+ 532.5827
+ 368.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER27': class PDFDictionary
+32 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/getopt.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 361.1228
+ 290.5936
+ 392.9767
+ 302.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER28': class PDFDictionary
+33 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 493.6727
+ 290.5936
+ 531.3087
+ 302.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER29': class PDFDictionary
+34 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 164.0504
+ 278.5936
+ 206.7572
+ 290.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER30': class PDFDictionary
+35 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 493.1227
+ 278.5936
+ 532.1158
+ 290.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER31': class PDFDictionary
+36 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 95.71512
+ 236.5936
+ 114.0551
+ 248.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER32': class PDFDictionary
+37 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 120.9573
+ 236.5936
+ 143.4195
+ 248.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER33': class PDFDictionary
+38 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 337.6822
+ 224.5936
+ 359.0965
+ 236.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER34': class PDFDictionary
+39 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 490.3427
+ 224.5936
+ 529.8027
+ 236.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER35': class PDFDictionary
+40 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 346.3284
+ 212.5936
+ 367.4652
+ 224.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page1': class PDFPage
+41 0 obj
+% Page dictionary
+<< /Annots [ 5 0 R
+ 6 0 R
+ 8 0 R
+ 9 0 R
+ 10 0 R
+ 11 0 R
+ 12 0 R
+ 13 0 R
+ 14 0 R
+ 15 0 R
+ 16 0 R
+ 17 0 R
+ 18 0 R
+ 19 0 R
+ 20 0 R
+ 21 0 R
+ 22 0 R
+ 23 0 R
+ 24 0 R
+ 25 0 R
+ 26 0 R
+ 27 0 R
+ 28 0 R
+ 29 0 R
+ 30 0 R
+ 31 0 R
+ 32 0 R
+ 33 0 R
+ 34 0 R
+ 35 0 R
+ 36 0 R
+ 37 0 R
+ 38 0 R
+ 39 0 R
+ 40 0 R ]
+ /Contents 146 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER36': class PDFDictionary
+42 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.welton.it/articles/scalable_systems) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 471.4139
+ 693.5936
+ 529.8027
+ 705.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER37': class PDFDictionary
+43 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 105.7485
+ 645.5936
+ 127.684
+ 657.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER38': class PDFDictionary
+44 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 466.7629
+ 633.5936
+ 506.2229
+ 645.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F5': class PDFType1Font
+45 0 obj
+% Font Helvetica-Oblique
+<< /BaseFont /Helvetica-Oblique
+ /Encoding /WinAnsiEncoding
+ /Name /F5
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER39': class PDFDictionary
+46 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/getopt.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 504.7827
+ 314.3936
+ 532.1006
+ 326.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER40': class PDFDictionary
+47 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 82.40665
+ 302.3936
+ 124.3504
+ 314.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER41': class PDFDictionary
+48 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 290.3936
+ 104.9329
+ 302.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page2': class PDFPage
+49 0 obj
+% Page dictionary
+<< /Annots [ 42 0 R
+ 43 0 R
+ 44 0 R
+ 46 0 R
+ 47 0 R
+ 48 0 R ]
+ /Contents 147 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER42': class PDFDictionary
+50 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.82606
+ 756.5936
+ 106.0692
+ 768.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER43': class PDFDictionary
+51 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 243.8829
+ 744.5936
+ 265.0029
+ 756.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER44': class PDFDictionary
+52 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.6329
+ 635.3936
+ 105.6829
+ 647.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER45': class PDFDictionary
+53 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 421.9727
+ 635.3936
+ 465.1427
+ 647.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER46': class PDFDictionary
+54 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 211.6529
+ 486.1936
+ 232.7729
+ 498.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER47': class PDFDictionary
+55 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.47291
+ 229.9936
+ 106.5929
+ 241.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page3': class PDFPage
+56 0 obj
+% Page dictionary
+<< /Annots [ 50 0 R
+ 51 0 R
+ 52 0 R
+ 53 0 R
+ 54 0 R
+ 55 0 R ]
+ /Contents 148 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER48': class PDFDictionary
+57 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 687.3936
+ 84.20915
+ 699.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER49': class PDFDictionary
+58 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 91.59679
+ 494.1936
+ 109.9368
+ 506.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER50': class PDFDictionary
+59 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 446.6187
+ 332.9936
+ 464.9587
+ 344.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER51': class PDFDictionary
+60 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
+ 221.9936
+ 502.5727
+ 233.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER52': class PDFDictionary
+61 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
+ 209.9936
+ 312.43
+ 221.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER53': class PDFDictionary
+62 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 179.9936
+ 84.28901
+ 191.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER54': class PDFDictionary
+63 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
+ 179.9936
+ 217.2895
+ 191.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page4': class PDFPage
+64 0 obj
+% Page dictionary
+<< /Annots [ 57 0 R
+ 58 0 R
+ 59 0 R
+ 60 0 R
+ 61 0 R
+ 62 0 R
+ 63 0 R ]
+ /Contents 149 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER55': class PDFDictionary
+65 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 133.1479
+ 629.3936
+ 154.4129
+ 641.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page5': class PDFPage
+66 0 obj
+% Page dictionary
+<< /Annots [ 65 0 R ]
+ /Contents 150 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER56': class PDFDictionary
+67 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 185.8554
+ 744.5936
+ 207.616
+ 756.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER57': class PDFDictionary
+68 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 162.7329
+ 590.1936
+ 181.0729
+ 602.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER58': class PDFDictionary
+69 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 512.9936
+ 84.57878
+ 524.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER59': class PDFDictionary
+70 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 153.3575
+ 141.5936
+ 175.3449
+ 153.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page6': class PDFPage
+71 0 obj
+% Page dictionary
+<< /Annots [ 67 0 R
+ 68 0 R
+ 69 0 R
+ 70 0 R ]
+ /Contents 151 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER60': class PDFDictionary
+72 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 371.2728
+ 518.1936
+ 393.9678
+ 530.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER61': class PDFDictionary
+73 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 443.1936
+ 102.1529
+ 455.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER62': class PDFDictionary
+74 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 375.4489
+ 443.1936
+ 414.9089
+ 455.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER63': class PDFDictionary
+75 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 493.1227
+ 443.1936
+ 532.1699
+ 455.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER64': class PDFDictionary
+76 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 140.8079
+ 431.1936
+ 159.1479
+ 443.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER65': class PDFDictionary
+77 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 415.0529
+ 239.1936
+ 457.2929
+ 251.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page7': class PDFPage
+78 0 obj
+% Page dictionary
+<< /Annots [ 72 0 R
+ 73 0 R
+ 74 0 R
+ 75 0 R
+ 76 0 R
+ 77 0 R ]
+ /Contents 152 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER66': class PDFDictionary
+79 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 371.8549
+ 83.9079
+ 383.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER67': class PDFDictionary
+80 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 133.1029
+ 371.8549
+ 175.4379
+ 383.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER68': class PDFDictionary
+81 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 454.1177
+ 371.8549
+ 496.4527
+ 383.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER69': class PDFDictionary
+82 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 127.9872
+ 162.6549
+ 149.3819
+ 174.6549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page8': class PDFPage
+83 0 obj
+% Page dictionary
+<< /Annots [ 79 0 R
+ 80 0 R
+ 81 0 R
+ 82 0 R ]
+ /Contents 153 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER70': class PDFDictionary
+84 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 326.9971
+ 711.3936
+ 351.8113
+ 723.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER71': class PDFDictionary
+85 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 407.706
+ 699.3936
+ 452.2944
+ 711.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER72': class PDFDictionary
+86 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 259.0928
+ 687.3936
+ 302.7528
+ 699.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER73': class PDFDictionary
+87 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 258.3129
+ 663.3936
+ 279.4329
+ 675.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER74': class PDFDictionary
+88 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 327.2261
+ 645.3936
+ 410.5152
+ 657.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER75': class PDFDictionary
+89 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 106.6216
+ 445.1936
+ 128.3202
+ 457.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page9': class PDFPage
+90 0 obj
+% Page dictionary
+<< /Annots [ 84 0 R
+ 85 0 R
+ 86 0 R
+ 87 0 R
+ 88 0 R
+ 89 0 R ]
+ /Contents 154 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER76': class PDFDictionary
+91 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 552.3936
+ 85.4908
+ 564.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER77': class PDFDictionary
+92 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 477.5448
+ 552.3936
+ 517.0048
+ 564.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER78': class PDFDictionary
+93 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 519.3936
+ 107.7029
+ 531.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER79': class PDFDictionary
+94 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 447.7627
+ 519.3936
+ 486.6727
+ 531.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER80': class PDFDictionary
+95 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 493.1227
+ 519.3936
+ 531.6927
+ 531.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER81': class PDFDictionary
+96 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 232.9652
+ 495.3936
+ 271.8752
+ 507.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER82': class PDFDictionary
+97 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 465.3936
+ 107.9337
+ 477.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER83': class PDFDictionary
+98 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 308.5389
+ 465.3936
+ 351.8997
+ 477.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER84': class PDFDictionary
+99 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 423.3936
+ 108.3529
+ 435.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER85': class PDFDictionary
+100 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 277.2428
+ 423.3936
+ 321.0228
+ 435.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER86': class PDFDictionary
+101 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 404.5839
+ 411.3936
+ 426.0657
+ 423.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER87': class PDFDictionary
+102 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 357.3936
+ 108.61
+ 369.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER88': class PDFDictionary
+103 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 459.2622
+ 345.3936
+ 481.289
+ 357.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER89': class PDFDictionary
+104 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 315.3936
+ 107.0573
+ 327.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER90': class PDFDictionary
+105 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 140.1729
+ 303.3936
+ 158.5129
+ 315.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER91': class PDFDictionary
+106 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 285.3936
+ 107.9247
+ 297.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER92': class PDFDictionary
+107 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 273.3936
+ 104.0329
+ 285.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER93': class PDFDictionary
+108 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 340.7317
+ 240.3936
+ 385.1185
+ 252.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER94': class PDFDictionary
+109 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 451.1022
+ 240.3936
+ 474.369
+ 252.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER95': class PDFDictionary
+110 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 321.0443
+ 228.3936
+ 399.3474
+ 240.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER96': class PDFDictionary
+111 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 216.3936
+ 107.3744
+ 228.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER97': class PDFDictionary
+112 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 314.0328
+ 153.3936
+ 335.3494
+ 165.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page10': class PDFPage
+113 0 obj
+% Page dictionary
+<< /Annots [ 91 0 R
+ 92 0 R
+ 93 0 R
+ 94 0 R
+ 95 0 R
+ 96 0 R
+ 97 0 R
+ 98 0 R
+ 99 0 R
+ 100 0 R
+ 101 0 R
+ 102 0 R
+ 103 0 R
+ 104 0 R
+ 105 0 R
+ 106 0 R
+ 107 0 R
+ 108 0 R
+ 109 0 R
+ 110 0 R
+ 111 0 R
+ 112 0 R ]
+ /Contents 155 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER98': class PDFDictionary
+114 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 86.82623
+ 197.1936
+ 126.2862
+ 209.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER99': class PDFDictionary
+115 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 415.1627
+ 197.1936
+ 459.306
+ 209.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER100': class PDFDictionary
+116 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 468.9894
+ 197.1936
+ 492.0127
+ 209.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER101': class PDFDictionary
+117 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 161.1936
+ 102.1529
+ 173.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page11': class PDFPage
+118 0 obj
+% Page dictionary
+<< /Annots [ 114 0 R
+ 115 0 R
+ 116 0 R
+ 117 0 R ]
+ /Contents 156 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER102': class PDFDictionary
+119 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.64556
+ 729.5936
+ 105.7082
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER103': class PDFDictionary
+120 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 446.6
+ 729.5936
+ 502.5727
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER104': class PDFDictionary
+121 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 275.6828
+ 717.5936
+ 297.3688
+ 729.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER105': class PDFDictionary
+122 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 77.19665
+ 705.5936
+ 139.4904
+ 717.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER106': class PDFDictionary
+123 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 97.06763
+ 693.5936
+ 139.815
+ 705.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER107': class PDFDictionary
+124 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 203.5016
+ 660.5936
+ 245.8453
+ 672.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER108': class PDFDictionary
+125 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
+ 585.5936
+ 138.7898
+ 597.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER109': class PDFDictionary
+126 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 89.49556
+ 573.5936
+ 128.9556
+ 585.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER110': class PDFDictionary
+127 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 141.0629
+ 561.5936
+ 183.3029
+ 573.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER111': class PDFDictionary
+128 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/Clap/0.7) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 263.3429
+ 531.5936
+ 286.6829
+ 543.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page12': class PDFPage
+129 0 obj
+% Page dictionary
+<< /Annots [ 119 0 R
+ 120 0 R
+ 121 0 R
+ 122 0 R
+ 123 0 R
+ 124 0 R
+ 125 0 R
+ 126 0 R
+ 127 0 R
+ 128 0 R ]
+ /Contents 157 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 145 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'R130': class PDFCatalog
+130 0 obj
+% Document Root
+<< /Outlines 132 0 R
+ /PageLabels 158 0 R
+ /PageMode /UseNone
+ /Pages 145 0 R
+ /Type /Catalog >>
+endobj
+% 'R131': class PDFInfo
+131 0 obj
+<< /Author (Michele Simionato)
+ /CreationDate (D:20100601131016-01'00')
+ /Keywords ()
+ /Producer (ReportLab http://www.reportlab.com)
+ /Subject (\(unspecified\))
+ /Title (Plac, the Easiest Command Line Arguments Parser in the World) >>
+endobj
+% 'R132': class PDFOutlines
+132 0 obj
+<< /Count 12
+ /First 133 0 R
+ /Last 144 0 R
+ /Type /Outlines >>
+endobj
+% 'Outline.0': class OutlineEntryObject
+133 0 obj
+<< /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 338.0236
+ 0 ]
+ /Next 134 0 R
+ /Parent 132 0 R
+ /Title (Introduction) >>
+endobj
+% 'Outline.1': class OutlineEntryObject
+134 0 obj
+<< /Dest [ 49 0 R
+ /XYZ
+ 62.69291
+ 729.0236
+ 0 ]
+ /Next 135 0 R
+ /Parent 132 0 R
+ /Prev 133 0 R
+ /Title (The importance of scaling down) >>
+endobj
+% 'Outline.2': class OutlineEntryObject
+135 0 obj
+<< /Dest [ 56 0 R
+ /XYZ
+ 62.69291
+ 470.6236
+ 0 ]
+ /Next 136 0 R
+ /Parent 132 0 R
+ /Prev 134 0 R
+ /Title (Positional default arguments) >>
+endobj
+% 'Outline.3': class OutlineEntryObject
+136 0 obj
+<< /Dest [ 64 0 R
+ /XYZ
+ 62.69291
+ 305.4236
+ 0 ]
+ /Next 137 0 R
+ /Parent 132 0 R
+ /Prev 135 0 R
+ /Title (Options and flags) >>
+endobj
+% 'Outline.4': class OutlineEntryObject
+137 0 obj
+<< /Dest [ 71 0 R
+ /XYZ
+ 62.69291
+ 201.0236
+ 0 ]
+ /Next 138 0 R
+ /Parent 132 0 R
+ /Prev 136 0 R
+ /Title (plac for Python 2.X users) >>
+endobj
+% 'Outline.5': class OutlineEntryObject
+138 0 obj
+<< /Dest [ 78 0 R
+ /XYZ
+ 62.69291
+ 490.6236
+ 0 ]
+ /Next 139 0 R
+ /Parent 132 0 R
+ /Prev 137 0 R
+ /Title (More features) >>
+endobj
+% 'Outline.6': class OutlineEntryObject
+139 0 obj
+<< /Dest [ 83 0 R
+ /XYZ
+ 62.69291
+ 407.2849
+ 0 ]
+ /Next 140 0 R
+ /Parent 132 0 R
+ /Prev 138 0 R
+ /Title (A few notes on the underlying implementation) >>
+endobj
+% 'Outline.7': class OutlineEntryObject
+140 0 obj
+<< /Dest [ 90 0 R
+ /XYZ
+ 62.69291
+ 480.6236
+ 0 ]
+ /Next 141 0 R
+ /Parent 132 0 R
+ /Prev 139 0 R
+ /Title (Custom annotation objects) >>
+endobj
+% 'Outline.8': class OutlineEntryObject
+141 0 obj
+<< /Dest [ 113 0 R
+ /XYZ
+ 62.69291
+ 587.8236
+ 0 ]
+ /Next 142 0 R
+ /Parent 132 0 R
+ /Prev 140 0 R
+ /Title (plac vs argparse) >>
+endobj
+% 'Outline.9': class OutlineEntryObject
+142 0 obj
+<< /Dest [ 113 0 R
+ /XYZ
+ 62.69291
+ 188.8236
+ 0 ]
+ /Next 143 0 R
+ /Parent 132 0 R
+ /Prev 141 0 R
+ /Title (A final example) >>
+endobj
+% 'Outline.10': class OutlineEntryObject
+143 0 obj
+<< /Dest [ 118 0 R
+ /XYZ
+ 62.69291
+ 256.6236
+ 0 ]
+ /Next 144 0 R
+ /Parent 132 0 R
+ /Prev 142 0 R
+ /Title (The future) >>
+endobj
+% 'Outline.11': class OutlineEntryObject
+144 0 obj
+<< /Dest [ 129 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Parent 132 0 R
+ /Prev 143 0 R
+ /Title (Trivia: the story behind the name) >>
+endobj
+% 'R145': class PDFPages
+145 0 obj
+% page tree
+<< /Count 12
+ /Kids [ 41 0 R
+ 49 0 R
+ 56 0 R
+ 64 0 R
+ 66 0 R
+ 71 0 R
+ 78 0 R
+ 83 0 R
+ 90 0 R
+ 113 0 R
+ 118 0 R
+ 129 0 R ]
+ /Type /Pages >>
+endobj
+% 'R146': class PDFStream
+146 0 obj
+% page stream
+<< /Length 8388 >>
+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 25.44488 0 Td 24 TL /F2 20 Tf 0 0 0 rg (Plac, the Easiest Command Line Arguments) Tj T* 117.81 0 Td (Parser in the World) Tj T* -143.2549 0 Td ET
+Q
+Q
+q
+1 0 0 1 62.69291 692.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 36.93937 0 Td (Author:) Tj T* -36.93937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Michele Simionato) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 677.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 39.69937 0 Td (E-mail:) Tj T* -39.69937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 3 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (michele.simionato@gmail.com) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 662.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 26.91937 0 Td (Requires:) Tj T* -26.91937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Python 2.3+) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 635.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F2 10 Tf 12 TL 25.25937 0 Td (Download) Tj T* 21.11 0 Td (page:) Tj T* -46.36937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 15 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (http://pypi.python.org/pypi/plac) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 620.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 16.91937 0 Td (Installation:) Tj T* -16.91937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (easy_install plac) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 605.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 32.46937 0 Td (License:) Tj T* -32.46937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (BSD license) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 572.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Contents) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 350.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 0 201 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Introduction) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 201 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (1) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 183 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (The importance of scaling down) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 183 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (2) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 165 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Positional default arguments) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 165 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (3) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 147 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Options and flags) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 147 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (4) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 129 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (plac for Python 2.X users) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 129 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (6) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 111 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (More features) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 111 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 93 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A few notes on the underlying implementation) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 93 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (8) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 75 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Custom annotation objects) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 75 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 57 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (plac vs argparse) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 57 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (10) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 39 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A final example) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 39 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (10) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 21 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (The future) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 21 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 3 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Trivia: the story behind the name) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 3 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (12) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 317.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Introduction) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 251.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 .25686 Tw 0 0 0 rg (is an industrial strength solution; unfortunately, all of them feature 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 161.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 (plac) Tj 0 0 0 rg (. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is designed to be downwardly scalable, 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 (plac ) 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 (plac ) 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 83.02362 cm
+q
+BT 1 0 0 1 0 64.82 Tm .536457 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually I am pretty much convinced that features provided by ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (are more than enough for 99.9% of ) Tj T* 0 Tw 2.315318 Tw (the typical use cases of a scripter. I am targetting here programmers, sys-admins, scientists and in ) Tj T* 0 Tw 2.200751 Tw (general people writing throw-away scripts for themselves, choosing to use a command line interface ) Tj T* 0 Tw .510488 Tw (because it is the quick and simple. Such users are not interested in features, they just want to be able to ) Tj T* 0 Tw .231098 Tw (write a simple command line tool from a simple specification, not to build a command line parser by hand. ) Tj T* 0 Tw 2.02936 Tw (Unfortunately, the current modules in the standard library forces them to go the hard way. They are) Tj T* 0 Tw ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R147': class PDFStream
+147 0 obj
+% page stream
+<< /Length 5433 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 741.0236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.004269 Tw 12 TL /F1 10 Tf 0 0 0 rg (designed to implement power user tools for programmers or system administrators, and they have a) Tj T* 0 Tw (non-trivial learning curve.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 708.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 630.0236 cm
+q
+BT 1 0 0 1 0 64.82 Tm .588735 Tw 12 TL /F1 10 Tf 0 0 0 rg (An ex-coworker of mine, David Welton, once wrote a nice article about the import) Tj (<) Tj (ance 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 .15436 Tw (issue of scaling down. In other worlds, simple things should be kept simple, and programs should address) Tj T* 0 Tw .50061 Tw (the common cases in an easy way, hopefully without loosing too much power and keeping difficult things) Tj T* 0 Tw .815542 Tw (possible. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (adhere as much as possible to this philosophy and it is designed to handle well the trivial) Tj T* 0 Tw (case, while retaining the ability to handle complex cases relying on the underlying power of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 564.0236 cm
+q
+BT 1 0 0 1 0 52.82 Tm 1.217209 Tw 12 TL /F1 10 Tf 0 0 0 rg (To be concrete, let me start with the simplest possible thing: a script that takes a single argument and) Tj T* 0 Tw 1.844692 Tw (does something to it. It cannot get more trivial than that \(discarding the possibility of a script without) Tj T* 0 Tw 2.277318 Tw (command line arguments, where there is nothing to parse\), nevertheless it is a use case ) Tj /F5 10 Tf (extremely) Tj T* 0 Tw .540488 Tw (common) Tj /F1 10 Tf (: I need to write scripts like that nearly every day, I wrote hundreds of them in the last few years) Tj T* 0 Tw (and I have never been happy. Here is a typical example of code I have been writing by hand for years:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 558.0236 cm
+Q
+q
+1 0 0 1 62.69291 388.8236 cm
+0 0 0 rg
+BT /F3 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 /F4 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 388.8236 cm
+Q
+q
+1 0 0 1 62.69291 286.8236 cm
+q
+BT 1 0 0 1 0 88.82 Tm .880651 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see the whole ) Tj /F4 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 /F5 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 .482093 Tw (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 280.8236 cm
+Q
+q
+1 0 0 1 62.69291 147.6236 cm
+0 0 0 rg
+BT /F3 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 /F4 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 147.6236 cm
+Q
+q
+1 0 0 1 62.69291 105.6236 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
+
+endstream
+
+endobj
+% 'R148': class PDFStream
+148 0 obj
+% page stream
+<< /Length 4329 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 741.0236 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 (plac ) 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 (plac ) Tj 0 0 0 rg (module all you need to write is) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 735.0236 cm
+Q
+q
+1 0 0 1 62.69291 649.8236 cm
+0 0 0 rg
+BT /F3 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 /F4 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 plac; plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 649.8236 cm
+Q
+q
+1 0 0 1 62.69291 619.8236 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 (plac ) 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 502.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 108 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 89.71 Tm /F4 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 482.6236 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 (plac ) 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 449.6236 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 431.6236 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 425.6236 cm
+Q
+q
+1 0 0 1 62.69291 244.4236 cm
+0 0 0 rg
+BT /F3 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 180 re B*
+Q
+q
+BT 1 0 0 1 0 161.71 Tm 12 TL /F4 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* ( 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 244.4236 cm
+Q
+q
+1 0 0 1 62.69291 226.4236 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 (plac ) Tj 0 0 0 rg (the entire ) Tj /F4 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 181.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 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 161.2236 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 80.02362 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 /F4 10 Tf 12 TL (usage: example4_.py [-h] dsn [table] [today]) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( table) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+
+endstream
+
+endobj
+% 'R149': class PDFStream
+149 0 obj
+% page stream
+<< /Length 4867 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 703.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 60 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ( 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 671.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .396235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) 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 /F4 10 Tf (.sql ) Tj /F1 10 Tf (scripts:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 665.8236 cm
+Q
+q
+1 0 0 1 62.69291 508.6236 cm
+0 0 0 rg
+BT /F3 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 156 re B*
+Q
+q
+BT 1 0 0 1 0 137.71 Tm 12 TL /F4 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 508.6236 cm
+Q
+q
+1 0 0 1 62.69291 478.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .563876 Tw 12 TL /F1 10 Tf 0 0 0 rg (Using ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (, you can just replace the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block with the usual two lines \(I have defined an Emacs) Tj T* 0 Tw (keybinding for them\) and you get the following usage message:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 361.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 /F4 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 317.4236 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 /F5 10 Tf (plac 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 (plac) Tj 0 0 0 rg (: if my intent is) Tj T* 0 Tw (clear, let's the machine take care of the details.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 284.4236 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 194.4236 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 152.4236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .476098 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) 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 /F4 10 Tf (main ) Tj /F1 10 Tf (function.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 122.4236 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 /F5 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 116.4236 cm
+Q
+
+endstream
+
+endobj
+% 'R150': class PDFStream
+150 0 obj
+% page stream
+<< /Length 4749 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 667.8236 cm
+0 0 0 rg
+BT /F3 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 96 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F4 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 plac; plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 667.8236 cm
+Q
+q
+1 0 0 1 62.69291 601.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 /F4 10 Tf (command ) Tj /F1 10 Tf (has been annotated with the tuple ) Tj /F4 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 (plac ) Tj 0 0 0 rg (that ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (is an option and that it can be abbreviated with the letter ) Tj /F4 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 /F4 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 460.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 /F4 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 440.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 359.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 /F4 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 327.4236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.34104 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that if the option is not passed, the variable ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (will get the value ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (. It is possible to) Tj T* 0 Tw (specify a non-trivial default for an option. Here is an example:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 321.4236 cm
+Q
+q
+1 0 0 1 62.6378 227.0167 cm
+0 0 0 rg
+BT /F3 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
+.971265 0 0 .971265 0 0 cm
+q
+1 0 0 1 6.6 6.795265 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 462 96 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL (# example8_) Tj T* T* (def main\(dsn, command: \("SQL query", 'option', 'c'\)='select * from table'\):) Tj T* ( print\('executing %r on %s' % \(command, dsn\)\)) 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 227.0167 cm
+Q
+q
+1 0 0 1 62.69291 209.0167 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Now if you do not pass the ) Tj /F4 10 Tf (command option) Tj /F1 10 Tf (, the default query will be executed:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 163.8167 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 /F4 10 Tf 12 TL ($ python article/example8_.py dsn) Tj T* (executing 'select * from table' on dsn) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 143.8167 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Positional argument can be annotated too:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 86.6167 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 /F4 10 Tf 12 TL (def main\(command: \("SQL query", 'option', 'c'\),) Tj T* ( dsn: \("Database dsn", 'positional', None\)\):) Tj T* ( ...) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+
+endstream
+
+endobj
+% 'R151': class PDFStream
+151 0 obj
+% page stream
+<< /Length 5617 >>
+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 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 (plac ) 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 /F4 10 Tf (\("Database dsn", 'positional', None\)) Tj /F1 10 Tf (:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 683.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 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 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 651.8236 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 /F4 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 606.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 /F4 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 586.6236 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 (plac) Tj 0 0 0 rg (, which will recognize the help strings for both ) Tj /F4 10 Tf (dsn ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (scripts) Tj /F1 10 Tf (:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 529.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 48 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 29.71 Tm /F4 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 497.4236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .765868 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (also recognizes flags, i.e. boolean options which are ) Tj /F4 10 Tf (True ) Tj /F1 10 Tf (if they are passed to the command line) Tj T* 0 Tw (and ) Tj /F4 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 452.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 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 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 323.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 120 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 101.71 Tm /F4 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 279.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .31408 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that it is an error trying to specify a default for flags: the default value for a flag is always ) Tj /F4 10 Tf (False) Tj /F1 10 Tf (. If) Tj T* 0 Tw 2.652485 Tw (you feel the need to implement non-boolean flags, you should use an option with two choices, as) Tj T* 0 Tw (explained in the "more features" section.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 213.0236 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.895433 Tw (Flag-Option-Required-Default \(FORD\) convention: in the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function write first the flag arguments,) Tj T* 0 Tw .881235 Tw (then the option arguments, then the required arguments and finally the default arguments. This is just a) Tj T* 0 Tw .110574 Tw (convention and you are not forced to use it, except for the default arguments \(including the varargs\) which) Tj T* 0 Tw (must stay at the end since it is required by the Python syntax.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 180.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac for Python 2.X users) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 126.0236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .211807 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. It will take years) Tj T* 0 Tw 1.269988 Tw (before we even think to migrate to Python 3. I am pretty much sure most Pythonistas are in the same) Tj T* 0 Tw .867318 Tw (situation. Therefore ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides a way to work with function annotations even in Python 2.X \(including) Tj T* 0 Tw (Python 2.3\). There is no magic involved; you just need to add the annotations by hand. For instance) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 92.82362 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 /F4 10 Tf 12 TL (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+
+endstream
+
+endobj
+% 'R152': class PDFStream
+152 0 obj
+% page stream
+<< /Length 5596 >>
+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 /F4 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 /F4 10 Tf (plac.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 /F4 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 502.6236 cm
+q
+BT 1 0 0 1 0 28.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 /F4 10 Tf (X >) Tj (= 4 ) Tj /F1 10 Tf (and I will use the) Tj T* 0 Tw 1.574983 Tw /F4 10 Tf (plac.annotations ) Tj /F1 10 Tf (decorator. Notice however that the tests for ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (are supposed to run even with) Tj T* 0 Tw (Python 2.3.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 469.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (More features) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 415.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 plac is to have a learning curve of ) Tj /F5 10 Tf (minutes) Tj /F1 10 Tf (, compared to the learning curve of ) Tj /F5 10 Tf (hours ) Tj /F1 10 Tf (of) Tj T* 0 Tw .412765 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. That does not mean that I have removed all the features of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually a lot of ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw .104987 Tw 0 0 0 rg (power persists in ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Until now, I have only showed simple annotations, but in general an annotation is a) Tj T* 0 Tw (5-tuple of the form) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 409.6236 cm
+Q
+q
+1 0 0 1 62.69291 397.6236 cm
+0 0 0 rg
+BT /F3 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 /F4 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 397.6236 cm
+Q
+q
+1 0 0 1 62.69291 355.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 /F4 10 Tf (help ) Tj /F1 10 Tf (is the help message, ) Tj /F4 10 Tf (kind ) Tj /F1 10 Tf (is one of {"flag", "option ", "positional"}, ) Tj /F4 10 Tf (abbrev ) Tj /F1 10 Tf (is a) Tj T* 0 Tw 2.203735 Tw (one-character string, ) Tj /F4 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 /F4 10 Tf (metavar ) Tj /F1 10 Tf (is a string.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 325.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .006654 Tw 12 TL /F4 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 /F4 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 295.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.904692 Tw 12 TL /F4 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 /F4 10 Tf (choices=None) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 253.6236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.071751 Tw 12 TL /F4 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 .787988 Tw (metavar is equal to the name of the argument, unless the argument has a default and in such a case is) Tj T* 0 Tw (equal to the stringified form of the default.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 235.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 many of the 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 229.6236 cm
+Q
+q
+1 0 0 1 62.69291 76.86614 cm
+0 0 0 rg
+BT /F3 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
+.922464 0 0 .922464 0 0 cm
+q
+1 0 0 1 6.6 7.154749 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 480 144 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (import plac) Tj T* T* (@plac.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* ( "A script to add and multiply 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* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+
+endstream
+
+endobj
+% 'R153': class PDFStream
+153 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 703.8236 cm
+0 0 0 rg
+BT /F3 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 60 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ( print\(result\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 703.8236 cm
+Q
+q
+1 0 0 1 62.69291 685.8236 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 544.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 /F4 10 Tf 12 TL (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* T* (A script to add and multiply numbers) 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 512.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .15186 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function has been automatically added to the usage message. Here) Tj T* 0 Tw (are a couple of examples of use:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 419.2849 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 /F4 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 386.2849 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 356.2849 cm
+q
+BT 1 0 0 1 0 16.82 Tm .094988 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) 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 and it is possible to leverage on ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features) Tj T* 0 Tw (directly or indirectly.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 314.2849 cm
+q
+BT 1 0 0 1 0 28.82 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 11.11997 Tw ('==SUPPRESS==' as help string \(or ) Tj /F4 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use) Tj T* 0 Tw /F4 10 Tf (argparse.FileType ) Tj /F1 10 Tf (directly.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 260.2849 cm
+q
+BT 1 0 0 1 0 40.82 Tm 1.639213 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is also possible to pass options to the underlying ) Tj /F4 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F4 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F4 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 191.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 60 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F4 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 111.0849 cm
+q
+BT 1 0 0 1 0 64.82 Tm 1.256457 Tw 12 TL /F1 10 Tf 0 0 0 rg (disable the recognition of the help flag ) Tj /F4 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 (plac ) 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 .230514 Tw (still it is possible if she wants to. For instance, by setting the ) Tj /F4 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add) Tj T* 0 Tw .04332 Tw (a comment to the usage message \(by default the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function is used as description\). It) Tj T* 0 Tw .877765 Tw (is also possible to change the option prefix; for instance if your script must run under Windows and you) Tj T* 0 Tw (want to use "/" as option prefix you can add the lines:) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R154': class PDFStream
+154 0 obj
+% page stream
+<< /Length 5176 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 727.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 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (main.prefix_chars='-/') Tj T* (main.short_prefix = '/') Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 659.8236 cm
+q
+BT 1 0 0 1 0 52.82 Tm 3.694269 Tw 12 TL /F1 10 Tf 0 0 0 rg (The recognition of the ) Tj /F4 10 Tf (short_prefix ) Tj /F1 10 Tf (attribute is a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (extension; there is also a companion) Tj T* 0 Tw 2.348314 Tw /F4 10 Tf (long_prefix ) Tj /F1 10 Tf (attribute with default value of ) Tj /F4 10 Tf (--) Tj /F1 10 Tf (. ) Tj /F4 10 Tf (prefix_chars ) Tj /F1 10 Tf (is an ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (feature. Interested) Tj T* 0 Tw 1.419984 Tw (readers should read the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to understand the meaning of the other options. If) Tj T* 0 Tw .098935 Tw (there is a set of options that you use very often, you may consider writing a decorator adding such options) Tj T* 0 Tw (to the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function for you. For simplicity, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not perform any magic of that kind.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 629.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 7.709147 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is possible to access directly the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, by invoking the) Tj T* 0 Tw /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 512.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 108 re B*
+Q
+q
+BT 1 0 0 1 0 89.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print plac.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 492.6236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F4 10 Tf (plac.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 459.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Custom annotation objects) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 429.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .578651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses an ) Tj /F4 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the tuples in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F4 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 399.6236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .083735 Tw (Advanced users can implement their own annotation objects. For instance, here is an example of how you) Tj T* 0 Tw (could implement annotations for positional arguments:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 393.6236 cm
+Q
+q
+1 0 0 1 62.69291 284.4236 cm
+0 0 0 rg
+BT /F3 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 /F4 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 284.4236 cm
+Q
+q
+1 0 0 1 62.69291 266.4236 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 260.4236 cm
+Q
+q
+1 0 0 1 62.69291 103.2236 cm
+0 0 0 rg
+BT /F3 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 /F4 10 Tf 12 TL (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.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 plac; plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 103.2236 cm
+Q
+q
+1 0 0 1 62.69291 85.22362 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 you get:) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R155': class PDFStream
+155 0 obj
+% page stream
+<< /Length 8028 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 643.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 /F4 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 599.8236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .713516 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can go on and define ) Tj /F4 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (Flag ) Tj /F1 10 Tf (classes, if you like. Using custom annotation objects you) Tj T* 0 Tw .17528 Tw (could do advanced things like extracting the annotations from a configuration file or from a database, but I) Tj T* 0 Tw (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 566.8236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac vs argparse) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 536.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.677882 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is opinionated and by design it does not try to make available all of the features of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. In) Tj T* 0 Tw (particular you should be aware of the following limitations/differences.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 530.8236 cm
+Q
+q
+1 0 0 1 62.69291 530.8236 cm
+Q
+q
+1 0 0 1 62.69291 476.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 39 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 40.82 Tm .889984 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (automatically defines both a long and short form for each options, just like ) Tj 0 0 .501961 rg (optparse) Tj 0 0 0 rg (. ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw .446988 Tw 0 0 0 rg (allows you to define only a long form, or only a short form, if you like. However, since I have always) Tj T* 0 Tw .457045 Tw (been happy with the behavior of ) Tj 0 0 .501961 rg (optparse) Tj 0 0 0 rg (, which I feel is pretty much consistent, I have decided not) Tj T* 0 Tw (to support this feature.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 476.8236 cm
+Q
+q
+1 0 0 1 62.69291 476.8236 cm
+Q
+q
+1 0 0 1 62.69291 434.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 27 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.120751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support "required options". As the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation puts it: ) Tj /F5 10 Tf (Required options) Tj T* 0 Tw 1.075318 Tw (are generally considered bad form - normal users expect options to be optional. You should avoid) Tj T* 0 Tw (the use of required options whenever possible.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 434.8236 cm
+Q
+q
+1 0 0 1 62.69291 434.8236 cm
+Q
+q
+1 0 0 1 62.69291 368.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 51 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 52.82 Tm 1.539982 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (supports only regular boolean flags. ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (has the ability to define generalized two-value) Tj T* 0 Tw .361751 Tw (flags with values different from ) Tj /F4 10 Tf (True ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (False) Tj /F1 10 Tf (. An earlier version of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (had this feature too, but) Tj T* 0 Tw .814985 Tw (since you can use options with two choices instead, and in any case the conversion from ) Tj /F4 10 Tf ({True,) Tj T* 0 Tw .901984 Tw (False} ) Tj /F1 10 Tf (to any couple of values can be trivially implemented with a ternary operator \() Tj /F4 10 Tf (value1 if) Tj T* 0 Tw (flag else value2) Tj /F1 10 Tf (\), I have removed it \(KISS rules!\).) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 368.8236 cm
+Q
+q
+1 0 0 1 62.69291 368.8236 cm
+Q
+q
+1 0 0 1 62.69291 326.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 27 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.797126 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support ) Tj /F4 10 Tf (nargs ) Tj /F1 10 Tf (options directly \(it uses them internally, though, to implement flag) Tj T* 0 Tw .90683 Tw (recognition\). The reason it that all the use cases of interest to me are covered by ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and did not) Tj T* 0 Tw (feel the need to increase the learning curve by adding direct support to ) Tj /F4 10 Tf (nargs) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 326.8236 cm
+Q
+q
+1 0 0 1 62.69291 326.8236 cm
+Q
+q
+1 0 0 1 62.69291 296.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 16.82 Tm .24436 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support subparsers directly. For the moment, this looks like a feature too advanced for) Tj T* 0 Tw (the goals of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 296.8236 cm
+Q
+q
+1 0 0 1 62.69291 296.8236 cm
+Q
+q
+1 0 0 1 62.69291 254.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 27 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.111751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support actions directly. This also looks like a feature too advanced for the goals of) Tj T* 0 Tw .347485 Tw 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Notice however that the ability to define your own annotation objects may mitigate the need for) Tj T* 0 Tw (custom actions.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 254.8236 cm
+Q
+q
+1 0 0 1 62.69291 254.8236 cm
+Q
+q
+1 0 0 1 62.69291 200.8236 cm
+q
+BT 1 0 0 1 0 40.82 Tm 2.14683 Tw 12 TL /F1 10 Tf 0 0 0 rg (I should stress again that if you want to access all of the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features from ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (you can use) Tj T* 0 Tw 2.723059 Tw /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and you will get the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object. The the full power of) Tj T* 0 Tw 2.44152 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (is then available to you: you can use ) Tj /F4 10 Tf (add_argument) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (add_subparsers\(\)) Tj /F1 10 Tf (, etc. In other) Tj T* 0 Tw (words, while some features are not supported directly, ) Tj /F5 10 Tf (all ) Tj /F1 10 Tf (features are supported indirectly.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 167.8236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A final example) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 137.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .196655 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a more realistic script using all of the features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to run SQL queries on a database by relying) Tj T* 0 Tw (on SQLAlchemy.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 131.8236 cm
+Q
+q
+1 0 0 1 62.69291 76.86614 cm
+0 0 0 rg
+BT /F3 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 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (import clap) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+
+endstream
+
+endobj
+% 'R156': class PDFStream
+156 0 obj
+% page stream
+<< /Length 3299 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 475.8236 cm
+0 0 0 rg
+BT /F3 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 288 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 269.71 Tm /F4 10 Tf 12 TL T* (@clap.annotations\() Tj T* ( db=\("Connection string", 'positional', None, SqlSoup\),) Tj T* ( header=\("Header", 'flag', 'H'\),) Tj T* ( sqlcmd=\("SQL command", 'option', 'c', str, None, "SQL"\),) Tj T* ( delimiter=\("Column separator", 'option', 'd'\),) Tj T* ( scripts="SQL scripts",) Tj T* ( \)) Tj T* (def main\(db, header, sqlcmd, delimiter="|", *scripts\):) Tj T* ( "A script to run queries and SQL scripts on a database") Tj T* ( print\('Working on %s' % db.bind.url\)) Tj T* ( if sqlcmd:) Tj T* ( result = db.bind.execute\(sqlcmd\)) Tj T* ( if header: # print the header) Tj T* ( print\(delimiter.join\(result.keys\(\)\)\)) Tj T* ( for row in result: # print the rows) Tj T* ( print\(delimiter.join\(map\(str, row\)\)\)) Tj T* T* ( for script in scripts:) Tj T* ( db.execute\(file\(script\).read\(\)\)) 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 475.8236 cm
+Q
+q
+1 0 0 1 62.69291 457.8236 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 268.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 180 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL ($ python article/dbcli.py -h) Tj T* (usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]) Tj T* T* (A script to run queries and SQL scripts on a database) Tj T* T* (positional arguments:) Tj T* ( db Connection string) Tj T* ( scripts SQL scripts) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -H, --header Header) Tj T* ( -c SQL, --sqlcmd SQL SQL command) Tj T* ( -d |, --delimiter | Column separator) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 235.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The future) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 157.6236 cm
+q
+BT 1 0 0 1 0 64.82 Tm .444431 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently plac is below 100 lines of code, not counting blanks, comments and docstrings. I do not plan to) Tj T* 0 Tw .035444 Tw (extend it much in the future. The idea is to keep the module short: it is and it should remain a little wrapper) Tj T* 0 Tw 1.903318 Tw (over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually I have thought about contributing the code back to ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (becomes) Tj T* 0 Tw 4.105697 Tw (successfull and gains a reasonable number of users. For the moment it should be considered) Tj T* 0 Tw .351654 Tw (experimental: after all I wrote it in three days, including the tests, the documentation and the time to learn) Tj T* 0 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (.) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R157': class PDFStream
+157 0 obj
+% page stream
+<< /Length 3429 >>
+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 (Trivia: the story behind the name) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 678.0236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .942651 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (project started very humble: I just wanted to make easy_installable my old ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw .565988 Tw (and to publish it on PyPI. The original name of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was optionparser and the idea behind it was to build) Tj T* 0 Tw .603735 Tw (an ) Tj 0 0 .501961 rg (OptionParser ) Tj 0 0 0 rg (object from the docstring of the module. However, before doing that, I decided to check) Tj T* 0 Tw .507356 Tw (out the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module, since I new it was going into Python 2.7 and Python 2.7 was coming out. Soon) Tj T* 0 Tw (enough I realized two things:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 672.0236 cm
+Q
+q
+1 0 0 1 62.69291 672.0236 cm
+Q
+q
+1 0 0 1 62.69291 642.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (1.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 16.82 Tm .103735 Tw 12 TL /F1 10 Tf 0 0 0 rg (the single greatest idea of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (was unifying the positional arguments and the options in a single) Tj T* 0 Tw (namespace object;) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 642.0236 cm
+Q
+q
+1 0 0 1 62.69291 642.0236 cm
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.66748 Tw (parsing the docstring was so old-fashioned, considering the existence of functions annotations in) Tj T* 0 Tw (Python 3.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+Q
+q
+1 0 0 1 62.69291 558.0236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .600574 Tw 12 TL /F1 10 Tf 0 0 0 rg (Putting together these two observations with the original idea of inferring the parser I decided to build an) Tj T* 0 Tw .516905 Tw 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object from function annotations. The ) Tj /F4 10 Tf (optionparser ) Tj /F1 10 Tf (name was ruled out, since I was) Tj T* 0 Tw .122651 Tw (using ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (; a name like ) Tj /F4 10 Tf (argparse_plus ) Tj /F1 10 Tf (was also ruled out, since the typical usage was completely) Tj T* 0 Tw (different from the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (usage.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 528.0236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.093876 Tw 12 TL /F1 10 Tf 0 0 0 rg (I made a research on PyPI and the name clap \(Command Line Arguments Parser\) was not taken, so I) Tj T* 0 Tw (renamed everything to clap. After two days a ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (module appeared on PyPI! <) Tj (expletive deleted) Tj (>) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 498.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.123145 Tw (Having little fantasy, I decided to rename everything again to plac, as an anagram of clap: since it is a) Tj T* 0 Tw (non-existing English name, I hope nobody will stole it from me!) Tj T* ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R158': class PDFPageLabels
+158 0 obj
+% Document Root
+<< /Nums [ 0
+ 159 0 R
+ 1
+ 160 0 R
+ 2
+ 161 0 R
+ 3
+ 162 0 R
+ 4
+ 163 0 R
+ 5
+ 164 0 R
+ 6
+ 165 0 R
+ 7
+ 166 0 R
+ 8
+ 167 0 R
+ 9
+ 168 0 R
+ 10
+ 169 0 R
+ 11
+ 170 0 R ] >>
+endobj
+% 'R159': class PDFPageLabel
+159 0 obj
+% None
+<< /S /D
+ /St 1 >>
+endobj
+% 'R160': class PDFPageLabel
+160 0 obj
+% None
+<< /S /D
+ /St 2 >>
+endobj
+% 'R161': class PDFPageLabel
+161 0 obj
+% None
+<< /S /D
+ /St 3 >>
+endobj
+% 'R162': class PDFPageLabel
+162 0 obj
+% None
+<< /S /D
+ /St 4 >>
+endobj
+% 'R163': class PDFPageLabel
+163 0 obj
+% None
+<< /S /D
+ /St 5 >>
+endobj
+% 'R164': class PDFPageLabel
+164 0 obj
+% None
+<< /S /D
+ /St 6 >>
+endobj
+% 'R165': class PDFPageLabel
+165 0 obj
+% None
+<< /S /D
+ /St 7 >>
+endobj
+% 'R166': class PDFPageLabel
+166 0 obj
+% None
+<< /S /D
+ /St 8 >>
+endobj
+% 'R167': class PDFPageLabel
+167 0 obj
+% None
+<< /S /D
+ /St 9 >>
+endobj
+% 'R168': class PDFPageLabel
+168 0 obj
+% None
+<< /S /D
+ /St 10 >>
+endobj
+% 'R169': class PDFPageLabel
+169 0 obj
+% None
+<< /S /D
+ /St 11 >>
+endobj
+% 'R170': class PDFPageLabel
+170 0 obj
+% None
+<< /S /D
+ /St 12 >>
+endobj
+xref
+0 171
+0000000000 65535 f
+0000000113 00000 n
+0000000258 00000 n
+0000000423 00000 n
+0000000598 00000 n
+0000000779 00000 n
+0000001031 00000 n
+0000001269 00000 n
+0000001443 00000 n
+0000001684 00000 n
+0000001925 00000 n
+0000002167 00000 n
+0000002409 00000 n
+0000002651 00000 n
+0000002893 00000 n
+0000003136 00000 n
+0000003379 00000 n
+0000003622 00000 n
+0000003865 00000 n
+0000004108 00000 n
+0000004351 00000 n
+0000004594 00000 n
+0000004837 00000 n
+0000005080 00000 n
+0000005323 00000 n
+0000005567 00000 n
+0000005811 00000 n
+0000006055 00000 n
+0000006299 00000 n
+0000006543 00000 n
+0000006787 00000 n
+0000007031 00000 n
+0000007274 00000 n
+0000007536 00000 n
+0000007800 00000 n
+0000008050 00000 n
+0000008300 00000 n
+0000008552 00000 n
+0000008804 00000 n
+0000009056 00000 n
+0000009306 00000 n
+0000009543 00000 n
+0000010163 00000 n
+0000010429 00000 n
+0000010680 00000 n
+0000010917 00000 n
+0000011112 00000 n
+0000011374 00000 n
+0000011638 00000 n
+0000011873 00000 n
+0000012236 00000 n
+0000012488 00000 n
+0000012740 00000 n
+0000012991 00000 n
+0000013241 00000 n
+0000013493 00000 n
+0000013730 00000 n
+0000014093 00000 n
+0000014345 00000 n
+0000014597 00000 n
+0000014849 00000 n
+0000015137 00000 n
+0000015421 00000 n
+0000015673 00000 n
+0000015946 00000 n
+0000016318 00000 n
+0000016555 00000 n
+0000016873 00000 n
+0000017124 00000 n
+0000017376 00000 n
+0000017628 00000 n
+0000017865 00000 n
+0000018210 00000 n
+0000018462 00000 n
+0000018712 00000 n
+0000018962 00000 n
+0000019212 00000 n
+0000019464 00000 n
+0000019699 00000 n
+0000020062 00000 n
+0000020313 00000 n
+0000020563 00000 n
+0000020813 00000 n
+0000021050 00000 n
+0000021395 00000 n
+0000021647 00000 n
+0000021896 00000 n
+0000022146 00000 n
+0000022398 00000 n
+0000022685 00000 n
+0000022922 00000 n
+0000023285 00000 n
+0000023536 00000 n
+0000023786 00000 n
+0000024038 00000 n
+0000024302 00000 n
+0000024552 00000 n
+0000024816 00000 n
+0000025068 00000 n
+0000025318 00000 n
+0000025570 00000 n
+0000025821 00000 n
+0000026074 00000 n
+0000026325 00000 n
+0000026577 00000 n
+0000026830 00000 n
+0000027083 00000 n
+0000027336 00000 n
+0000027589 00000 n
+0000027840 00000 n
+0000028092 00000 n
+0000028380 00000 n
+0000028631 00000 n
+0000028870 00000 n
+0000029391 00000 n
+0000029642 00000 n
+0000029893 00000 n
+0000030147 00000 n
+0000030384 00000 n
+0000030735 00000 n
+0000030989 00000 n
+0000031276 00000 n
+0000031530 00000 n
+0000031841 00000 n
+0000032092 00000 n
+0000032344 00000 n
+0000032633 00000 n
+0000032885 00000 n
+0000033137 00000 n
+0000033380 00000 n
+0000033777 00000 n
+0000033941 00000 n
+0000034232 00000 n
+0000034361 00000 n
+0000034537 00000 n
+0000034747 00000 n
+0000034955 00000 n
+0000035152 00000 n
+0000035357 00000 n
+0000035550 00000 n
+0000035774 00000 n
+0000035979 00000 n
+0000036176 00000 n
+0000036373 00000 n
+0000036565 00000 n
+0000036748 00000 n
+0000036961 00000 n
+0000045452 00000 n
+0000050988 00000 n
+0000055420 00000 n
+0000060390 00000 n
+0000065242 00000 n
+0000070962 00000 n
+0000076661 00000 n
+0000081924 00000 n
+0000087203 00000 n
+0000095334 00000 n
+0000098736 00000 n
+0000102272 00000 n
+0000102524 00000 n
+0000102603 00000 n
+0000102682 00000 n
+0000102761 00000 n
+0000102840 00000 n
+0000102919 00000 n
+0000102998 00000 n
+0000103077 00000 n
+0000103156 00000 n
+0000103235 00000 n
+0000103315 00000 n
+0000103395 00000 n
+trailer
+<< /ID
+ % ReportLab generated PDF document -- digest (http://www.reportlab.com)
+ [(\277\245\225\024\025\215\375JE\232\372/B\334iX) (\277\245\225\024\025\215\375JE\232\372/B\334iX)]
+
+ /Info 131 0 R
+ /Root 130 0 R
+ /Size 171 >>
+startxref
+103444
+%%EOF
diff --git a/plac/doc/article.txt b/plac/doc/article.txt
new file mode 100644
index 0000000..7acd5c8
--- /dev/null
+++ b/plac/doc/article.txt
@@ -0,0 +1,618 @@
+Plac, 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/plac
+:Installation: ``easy_install plac``
+:License: BSD license
+
+.. contents::
+
+Introduction
+------------------------------------------------
+
+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 feature a non-zero learning
+curve and a certain verbosity.
+
+Enters plac_. plac_ 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 plac_ is just a simple
+wrapper over argparse_, hiding most of the complexity while retaining
+most of the power. plac_ 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 ``plac``
+are more than enough for 99.9% of the typical use cases of a scripter.
+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 import<ance 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, and programs should address the common cases in an easy way,
+hopefully without loosing too much power and keeping difficult things
+possible. plac_ adhere as much as possible to this philosophy and it
+is designed to handle well the trivial case, while retaining the
+ability to handle complex cases relying on the underlying power of
+argparse_.
+
+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 plac_ 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
+plac_ module all you need to write is
+
+ .. include:: example3.py
+ :literal:
+
+The plac_ 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: plac_ 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 plac_ the entire ``__main__`` block reduces to the usual two lines::
+
+ if __name__ == '__main__':
+ import plac; plac.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
+
+plac_ 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 plac_, you can just replace the ``__main__`` block with the
+usual two lines (I have defined an Emacs keybinding for them)
+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 *plac 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 plac_: if my intent is clear,
+let's the machine take 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.
+
+plac_ 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 plac_ 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``. It is possible to specify a non-trivial
+default for an option. Here is an example:
+
+ .. include:: example8_.py
+ :literal:
+
+Now if you do not pass the ``command option``, the
+default query will be executed::
+
+ $ python article/example8_.py dsn
+ executing 'select * from table' on dsn
+
+Positional argument can be annotated too::
+
+ 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 plac_ 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 plac_, which will recognize the help strings
+for both ``dsn`` and ``scripts``::
+
+ positional arguments:
+ dsn Database dsn
+ scripts SQL scripts
+
+plac_ 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
+
+Notice that it is an error trying to specify a default for flags: the
+default value for a flag is always ``False``. If you feel the need to
+implement non-boolean flags, you should use an option with two
+choices, as explained in the "more features" section.
+
+For consistency with the way the usage message is printed, I suggest
+you to follow the Flag-Option-Required-Default (FORD) convention: in
+the ``main`` function write first the flag arguments, then the option
+arguments, then the required arguments and finally the default
+arguments. This is just a convention and you are not forced to use it,
+except for the default arguments (including the varargs) which must
+stay at the end since it is required by the Python syntax.
+
+plac for Python 2.X users
+--------------------------------------------------
+
+I do not use Python 3. At work we are just starting to think about
+migrating to Python 2.6. 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 plac_ 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
+``plac.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 ``plac.annotations`` decorator. Notice however
+that the tests for plac_ are supposed to run even with Python 2.3.
+
+More features
+--------------------------------------------------
+
+One of the goals of plac 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 features of argparse_. Actually
+a lot of argparse_ power persists in plac_.
+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, unless the argument has a default and in such a case is
+equal to the stringified form of the default.
+
+Here is an example showing many of the 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 ...]]
+
+ A script to add and multiply numbers
+
+ positional arguments:
+ {add,mul} The name of an operator
+ n A number
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+Notice that the docstring of the ``main`` function has been automatically added
+to the usage message. 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
+----------------------------------------------------
+
+plac_ relies on a argparse_ for all of the heavy lifting work and it is
+possible to leverage on argparse_ features directly or indirectly.
+
+For instance, you can make invisible an argument in the usage message simply by
+using '==SUPPRESS==' as help string (or ``argparse.SUPPRESS``). Similarly, you can
+use ``argparse.FileType`` directly.
+
+It is also possible to pass options to the underlying
+``argparse.ArgumentParser`` object (currently it accepts the default
+arguments ``description``, ``epilog``, ``prog``, ``usage``,
+``add_help``, ``argument_default``, ``parents``, ``prefix_chars``,
+``fromfile_prefix_chars``, ``conflict_handler``, ``formatter_class``).
+It is enough to set 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 plac_ 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. For instance, by
+setting the ``description`` attribute, it is possible to add a comment to the
+usage message (by default the docstring of the ``main`` function is
+used as description). It is also possible to change the option prefix;
+for instance if your script must run under Windows and you want to use
+"/" as option prefix you can add the lines::
+
+ main.prefix_chars='-/'
+ main.short_prefix = '/'
+
+The recognition of the ``short_prefix`` attribute is a plac_
+extension; there is also a companion ``long_prefix``
+attribute with default value of ``--``. ``prefix_chars`` is an argparse_ feature.
+Interested readers should read the documentation of argparse_ to understand the
+meaning of the other options. If there is a set of options that you
+use very often, you may consider writing a decorator adding such
+options to the ``main`` function for you. For simplicity, plac_ does
+not perform any magic of that kind.
+
+It is possible to access directly the underlying ArgumentParser_ object, by
+invoking the ``plac.parser_from`` utility function:
+
+>>> import plac
+>>> def main(arg):
+... pass
+...
+>>> print plac.parser_from(main)
+ArgumentParser(prog='', usage=None, description=None, version=None,
+formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error',
+add_help=True)
+
+I use ``plac.parser_from`` in the unit tests of the module, but regular
+users should never need to use it.
+
+Custom annotation objects
+------------------------------------------------------
+
+Internally plac_ 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 you get::
+
+ 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.
+
+plac vs argparse
+---------------------------------------------
+
+plac_ is opinionated and by design it does not try to make available
+all of the features of argparse_. In particular you should be aware
+of the following limitations/differences.
+
+- plac_ automatically defines both a long and short form for each options,
+ just like optparse_. argparse_ allows you to define only a long form,
+ or only a short form, if you like. However, since I have always been
+ happy with the behavior of optparse_, which I feel is pretty much
+ consistent, I have decided not to support this feature.
+
+- plac_ does not support "required options". As the argparse_
+ documentation puts it: *Required options are generally considered bad
+ form - normal users expect options to be optional. You should avoid
+ the use of required options whenever possible.*
+
+- plac_ supports only regular boolean flags. argparse_ has the ability to
+ define generalized two-value flags with values different from ``True``
+ and ``False``. An earlier version of plac_ had this feature too, but
+ since you can use options with two choices instead, and in any case
+ the conversion from ``{True, False}`` to any couple of values
+ can be trivially implemented with a ternary operator
+ (``value1 if flag else value2``), I have removed it (KISS rules!).
+
+- plac_ does not support ``nargs`` options directly (it uses them internally,
+ though, to implement flag recognition). The reason it that all the use
+ cases of interest to me are covered by plac_ and did not feel the need
+ to increase the learning curve by adding direct support to ``nargs``.
+
+- plac_ does not support subparsers directly. For the moment, this
+ looks like a feature too advanced for the goals of plac_.
+
+- plac_ does not support actions directly. This also
+ looks like a feature too advanced for the goals of plac_. Notice however
+ that the ability to define your own annotation objects may mitigate the
+ need for custom actions.
+
+I should stress again that if you want to access all of the argparse_ features
+from plac_ you can use ``plac.parser_from`` and you will get
+the underlying ArgumentParser_ object. The the full power of argparse_
+is then available to you: you can use ``add_argument``, ``add_subparsers()``,
+etc. In other words, while some features are not supported directly,
+*all* features are supported indirectly.
+
+A final example
+---------------------------------------
+
+Here is a more realistic script using all of the features of plac_ to run SQL queries
+on a database by relying on SQLAlchemy.
+
+ .. include:: dbcli.py
+ :literal:
+
+Here is the usage message::
+
+ $ python article/dbcli.py -h
+ usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
+
+ A script to run queries and SQL scripts on a database
+
+ positional arguments:
+ db Connection string
+ scripts SQL scripts
+
+ optional arguments:
+ -h, --help show this help message and exit
+ -H, --header Header
+ -c SQL, --sqlcmd SQL SQL command
+ -d |, --delimiter | Column separator
+
+The future
+-------------------------------
+
+Currently plac is below 100 lines of code, not counting blanks, comments
+and docstrings. I do not plan to extend it much in the future. The idea is
+to keep the module short: it is and it should remain a little wrapper over
+argparse_. Actually I have thought about contributing the code back to
+argparse_ if plac_ becomes successfull and gains a reasonable number of
+users. For the moment it should be considered experimental: after all
+I wrote it in three days, including the tests, the documentation and the
+time to learn argparse_.
+
+Trivia: the story behind the name
+-----------------------------------------
+
+The plac_ project started very humble: I just wanted to make
+easy_installable my old optionparse_ recipe, and to publish it on PyPI.
+The original name of plac_ was optionparser and the idea behind it was
+to build an OptionParser_ object from the docstring of the module.
+However, before doing that, I decided to check out the argparse_ module,
+since I new it was going into Python 2.7 and Python 2.7 was coming out.
+Soon enough I realized two things:
+
+1. the single greatest idea of argparse_ was unifying the positional arguments
+ and the options in a single namespace object;
+2. parsing the docstring was so old-fashioned, considering the existence
+ of functions annotations in Python 3.
+
+Putting together these two observations with the original idea of inferring the
+parser I decided to build an ArgumentParser_ object from function
+annotations. The ``optionparser`` name was ruled out, since I was
+using argparse_; a name like ``argparse_plus`` was also ruled out,
+since the typical usage was completely different from the argparse_ usage.
+
+I made a research on PyPI and the name clap (Command Line Arguments Parser)
+was not taken, so I renamed everything to clap. After two days
+a Clap_ module appeared on PyPI! <expletive deleted>
+
+Having little fantasy, I decided to rename everything again to plac, as
+an anagram of clap: since it is a non-existing English name, I hope nobody
+will stole it from me!
+
+.. _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/
+.. _plac: http://pypi.python.org/pypi/plac
+.. _scaling down: http://www.welton.it/articles/scalable_systems
+.. _ArgumentParser: http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html
+.. _Clap: http://pypi.python.org/pypi/Clap/0.7
+.. _OptionParser: http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser
diff --git a/plac/doc/dbcli.py b/plac/doc/dbcli.py
new file mode 100644
index 0000000..ba6d586
--- /dev/null
+++ b/plac/doc/dbcli.py
@@ -0,0 +1,25 @@
+import clap
+from sqlalchemy.ext.sqlsoup import SqlSoup
+
+@clap.annotations(
+ db=("Connection string", 'positional', None, SqlSoup),
+ header=("Header", 'flag', 'H'),
+ sqlcmd=("SQL command", 'option', 'c', str, None, "SQL"),
+ delimiter=("Column separator", 'option', 'd'),
+ scripts="SQL scripts",
+ )
+def main(db, header, sqlcmd, delimiter="|", *scripts):
+ "A script to run queries and SQL scripts on a database"
+ print('Working on %s' % db.bind.url)
+ if sqlcmd:
+ result = db.bind.execute(sqlcmd)
+ if header: # print the header
+ print(delimiter.join(result.keys()))
+ for row in result: # print the rows
+ print(delimiter.join(map(str, row)))
+
+ for script in scripts:
+ db.execute(file(script).read())
+
+if __name__ == '__main__':
+ clap.call(main)
diff --git a/plac/doc/example1.py b/plac/doc/example1.py
new file mode 100644
index 0000000..59d4ef1
--- /dev/null
+++ b/plac/doc/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/plac/doc/example10.py b/plac/doc/example10.py
new file mode 100644
index 0000000..eb45343
--- /dev/null
+++ b/plac/doc/example10.py
@@ -0,0 +1,15 @@
+import plac
+
+@plac.annotations(
+operator=("The name of an operator", 'positional', None, str, ['add', 'mul']),
+numbers=("A number", 'positional', None, float, None, "n"))
+def main(operator, *numbers):
+ "A script to add and multiply 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__':
+ plac.call(main)
diff --git a/plac/doc/example11.py b/plac/doc/example11.py
new file mode 100644
index 0000000..0de8395
--- /dev/null
+++ b/plac/doc/example11.py
@@ -0,0 +1,12 @@
+import plac
+from annotations import Positional
+
+@plac.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 plac; plac.call(main)
diff --git a/plac/doc/example2.py b/plac/doc/example2.py
new file mode 100644
index 0000000..69981ee
--- /dev/null
+++ b/plac/doc/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/plac/doc/example3.py b/plac/doc/example3.py
new file mode 100644
index 0000000..1c1a0bd
--- /dev/null
+++ b/plac/doc/example3.py
@@ -0,0 +1,6 @@
+def main(dsn):
+ "Do something with the database"
+ print(dsn)
+
+if __name__ == '__main__':
+ import plac; plac.call(main)
diff --git a/plac/doc/example4.py b/plac/doc/example4.py
new file mode 100644
index 0000000..08a7dd2
--- /dev/null
+++ b/plac/doc/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/plac/doc/example4_.py b/plac/doc/example4_.py
new file mode 100644
index 0000000..2a61d47
--- /dev/null
+++ b/plac/doc/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 plac; plac.call(main)
diff --git a/plac/doc/example5.py b/plac/doc/example5.py
new file mode 100644
index 0000000..d6c8c8a
--- /dev/null
+++ b/plac/doc/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 plac; plac.call(main)
diff --git a/plac/doc/example6.py b/plac/doc/example6.py
new file mode 100644
index 0000000..9c7b5fd
--- /dev/null
+++ b/plac/doc/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/plac/doc/example7.py b/plac/doc/example7.py
new file mode 100644
index 0000000..c1e6b2a
--- /dev/null
+++ b/plac/doc/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 plac; plac.call(main)
diff --git a/plac/doc/example8.py b/plac/doc/example8.py
new file mode 100644
index 0000000..ef5a4e7
--- /dev/null
+++ b/plac/doc/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 plac; plac.call(main)
diff --git a/plac/doc/example8_.py b/plac/doc/example8_.py
new file mode 100644
index 0000000..1c14748
--- /dev/null
+++ b/plac/doc/example8_.py
@@ -0,0 +1,7 @@
+# example8_
+
+def main(dsn, command: ("SQL query", 'option', 'c')='select * from table'):
+ print('executing %r on %s' % (command, dsn))
+
+if __name__ == '__main__':
+ import clap; clap.call(main)
diff --git a/plac/doc/example9.py b/plac/doc/example9.py
new file mode 100644
index 0000000..8ab3932
--- /dev/null
+++ b/plac/doc/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 plac; plac.call(main)