summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-06-11 14:18:43 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-06-11 14:18:43 +0200
commit19c260e63ffe20b5196c8401ed1cb2d7ccf71f7f (patch)
tree59373c2fa05a1a5b2aa303169cb5b5cdbe3ca940
parentfa41140285817bd1792e46277da7de0d2ee4cf7a (diff)
downloadmicheles-19c260e63ffe20b5196c8401ed1cb2d7ccf71f7f.tar.gz
Initial commit for release 0.5.0
-rw-r--r--plac/CHANGES.txt9
-rw-r--r--plac/Makefile4
-rw-r--r--plac/README.txt9
-rw-r--r--plac/doc/dbcli.help12
-rw-r--r--plac/doc/dbcli.py11
-rw-r--r--plac/doc/example10.py4
-rw-r--r--plac/doc/example12.help10
-rw-r--r--plac/doc/example12.py10
-rw-r--r--plac/doc/example13.help10
-rw-r--r--plac/doc/example13.py27
-rw-r--r--plac/doc/example14.help8
-rw-r--r--plac/doc/example14.py15
-rw-r--r--plac/doc/example3.help9
-rw-r--r--plac/doc/example6.help8
-rw-r--r--plac/doc/example6.py14
-rw-r--r--plac/doc/example7_.help10
-rw-r--r--plac/doc/example7_.py11
-rw-r--r--plac/doc/example8.help2
-rw-r--r--plac/doc/example8_.help9
-rw-r--r--plac/doc/example8_.py2
-rw-r--r--plac/doc/plac.html573
-rw-r--r--plac/doc/plac.pdf3410
-rw-r--r--plac/doc/plac.txt369
-rw-r--r--plac/doc/plac_adv.html904
-rw-r--r--plac/doc/plac_adv.pdf1543
-rw-r--r--plac/doc/plac_adv.txt279
-rw-r--r--plac/doc/vcs.help10
-rw-r--r--plac/doc/vcs.py23
-rw-r--r--plac/plac.py224
-rw-r--r--plac/plac_core.py218
-rw-r--r--plac/plac_ext.py156
-rw-r--r--plac/setup.py13
-rw-r--r--plac/test_plac.py29
33 files changed, 5345 insertions, 2600 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt
index 2fb6f07..f511500 100644
--- a/plac/CHANGES.txt
+++ b/plac/CHANGES.txt
@@ -1,7 +1,14 @@
HISTORY
----------
-0.4.1. Changed the default formatting class and fixed a bug in the
+0.5.0 Introduced smart options and removed the default formatter class.
+ Made the split plac_code and plac_ext and added an Interpreter class
+ (2010-06-XX).
+0.4.3 Fixed the installation procedure to automatically download argparse
+ if needed (2010-06-11)
+0.4.2 Added missing .help files, made the tests generative and added a
+ note about Clap in the documentation (2010-06-04)
+0.4.1 Changed the default formatter class and fixed a bug in the
display of the default arguments. Added more stringent tests. (2010-06-03)
0.4.0 abbrev is now optional. Added a note about CLIArgs and opterate.
Added keyword arguments recognition. ``plac.call`` now returns the
diff --git a/plac/Makefile b/plac/Makefile
index 22302fc..f83245c 100644
--- a/plac/Makefile
+++ b/plac/Makefile
@@ -1,4 +1,8 @@
+default:
+ make doc/plac.pdf; make doc/plac_adv.pdf
doc/plac.pdf: doc/plac.txt
cd doc; rst2pdf --footer=###Page### plac.txt; rst2html --stylesheet=$(HOME)/gcode/df.css plac.txt plac.html
+doc/plac_adv.pdf: doc/plac_adv.txt
+ cd doc; rst2pdf --footer=###Page### plac_adv.txt; rst2html --stylesheet=$(HOME)/gcode/df.css plac_adv.txt plac_adv.html
upload:
python setup.py register sdist upload
diff --git a/plac/README.txt b/plac/README.txt
index 6c5d887..4cbcab6 100644
--- a/plac/README.txt
+++ b/plac/README.txt
@@ -5,7 +5,7 @@ plac, the easiest command line arguments parser in the world
:E-mail: michele.simionato@gmail.com
:Requires: Python 2.3+
:Download page: http://pypi.python.org/pypi/plac
-:Installation: ``easy_install plac``
+:Installation: ``easy_install -U plac``
:License: BSD license
Installation
@@ -15,10 +15,11 @@ If you are lazy, just perform
::
- $ easy_install plac
+ $ easy_install -U plac
-which will install just the module on your system. Notice that
-Python 3 requires the easy_install version of the distribute_ project.
+which will install the module on your system (and possibly argparse
+too, if it is not already installed). Notice that Python 3 requires
+the easy_install version of the distribute_ project.
If you prefer to install the full distribution from source, including
the documentation, download the tarball_, unpack it and run
diff --git a/plac/doc/dbcli.help b/plac/doc/dbcli.help
index c606181..8c0c18a 100644
--- a/plac/doc/dbcli.help
+++ b/plac/doc/dbcli.help
@@ -3,11 +3,11 @@ 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
+ db Connection string
+ scripts SQL scripts
optional arguments:
- -h, --help show this help message and exit
- -H, --header Header
- -c, --sqlcmd SQL SQL command
- -d, --delimiter | Column separator
+ -h, --help show this help message and exit
+ -H, --header Header
+ -c SQL, --sqlcmd SQL SQL command
+ -d |, --delimiter | Column separator
diff --git a/plac/doc/dbcli.py b/plac/doc/dbcli.py
index 7f0cec6..d5db392 100644
--- a/plac/doc/dbcli.py
+++ b/plac/doc/dbcli.py
@@ -11,16 +11,19 @@ from sqlalchemy.ext.sqlsoup import SqlSoup
)
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)
+ yield 'Working on %s' % db.bind.url
+
if sqlcmd:
result = db.bind.execute(sqlcmd)
if header: # print the header
- print(delimiter.join(result.keys()))
+ yield delimiter.join(result.keys())
for row in result: # print the rows
- print(delimiter.join(map(str, row)))
+ yield delimiter.join(map(str, row))
for script in scripts:
db.bind.execute(file(script).read())
+ yield 'executed %s' % script
if __name__ == '__main__':
- plac.call(main)
+ for output in plac.call(main):
+ print(output)
diff --git a/plac/doc/example10.py b/plac/doc/example10.py
index 5da3a22..ac64db9 100644
--- a/plac/doc/example10.py
+++ b/plac/doc/example10.py
@@ -10,7 +10,7 @@ def main(operator, *numbers):
result = dict(add=0.0, mul=1.0)[operator]
for n in numbers:
result = op(result, n)
- print(result)
+ return result
if __name__ == '__main__':
- plac.call(main)
+ print(plac.call(main)[0])
diff --git a/plac/doc/example12.help b/plac/doc/example12.help
index cceb8be..fec1715 100644
--- a/plac/doc/example12.help
+++ b/plac/doc/example12.help
@@ -1,9 +1,9 @@
-usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
+usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]
positional arguments:
- args default arguments
- kw keyword arguments
+ args default arguments
+ kw keyword arguments
optional arguments:
- -h, --help show this help message and exit
- -o, --opt OPT some option
+ -h, --help show this help message and exit
+ -opt OPT some option
diff --git a/plac/doc/example12.py b/plac/doc/example12.py
index 81a6c8d..56c65a9 100644
--- a/plac/doc/example12.py
+++ b/plac/doc/example12.py
@@ -6,7 +6,13 @@ import plac
args='default arguments',
kw='keyword arguments')
def main(opt, *args, **kw):
- print(opt, args, kw)
+ if opt:
+ yield 'opt=%s' % opt
+ if args:
+ yield 'args=%s' % str(args)
+ if kw:
+ yield 'kw=%s' % kw
if __name__ == '__main__':
- plac.call(main)
+ for output in plac.call(main):
+ print(output)
diff --git a/plac/doc/example13.help b/plac/doc/example13.help
new file mode 100644
index 0000000..91216a3
--- /dev/null
+++ b/plac/doc/example13.help
@@ -0,0 +1,10 @@
+usage: example13.py [-h] {status,commit,checkout,help} ...
+
+A Fake Version Control System
+
+optional arguments:
+ -h, --help show this help message and exit
+
+subcommands:
+ {status,commit,checkout,help}
+ -h to get additional help
diff --git a/plac/doc/example13.py b/plac/doc/example13.py
new file mode 100644
index 0000000..3c91f07
--- /dev/null
+++ b/plac/doc/example13.py
@@ -0,0 +1,27 @@
+import plac
+
+class FVCS(object):
+ "A Fake Version Control System"
+ commands = 'checkout', 'commit', 'status', 'help'
+
+ @plac.annotations(
+ name=('a recognized command', 'positional', None, str, commands))
+ def help(self, name):
+ self.p.subp[name].print_help()
+
+ @plac.annotations(
+ url=('url of the source code', 'positional'))
+ def checkout(self, url):
+ print('checkout', url)
+
+ def commit(self):
+ print('commit')
+
+ @plac.annotations(quiet=('summary information', 'flag'))
+ def status(self, quiet):
+ print('status', quiet)
+
+main = FVCS()
+
+if __name__ == '__main__':
+ plac.call(main)
diff --git a/plac/doc/example14.help b/plac/doc/example14.help
new file mode 100644
index 0000000..50c600b
--- /dev/null
+++ b/plac/doc/example14.help
@@ -0,0 +1,8 @@
+usage: example14.py [-h] {status,commit,checkout,help} ...
+
+optional arguments:
+ -h, --help show this help message and exit
+
+subcommands:
+ {status,commit,checkout,help}
+ -h to get additional help
diff --git a/plac/doc/example14.py b/plac/doc/example14.py
new file mode 100644
index 0000000..18a2931
--- /dev/null
+++ b/plac/doc/example14.py
@@ -0,0 +1,15 @@
+import plac
+from example13 import FVCS
+
+class VCS_with_help(FVCS):
+ commands = FVCS.commands + ('help',)
+
+ @plac.annotations(
+ name=('a recognized command', 'positional', None, str, commands))
+ def help(self, name):
+ self.p.subp[name].print_help()
+
+main = VCS_with_help()
+
+if __name__ == '__main__':
+ plac.call(main)
diff --git a/plac/doc/example3.help b/plac/doc/example3.help
new file mode 100644
index 0000000..dd4d96a
--- /dev/null
+++ b/plac/doc/example3.help
@@ -0,0 +1,9 @@
+usage: example3.py [-h] dsn
+
+Do something with the database
+
+positional arguments:
+ dsn
+
+optional arguments:
+ -h, --help show this help message and exit
diff --git a/plac/doc/example6.help b/plac/doc/example6.help
new file mode 100644
index 0000000..c950e78
--- /dev/null
+++ b/plac/doc/example6.help
@@ -0,0 +1,8 @@
+usage: example6.py [-h] [-command COMMAND] dsn
+
+positional arguments:
+ dsn
+
+optional arguments:
+ -h, --help show this help message and exit
+ -command COMMAND SQL query
diff --git a/plac/doc/example6.py b/plac/doc/example6.py
index 5586c03..045839b 100644
--- a/plac/doc/example6.py
+++ b/plac/doc/example6.py
@@ -1,14 +1,6 @@
# example6.py
-from datetime import datetime
-
-def main(dsn, *scripts):
- "Run the given scripts on the database"
- for script in scripts:
- print('executing %s' % script)
- # ...
+def main(dsn, command: ("SQL query", 'option')):
+ print('executing %r on %s' % (command, dsn))
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:])
+ import plac; plac.call(main)
diff --git a/plac/doc/example7_.help b/plac/doc/example7_.help
new file mode 100644
index 0000000..6f0f4b8
--- /dev/null
+++ b/plac/doc/example7_.help
@@ -0,0 +1,10 @@
+usage: example7_.py [-h] dsn [scripts [scripts ...]]
+
+Run the given scripts on the database
+
+positional arguments:
+ dsn Database dsn
+ scripts SQL scripts
+
+optional arguments:
+ -h, --help show this help message and exit
diff --git a/plac/doc/example7_.py b/plac/doc/example7_.py
new file mode 100644
index 0000000..550a524
--- /dev/null
+++ b/plac/doc/example7_.py
@@ -0,0 +1,11 @@
+# example7_.py
+from datetime import datetime
+
+def main(dsn: "Database dsn", *scripts: "SQL 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.help b/plac/doc/example8.help
index c7fa3a5..a566258 100644
--- a/plac/doc/example8.help
+++ b/plac/doc/example8.help
@@ -5,5 +5,5 @@ positional arguments:
optional arguments:
-h, --help show this help message and exit
- -c, --command COMMAND
+ -c COMMAND, --command COMMAND
SQL query
diff --git a/plac/doc/example8_.help b/plac/doc/example8_.help
new file mode 100644
index 0000000..872b05a
--- /dev/null
+++ b/plac/doc/example8_.help
@@ -0,0 +1,9 @@
+usage: example8_.py [-h] [-command select * from table] dsn
+
+positional arguments:
+ dsn
+
+optional arguments:
+ -h, --help show this help message and exit
+ -command select * from table
+ SQL query
diff --git a/plac/doc/example8_.py b/plac/doc/example8_.py
index 20f6df0..cdea364 100644
--- a/plac/doc/example8_.py
+++ b/plac/doc/example8_.py
@@ -1,5 +1,5 @@
# example8_.py
-def main(dsn, command: ("SQL query", 'option', 'c')='select * from table'):
+def main(dsn, command: ("SQL query", 'option')='select * from table'):
print('executing %r on %s' % (command, dsn))
if __name__ == '__main__':
diff --git a/plac/doc/plac.html b/plac/doc/plac.html
index a973666..6339cc3 100644
--- a/plac/doc/plac.html
+++ b/plac/doc/plac.html
@@ -3,7 +3,7 @@
<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/" />
+<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" />
<title>Plac: Parsing the Command Line the Easy Way</title>
<meta name="author" content="Michele Simionato" />
<style type="text/css">
@@ -428,7 +428,7 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
</tr>
<tr class="field"><th class="docinfo-name">Project page:</th><td class="field-body"><a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">http://micheles.googlecode.com/hg/plac/doc/plac.html</a></td>
</tr>
-<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install plac</tt></td>
+<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal"><span class="pre">easy_install</span> <span class="pre">-U</span> <span class="pre">plac</span></tt></td>
</tr>
<tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td>
</tr>
@@ -440,18 +440,16 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<li><a class="reference internal" href="#the-importance-of-scaling-down" id="id1">The importance of scaling down</a></li>
<li><a class="reference internal" href="#scripts-with-required-arguments" id="id2">Scripts with required arguments</a></li>
<li><a class="reference internal" href="#scripts-with-default-arguments" id="id3">Scripts with default arguments</a></li>
-<li><a class="reference internal" href="#scripts-with-options" id="id4">Scripts with options</a></li>
+<li><a class="reference internal" href="#scripts-with-options-and-smart-options" id="id4">Scripts with options (and smart options)</a></li>
<li><a class="reference internal" href="#scripts-with-flags" id="id5">Scripts with flags</a></li>
<li><a class="reference internal" href="#plac-for-python-2-x-users" id="id6">plac for Python 2.X users</a></li>
<li><a class="reference internal" href="#more-features" id="id7">More features</a></li>
-<li><a class="reference internal" href="#keyword-arguments" id="id8">Keyword arguments</a></li>
-<li><a class="reference internal" href="#a-realistic-example" id="id9">A realistic example</a></li>
-<li><a class="reference internal" href="#advanced-usage" id="id10">Advanced usage</a></li>
-<li><a class="reference internal" href="#custom-annotation-objects" id="id11">Custom annotation objects</a></li>
-<li><a class="reference internal" href="#plac-vs-argparse" id="id12">plac vs argparse</a></li>
-<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id13">plac vs the rest of the world</a></li>
-<li><a class="reference internal" href="#the-future" id="id14">The future</a></li>
-<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id15">Trivia: the story behind the name</a></li>
+<li><a class="reference internal" href="#a-realistic-example" id="id8">A realistic example</a></li>
+<li><a class="reference internal" href="#keyword-arguments" id="id9">Keyword arguments</a></li>
+<li><a class="reference internal" href="#plac-vs-argparse" id="id10">plac vs argparse</a></li>
+<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id11">plac vs the rest of the world</a></li>
+<li><a class="reference internal" href="#the-future" id="id12">The future</a></li>
+<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id13">Trivia: the story behind the name</a></li>
</ul>
</div>
<div class="section" id="the-importance-of-scaling-down">
@@ -523,7 +521,7 @@ if __name__ == '__main__':
sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:]))
</pre>
-<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (nine lines)
+<p>As you see the whole <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__'</span></tt> block (nine lines)
is essentially boilerplate that should not exists. Actually I think
the language should recognize the main function and pass to it the
command line arguments automatically; unfortunaly this is unlikely to
@@ -573,13 +571,18 @@ if __name__ == '__main__':
underlying <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module) a nice usage message:</p>
<pre class="literal-block">
$ python example3.py -h
+</pre>
+<pre class="literal-block">
usage: example3.py [-h] dsn
+Do something with the database
+
positional arguments:
dsn
optional arguments:
-h, --help show this help message and exit
+
</pre>
<p>This is only the tip of the iceberg: <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to do much more than that.</p>
</div>
@@ -607,12 +610,12 @@ if __name__ == '__main__':
</pre>
<p>Here I want to perform a query on a database table, by extracting the
-today's data: it makes sense for <tt class="docutils literal">today</tt> to be a default argument.
-If there is a most used table (in this example a table called <tt class="docutils literal">'product'</tt>)
+today's data: it makes sense for <tt class="docutils literal"><span class="pre">today</span></tt> to be a default argument.
+If there is a most used table (in this example a table called <tt class="docutils literal"><span class="pre">'product'</span></tt>)
it also makes sense to make it a default argument. Performing the parsing
of the command lines arguments by hand takes 8 ugly lines of boilerplate
(using <a class="reference external" href="http://argparse.googlecode.com">argparse</a> would require about the same number of lines).
-With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal">__main__</tt> block reduces to the usual two lines:</p>
+With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal"><span class="pre">__main__</span></tt> block reduces to the usual two lines:</p>
<pre class="literal-block">
if __name__ == '__main__':
import plac; plac.call(main)
@@ -637,7 +640,7 @@ optional arguments:
variable number of arguments. Here is an example, a script running
on a database a series of SQL scripts:</p>
<pre class="literal-block">
-# example6.py
+# example7.py
from datetime import datetime
def main(dsn, *scripts):
@@ -647,15 +650,10 @@ def main(dsn, *scripts):
# ...
if __name__ == '__main__':
- import sys
- if len(sys.argv) &lt; 2:
- sys.exit('usage: python %s dsn script.sql ...' % sys.argv[0])
- main(sys.argv[1:])
+ import plac; plac.call(main)
</pre>
-<p>Using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, you can just replace the <tt class="docutils literal">__main__</tt> block with the
-usual two lines (I have defined an Emacs keybinding for them)
-and then you get the following nice usage message:</p>
+<p>Here is the usage message:</p>
<pre class="literal-block">
usage: example7.py [-h] dsn [scripts [scripts ...]]
@@ -673,9 +671,48 @@ optional arguments:
the command line arguments parser to use from the signature of the main
function</em>. This is the whole idea behind <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: if the intent is clear,
let's the machine take care of the details.</p>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">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, 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"><span class="pre">main</span></tt>
+function.</p>
+<p>The idea comes from the <cite>function annotations</cite> concept, a new
+feature of Python 3. An example is worth a thousand words, so here
+it is:</p>
+<pre class="literal-block">
+# example7_.py
+from datetime import datetime
+
+def main(dsn: &quot;Database dsn&quot;, *scripts: &quot;SQL scripts&quot;):
+ &quot;Run the given scripts on the database&quot;
+ for script in scripts:
+ print('executing %s' % script)
+ # ...
+
+if __name__ == '__main__':
+ import plac; plac.call(main)
+
+</pre>
+<p>Here the arguments of the <tt class="docutils literal"><span class="pre">main</span></tt> function have been annotated with
+strings which are intented to be used in the help message:</p>
+<pre class="literal-block">
+usage: example7_.py [-h] dsn [scripts [scripts ...]]
+
+Run the given scripts on the database
+
+positional arguments:
+ dsn Database dsn
+ scripts SQL scripts
+
+optional arguments:
+ -h, --help show this help message and exit
+
+</pre>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize much more complex annotations, as
+I will show in the next paragraphs.</p>
</div>
-<div class="section" id="scripts-with-options">
-<h1><a class="toc-backref" href="#id4">Scripts with options</a></h1>
+<div class="section" id="scripts-with-options-and-smart-options">
+<h1><a class="toc-backref" href="#id4">Scripts with options (and smart options)</a></h1>
<p>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 wrote (certainly more
@@ -685,15 +722,8 @@ 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>. Alternatively, in the simplest cases, I have just
-performed the parsing by hand.</p>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">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, 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 comes from the <cite>function annotations</cite> concept, a new
-feature of Python 3. An example is worth a thousand words, so here
-it is:</p>
+performed the parsing by hand. In <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the parser is inferred by the
+function annotations. Here is an example:</p>
<pre class="literal-block">
# example8.py
def main(command: (&quot;SQL query&quot;, 'option', 'c'), dsn):
@@ -705,13 +735,12 @@ if __name__ == '__main__':
import plac; plac.call(main)
</pre>
-<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;, 'option', 'c')</tt>: the first string is the help string
-which will appear in the usage message, the second string tell <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-that <tt class="docutils literal">command</tt> is an option and the third string that it can be
-abbreviated with the letter <tt class="docutils literal">c</tt>. Of course, the long option format
-(<tt class="docutils literal"><span class="pre">--command=</span></tt>) comes from the argument name. The resulting usage
-message is the following:</p>
+<p>Here the argument <tt class="docutils literal"><span class="pre">command</span></tt> has been annotated with the tuple
+<tt class="docutils literal"><span class="pre">(&quot;SQL</span> <span class="pre">query&quot;,</span> <span class="pre">'option',</span> <span class="pre">'c')</span></tt>: the first string is the help string
+which will appear in the usage message, the second string tells <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
+that <tt class="docutils literal"><span class="pre">command</span></tt> is an option and the third string that there is also
+a short form of the option <tt class="docutils literal"><span class="pre">-c</span></tt>, the long form being <tt class="docutils literal"><span class="pre">--command=</span></tt>.
+The usage message is the following:</p>
<pre class="literal-block">
usage: example8.py [-h] [-c COMMAND] dsn
@@ -720,7 +749,7 @@ positional arguments:
optional arguments:
-h, --help show this help message and exit
- -c, --command COMMAND
+ -c COMMAND, --command COMMAND
SQL query
</pre>
@@ -732,62 +761,84 @@ 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>. It is possible to specify a non-trivial
-default for an option. Here is an example:</p>
+<p>The third argument in the function annotation can be omitted: in such
+case it will be assumed to be <tt class="docutils literal"><span class="pre">None</span></tt>. The consequence is that
+the usual dichotomy between long and short options (GNU-style options)
+disappears: we get <em>smart options</em>, which have the single character prefix
+of short options and behave like both long and short options, since
+they can be abbreviated. Here is an example featuring smart options:</p>
<pre class="literal-block">
-# example8_.py
-def main(dsn, command: (&quot;SQL query&quot;, 'option', 'c')='select * from table'):
+# example6.py
+def main(dsn, command: (&quot;SQL query&quot;, 'option')):
print('executing %r on %s' % (command, dsn))
if __name__ == '__main__':
import plac; plac.call(main)
</pre>
-<p>Now if you do not pass the <tt class="docutils literal">command option</tt>, the
-default query will be executed:</p>
<pre class="literal-block">
-$ python3 example8_.py dsn
-executing 'select * from table' on dsn
+usage: example6.py [-h] [-command COMMAND] dsn
+
+positional arguments:
+ dsn
+
+optional arguments:
+ -h, --help show this help message and exit
+ -command COMMAND SQL query
+
</pre>
-<p>Positional argument can be annotated too:</p>
+<p>The following are all valid invocations ot the script:</p>
<pre class="literal-block">
-def main(command: (&quot;SQL query&quot;, 'option', 'c'),
- dsn: (&quot;Database dsn&quot;, 'positional', None)):
- ...
+$ python3 example6.py -c &quot;select&quot; dsn
+executing 'select' on dsn
+$ python3 example6.py -com &quot;select&quot; dsn
+executing 'select' on dsn
+$ python3 example6.py -command=&quot;select&quot; dsn
+executing 'select' on dsn
</pre>
-<p>Of course explicit is better than implicit, an no special cases are
-special enough, but sometimes practicality beats purity, so <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is
-able to use smart defaults; in particular you can omit the third argument
-and write:</p>
+<p>Notice that the form <tt class="docutils literal"><span class="pre">-command=SQL</span></tt> is recognized only for the full
+option, not for its abbreviations:</p>
<pre class="literal-block">
-def main(command: (&quot;SQL query&quot;, 'option'),
- dsn: (&quot;Database dsn&quot;, 'positional')):
- ...
+$ python3 example6.py -com=&quot;select&quot; dsn
+usage: example6.py [-h] [-command COMMAND] dsn
+example6.py: error: unrecognized arguments: -com=select
</pre>
-<p>When omitted, the third argument is assumed to be the first letter of
-the variable name for options and flags, and <tt class="docutils literal">None</tt> for positional
-arguments. Moreover, smart enough to convert help messages into tuples;
-in other words, you can just write <tt class="docutils literal">&quot;Database dsn&quot;</tt> instead of
-<tt class="docutils literal">(&quot;Database dsn&quot;, 'positional')</tt>.</p>
-<p>I should notice that varargs (starred-arguments) can be annotated too;
-here is an example:</p>
+<p>If the option is not passed, the variable <tt class="docutils literal"><span class="pre">command</span></tt>
+will get the value <tt class="docutils literal"><span class="pre">None</span></tt>. However, it is possible to specify a non-trivial
+default. Here is an example:</p>
<pre class="literal-block">
-def main(dsn: &quot;Database dsn&quot;, *scripts: &quot;SQL scripts&quot;):
- ...
+# example8_.py
+def main(dsn, command: (&quot;SQL query&quot;, 'option')='select * from table'):
+ print('executing %r on %s' % (command, dsn))
+
+if __name__ == '__main__':
+ import plac; plac.call(main)
+
</pre>
-<p>This is a valid signature for <a class="reference external" href="http://pypi.python.org/pypi/plac">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>Notice that the default value appears in the help message:</p>
<pre class="literal-block">
+usage: example8_.py [-h] [-command select * from table] dsn
+
positional arguments:
- dsn Database dsn
- scripts SQL scripts
+ dsn
+
+optional arguments:
+ -h, --help show this help message and exit
+ -command select * from table
+ SQL query
+
+</pre>
+<p>When you run the script and you do not pass the <tt class="docutils literal"><span class="pre">-command</span></tt> option, the
+default query will be executed:</p>
+<pre class="literal-block">
+$ python3 example8_.py dsn
+executing 'select * from table' on dsn
</pre>
</div>
<div class="section" id="scripts-with-flags">
<h1><a class="toc-backref" href="#id5">Scripts with flags</a></h1>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> also recognizes flags, i.e. boolean options which are
-<tt class="docutils literal">True</tt> if they are passed to the command line and <tt class="docutils literal">False</tt>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize flags, i.e. boolean options which are
+<tt class="docutils literal"><span class="pre">True</span></tt> if they are passed to the command line and <tt class="docutils literal"><span class="pre">False</span></tt>
if they are absent. Here is an example:</p>
<pre class="literal-block">
# example9.py
@@ -817,12 +868,12 @@ $ python3 example9.py -v dsn
connecting to dsn
</pre>
<p>Notice that it is an error trying to specify a default for flags: the
-default value for a flag is always <tt class="docutils literal">False</tt>. If you feel the need to
+default value for a flag is always <tt class="docutils literal"><span class="pre">False</span></tt>. If you feel the need to
implement non-boolean flags, you should use an option with two
choices, as explained in the &quot;more features&quot; section.</p>
<p>For consistency with the way the usage message is printed, I suggest
you to follow the Flag-Option-Required-Default (FORD) convention: in
-the <tt class="docutils literal">main</tt> function write first the flag arguments, then the option
+the <tt class="docutils literal"><span class="pre">main</span></tt> 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
@@ -852,7 +903,7 @@ main.__annotations__ = dict(
<p>One should be careful to match the keys of the annotation 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>
+<tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator that performs the check for you:</p>
<pre class="literal-block">
&#64;plac.annotations(
dsn=&quot;Database dsn&quot;,
@@ -861,30 +912,30 @@ def main(dsn, *scripts):
...
</pre>
<p>In the rest of this article I will assume that you are using Python 2.X with
-<tt class="docutils literal">X &gt;= 4</tt> and I will use the <tt class="docutils literal">plac.annotations</tt> decorator. Notice however
-that the tests for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> runs even on Python 2.3.</p>
+X &gt;= 4 and I will use the <tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator. Notice however
+that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> runs even on Python 2.3.</p>
</div>
<div class="section" id="more-features">
<h1><a class="toc-backref" href="#id7">More features</a></h1>
-<p>Even if 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>, it does not mean that I have removed all the 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://pypi.python.org/pypi/plac">plac</a>. Until
-now, I have only showed simple annotations, but in general an
-annotation is a 5-tuple of the form</p>
+<p>One of the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is to have a learning curve of <em>minutes</em> for
+its core features, compared to the learning curve of <em>hours</em> of
+<a class="reference external" href="http://argparse.googlecode.com">argparse</a>. In order to reach this goal, I have <em>not</em> sacrificed all
+the 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://pypi.python.org/pypi/plac">plac</a>. Until now, I have only showed simple annotations, but in
+general an annotation is a 6-tuple of the form</p>
<blockquote>
-<tt class="docutils literal">(help, kind, abbrev, type, choices, metavar)</tt></blockquote>
-<p>where <tt class="docutils literal">help</tt> is the help message, <tt class="docutils literal">kind</tt> is a string in the set {
-<tt class="docutils literal">&quot;flag&quot;</tt>, <tt class="docutils literal">&quot;option&quot;</tt>, <tt class="docutils literal">&quot;positional&quot;</tt>}, <tt class="docutils literal">abbrev</tt> is a
-one-character string, <tt class="docutils literal">type</tt> is a callable taking a string in input,
-<tt class="docutils literal">choices</tt> is a discrete sequence of values and <tt class="docutils literal">metavar</tt> is a string.</p>
-<p><tt class="docutils literal">type</tt> is used to automagically convert the command line arguments
+<tt class="docutils literal"><span class="pre">(help,</span> <span class="pre">kind,</span> <span class="pre">abbrev,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar)</span></tt></blockquote>
+<p>where <tt class="docutils literal"><span class="pre">help</span></tt> is the help message, <tt class="docutils literal"><span class="pre">kind</span></tt> is a string in the set {
+<tt class="docutils literal"><span class="pre">&quot;flag&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;option&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;positional&quot;</span></tt>}, <tt class="docutils literal"><span class="pre">abbrev</span></tt> is a
+one-character string, <tt class="docutils literal"><span class="pre">type</span></tt> is a callable taking a string in input,
+<tt class="docutils literal"><span class="pre">choices</span></tt> is a discrete sequence of values and <tt class="docutils literal"><span class="pre">metavar</span></tt> is a string.</p>
+<p><tt class="docutils literal"><span class="pre">type</span></tt> is used to automagically convert the command line arguments
from the string type to any Python type; by default there is no
-convertion and <tt class="docutils literal">type=None</tt>.</p>
-<p><tt class="docutils literal">choices</tt> is used to restrict the number of the valid
-options; by default there is no restriction i.e. <tt class="docutils literal">choices=None</tt>.</p>
-<p><tt class="docutils literal">metavar</tt> is used to change the argument name in the usage message
-(and only there); by default the metavar is <tt class="docutils literal">None</tt>: this means that
+conversion and <tt class="docutils literal"><span class="pre">type=None</span></tt>.</p>
+<p><tt class="docutils literal"><span class="pre">choices</span></tt> is used to restrict the number of the valid
+options; by default there is no restriction i.e. <tt class="docutils literal"><span class="pre">choices=None</span></tt>.</p>
+<p><tt class="docutils literal"><span class="pre">metavar</span></tt> is used to change the argument name in the usage message
+(and only there); by default the metavar is <tt class="docutils literal"><span class="pre">None</span></tt>: this means that
the name in the usage message is the same as the argument name,
unless the argument has a default and in such a case is
equal to the stringified form of the default.</p>
@@ -903,12 +954,19 @@ def main(operator, *numbers):
result = dict(add=0.0, mul=1.0)[operator]
for n in numbers:
result = op(result, n)
- print(result)
+ return result
if __name__ == '__main__':
- plac.call(main)
+ print(plac.call(main)[0])
</pre>
+<p>Usually the main function of a script works by side effects and returns
+<tt class="docutils literal"><span class="pre">None</span></tt>; in this example instead I choose to return the number and to
+print it in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block.</p>
+<p>Notice that <em>plac.call returns a list of strings</em>: in particular, it
+returns a single-element list if the main function returns a single
+non-None element (as in this example) or an empty list if the main
+function returns <tt class="docutils literal"><span class="pre">None</span></tt>.</p>
<p>Here is the usage:</p>
<pre class="literal-block">
usage: example10.py [-h] {add,mul} [n [n ...]]
@@ -923,7 +981,7 @@ optional arguments:
-h, --help show this help message and exit
</pre>
-<p>Notice that the docstring of the <tt class="docutils literal">main</tt> function has been automatically added
+<p>Notice that the docstring of the <tt class="docutils literal"><span class="pre">main</span></tt> function has been automatically added
to the usage message. Here are a couple of examples of use:</p>
<pre class="literal-block">
$ python example10.py add 1 2 3 4
@@ -934,63 +992,38 @@ $ 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')
</pre>
-</div>
-<div class="section" id="keyword-arguments">
-<h1><a class="toc-backref" href="#id8">Keyword arguments</a></h1>
-<p>Starting from release 0.4, if your main function has keyword arguments,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> recognizes arguments of the form <tt class="docutils literal">&quot;name=value&quot;</tt> in the command line.
-Here is an example:</p>
-<pre class="literal-block">
-# example12.py
-import plac
-
-&#64;plac.annotations(
- opt=('some option', 'option'),
- args='default arguments',
- kw='keyword arguments')
-def main(opt, *args, **kw):
- print(opt, args, kw)
-
-if __name__ == '__main__':
- plac.call(main)
-
-</pre>
-<p>Here is the generated usage message:</p>
-<pre class="literal-block">
-usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
-
-positional arguments:
- args default arguments
- kw keyword arguments
-
-optional arguments:
- -h, --help show this help message and exit
- -o, --opt OPT some option
-
-</pre>
-<p>Here is how you call the script:</p>
-<pre class="literal-block">
-$ python example12.py 1 2 kw1=1 kw2=2 --opt=0
-('0', ('1', '2'), {'kw1': '1', 'kw2': '2'})
+<p>If the main function returns a generic number of elements,
+the elements returned by <tt class="docutils literal"><span class="pre">plac.call</span></tt> are stringified by invoking
+<tt class="docutils literal"><span class="pre">str</span></tt> on each of them.
+The reason is to simplify testing: a plac-based
+command-line interface can be tested by simply comparing lists of
+strings in input and lists of strings in output.
+For instance a doctest may look like this:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import example10
+&gt;&gt;&gt; plac.call(example10.main, ['add', '1', '2'])
+['3.0']
</pre>
-<p>When using keyword arguments, one must be careful to use names which
-are not alreay taken; for instance in this examples the name <tt class="docutils literal">opt</tt>
-is taken:</p>
-<pre class="literal-block">
-$ python example12.py 1 2 kw1=1 kw2=2 opt=0
-usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
-example12.py: error: colliding keyword arguments: opt
+<p><tt class="docutils literal"><span class="pre">plac.call</span></tt> works for generators too:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def main(n):
+... for i in range(int(n)):
+... yield i
+&gt;&gt;&gt; plac.call(main, ['3'])
+['0', '1', '2']
</pre>
-<p>The names taken are the names of the flags, of the options, and of the
-positional arguments, excepted varargs and keywords. This limitation
-is a consequence of the way the argument names are managed in function calls
-by the Python language.</p>
+<p>However, you should notice that <tt class="docutils literal"><span class="pre">plac.call</span></tt> is <em>eager</em>, not lazy:
+the generator is exhausted by the call. This behavior avoids mistakes like
+forgetting of applying <tt class="docutils literal"><span class="pre">list(result)</span></tt> to the result of a <tt class="docutils literal"><span class="pre">plac.call</span></tt>.</p>
+<p>This behavior makes testing easier and supports the <em>yield-is-print</em>
+pattern: just replace the occurrences of <tt class="docutils literal"><span class="pre">print</span></tt> with <tt class="docutils literal"><span class="pre">yield</span></tt> in
+the main function and you will get an easy to test interface.</p>
</div>
<div class="section" id="a-realistic-example">
-<h1><a class="toc-backref" href="#id9">A realistic example</a></h1>
+<h1><a class="toc-backref" href="#id8">A realistic example</a></h1>
<p>Here is a more realistic script using most of the features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to
run SQL queries on a database by relying on <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a>. Notice the usage
-of the <tt class="docutils literal">type</tt> feature to automagically convert a SQLAlchemy connection
+of the <tt class="docutils literal"><span class="pre">type</span></tt> feature to automagically convert a SQLAlchemy connection
string into a <a class="reference external" href="http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html">SqlSoup</a> object:</p>
<pre class="literal-block">
# dbcli.py
@@ -1006,21 +1039,31 @@ from sqlalchemy.ext.sqlsoup import SqlSoup
)
def main(db, header, sqlcmd, delimiter=&quot;|&quot;, *scripts):
&quot;A script to run queries and SQL scripts on a database&quot;
- print('Working on %s' % db.bind.url)
+ yield 'Working on %s' % db.bind.url
+
if sqlcmd:
result = db.bind.execute(sqlcmd)
if header: # print the header
- print(delimiter.join(result.keys()))
+ yield delimiter.join(result.keys())
for row in result: # print the rows
- print(delimiter.join(map(str, row)))
+ yield delimiter.join(map(str, row))
for script in scripts:
db.bind.execute(file(script).read())
+ yield 'executed %s' % script
if __name__ == '__main__':
- plac.call(main)
+ for output in plac.call(main):
+ print(output)
</pre>
+<p>You can see the <em>yield-is-print</em> pattern here: instead of using
+<tt class="docutils literal"><span class="pre">print</span></tt> in the main function, we use <tt class="docutils literal"><span class="pre">yield</span></tt>, and we perform the
+print in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block. The advantage of the pattern is that
+the test becomes trivial: had we performed the printing in the main
+function, tje test would have involved redirecting <tt class="docutils literal"><span class="pre">sys.stdout</span></tt> to a
+<tt class="docutils literal"><span class="pre">StringIO</span></tt> object and we know that redirecting <tt class="docutils literal"><span class="pre">sys.stdout</span></tt> is
+always ugly, especially in multithreaded situations.</p>
<p>Here is the usage message:</p>
<pre class="literal-block">
usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
@@ -1028,146 +1071,90 @@ 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
+ db Connection string
+ scripts SQL scripts
optional arguments:
- -h, --help show this help message and exit
- -H, --header Header
- -c, --sqlcmd SQL SQL command
- -d, --delimiter | Column separator
+ -h, --help show this help message and exit
+ -H, --header Header
+ -c SQL, --sqlcmd SQL SQL command
+ -d |, --delimiter | Column separator
</pre>
+<p>I leave as an exercise for the reader to write doctests for this
+example.</p>
</div>
-<div class="section" id="advanced-usage">
-<h1><a class="toc-backref" href="#id10">Advanced usage</a></h1>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> relies on a <a class="reference external" href="http://argparse.googlecode.com">argparse</a> for all of the heavy lifting work and it is
-possible to leverage on <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features directly or indirectly.</p>
-<p>For instance, you can make invisible an argument in the usage message
-simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or
-<tt class="docutils literal">argparse.SUPPRESS</tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a>
-directly.</p>
-<p>It is also possible to pass options to the underlying
-<tt class="docutils literal">argparse.ArgumentParser</tt> object (currently it accepts the default
-arguments <tt class="docutils literal">description</tt>, <tt class="docutils literal">epilog</tt>, <tt class="docutils literal">prog</tt>, <tt class="docutils literal">usage</tt>,
-<tt class="docutils literal">add_help</tt>, <tt class="docutils literal">argument_default</tt>, <tt class="docutils literal">parents</tt>, <tt class="docutils literal">prefix_chars</tt>,
-<tt class="docutils literal">fromfile_prefix_chars</tt>, <tt class="docutils literal">conflict_handler</tt>, <tt class="docutils literal">formatter_class</tt>).
-It is enough to set such attributes on the <tt class="docutils literal">main</tt> function. For
-instance</p>
-<pre class="literal-block">
-def main(...):
- pass
-
-main.add_help = False
-</pre>
-<p>disable the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This is not
-particularly elegant, but I assume the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be
-happy with the defaults and would not want to change them; still it is
-possible if she wants to. For instance, by setting the <tt class="docutils literal">description</tt>
-attribute, it is possible to add a comment to the usage message (by
-default the docstring of the <tt class="docutils literal">main</tt> 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 &quot;/&quot;
-as option prefix you can add the lines:</p>
-<pre class="literal-block">
-main.prefix_chars='-/'
-main.short_prefix = '/'
-</pre>
-<p>The recognition of the <tt class="docutils literal">short_prefix</tt> attribute is a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-extension; there is also a companion <tt class="docutils literal">long_prefix</tt> attribute with
-default value of <tt class="docutils literal"><span class="pre">&quot;--&quot;</span></tt>. <tt class="docutils literal">prefix_chars</tt> is an <a class="reference external" href="http://argparse.googlecode.com">argparse</a> feature.
-Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> 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 <tt class="docutils literal">main</tt> function for you. For simplicity,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic of that kind.</p>
-<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
-invoking the <tt class="docutils literal">plac.parser_from</tt> utility function:</p>
-<pre class="doctest-block">
-&gt;&gt;&gt; import plac
-&gt;&gt;&gt; def main(arg):
-... pass
-...
-&gt;&gt;&gt; print plac.parser_from(main)
-ArgumentParser(prog='', usage=None, description=None, version=None,
-formatter_class=&lt;class 'argparse.HelpFormatter'&gt;, conflict_handler='error',
-add_help=True)
-</pre>
-<p>I use <tt class="docutils literal">plac.parser_from</tt> in the unit tests of the module, but regular
-users should never need to use it.</p>
-</div>
-<div class="section" id="custom-annotation-objects">
-<h1><a class="toc-backref" href="#id11">Custom annotation objects</a></h1>
-<p>Internally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses an <tt class="docutils literal">Annotation</tt> class to convert the tuples
-in the function signature into annotation objects, i.e. objects with
-six attributes <tt class="docutils literal">help, kind, short, type, choices, metavar</tt>.</p>
-<p>Advanced users can implement their own annotation objects.
-For instance, here is an example of how you could implement annotations for
-positional arguments:</p>
-<pre class="literal-block">
-# annotations.py
-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
-
-</pre>
-<p>You can use such annotations objects as follows:</p>
+<div class="section" id="keyword-arguments">
+<h1><a class="toc-backref" href="#id9">Keyword arguments</a></h1>
+<p>Starting from release 0.4, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports keyword arguments. In
+practice that means that if your main function has keyword arguments,
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> treats specially arguments of the form <tt class="docutils literal"><span class="pre">&quot;name=value&quot;</span></tt> in the
+command line. Here is an example:</p>
<pre class="literal-block">
-# example11.py
+# example12.py
import plac
-from annotations import Positional
&#64;plac.annotations(
- i=Positional(&quot;This is an int&quot;, int),
- n=Positional(&quot;This is a float&quot;, float),
- rest=Positional(&quot;Other arguments&quot;))
-def main(i, n, *rest):
- print(i, n, rest)
+ opt=('some option', 'option'),
+ args='default arguments',
+ kw='keyword arguments')
+def main(opt, *args, **kw):
+ if opt:
+ yield 'opt=%s' % opt
+ if args:
+ yield 'args=%s' % str(args)
+ if kw:
+ yield 'kw=%s' % kw
if __name__ == '__main__':
- import plac; plac.call(main)
+ for output in plac.call(main):
+ print(output)
</pre>
-<p>Here is the usage message you get:</p>
+<p>Here is the generated usage message:</p>
<pre class="literal-block">
-usage: example11.py [-h] i n [rest [rest ...]]
+usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]
positional arguments:
- i This is an int
- n This is a float
- rest Other arguments
+ args default arguments
+ kw keyword arguments
optional arguments:
-h, --help show this help message and exit
+ -opt OPT some option
</pre>
-<p>You can go on and define <tt class="docutils literal">Option</tt> and <tt class="docutils literal">Flag</tt> 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.</p>
+<p>Here is how you call the script:</p>
+<pre class="literal-block">
+$ python example12.py -o X a1 a2 name=value
+opt=X
+args=('a1', 'a2')
+kw={'name': 'value'}
+</pre>
+<p>When using keyword arguments, one must be careful to use names which
+are not alreay taken; for instance in this examples the name <tt class="docutils literal"><span class="pre">opt</span></tt>
+is taken:</p>
+<pre class="literal-block">
+$ python example12.py 1 2 kw1=1 kw2=2 opt=0
+usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
+example12.py: error: colliding keyword arguments: opt
+</pre>
+<p>The names taken are the names of the flags, of the options, and of the
+positional arguments, excepted varargs and keywords. This limitation
+is a consequence of the way the argument names are managed in function calls
+by the Python language.</p>
</div>
<div class="section" id="plac-vs-argparse">
-<h1><a class="toc-backref" href="#id12">plac vs argparse</a></h1>
+<h1><a class="toc-backref" href="#id10">plac vs argparse</a></h1>
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is opinionated and by design it does not try to make available
all of the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> in an easy way. In particular you
should be aware of the following limitations/differences (the
following assumes knowledge of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>):</p>
<ul class="simple">
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> automatically defines both a long and short form for each options,
-just like <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a>. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> 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 <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a>, which I feel is pretty much
-consistent, I have decided not to support this feature.</li>
<li>plac does not support the destination concept: the destination
coincides with the name of the argument, always. This restriction
has some drawbacks. For instance, suppose you want to define a long
-option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal">yield</tt>,
+option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal"><span class="pre">yield</span></tt>,
which is a Python keyword, and since you cannot introduce an
argument with that name in a function definition, it is impossible
to implement it. Your choices are to change the name of the long
@@ -1177,32 +1164,32 @@ documentation puts it: <em>Required options are generally considered bad
form - normal users expect options to be optional. You should avoid
the use of required options whenever possible.</em></li>
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to
-define generalized two-value flags with values different from <tt class="docutils literal">True</tt>
-and <tt class="docutils literal">False</tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but
+define generalized two-value flags with values different from <tt class="docutils literal"><span class="pre">True</span></tt>
+and <tt class="docutils literal"><span class="pre">False</span></tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but
since you can use options with two choices instead, and in any case
-the conversion from <tt class="docutils literal">{True, False}</tt> to any couple of values
+the conversion from <tt class="docutils literal"><span class="pre">{True,</span> <span class="pre">False}</span></tt> to any couple of values
can be trivially implemented with a ternary operator
-(<tt class="docutils literal">value1 if flag else value2</tt>), I have removed it (KISS rules!).</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal">nargs</tt> options directly (it uses them internally,
+(<tt class="docutils literal"><span class="pre">value1</span> <span class="pre">if</span> <span class="pre">flag</span> <span class="pre">else</span> <span class="pre">value2</span></tt>), I have removed it (KISS rules!).</li>
+<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal"><span class="pre">nargs</span></tt> 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 <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and did not feel the need
-to increase the learning curve by adding direct support for <tt class="docutils literal">nargs</tt>.</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support subparsers directly. For the moment, this
-looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</li>
+to increase the learning curve by adding direct support for <tt class="docutils literal"><span class="pre">nargs</span></tt>.</li>
+<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does support subparsers, but you must read the <a class="reference external" href="in-writing">advanced usage
+document</a> to see how it works.</li>
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support actions directly. This also
looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Notice however
-that the ability to define your own annotation objects may mitigate the
-need for custom actions.</li>
+that the ability to define your own annotation objects (again, see
+the <a class="reference external" href="in-writing">advanced usage document</a>) may mitigate the need for custom actions.</li>
</ul>
-<p>I should stress again that if you want to access all of the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features
-from <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you can use <tt class="docutils literal">plac.parser_from</tt> and you will get
+<p>I should stress that if you want to access all of the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features
+from <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you can use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> and you will get
the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object. The the full power of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>
-is then available to you: you can use <tt class="docutils literal">add_argument</tt>, <tt class="docutils literal">add_subparsers()</tt>,
+is then available to you: you can use <tt class="docutils literal"><span class="pre">add_argument</span></tt>, <tt class="docutils literal"><span class="pre">add_subparsers()</span></tt>,
etc. In other words, while some features are not supported directly,
<em>all</em> features are supported indirectly.</p>
</div>
<div class="section" id="plac-vs-the-rest-of-the-world">
-<h1><a class="toc-backref" href="#id13">plac vs the rest of the world</a></h1>
+<h1><a class="toc-backref" href="#id11">plac vs the rest of the world</a></h1>
<p>Originally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> boasted about being &quot;the easiest command-line
arguments parser in the world&quot;. Since then, people started pointing
out to me various projects which are based on the same idea
@@ -1221,19 +1208,23 @@ just the day before <a class="reference external" href="http://pypi.python.org/p
easier than <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
</div>
<div class="section" id="the-future">
-<h1><a class="toc-backref" href="#id14">The future</a></h1>
-<p>Currently plac is around 140 lines of code, not counting blanks,
-comments and docstrings. I do not plan to extend it much in the
+<h1><a class="toc-backref" href="#id12">The future</a></h1>
+<p>Currently <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is around 170 lines of code, not counting blanks,
+comments and docstrings. I do not plan to extend the core much in the
future. The idea is to keep the module short: it is and it should
remain a little wrapper over <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually I have thought about
-contributing the code back to <a class="reference external" href="http://argparse.googlecode.com">argparse</a> if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> becomes successfull
+contributing the core back to <a class="reference external" href="http://argparse.googlecode.com">argparse</a> if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> becomes successfull
and gains a reasonable number of users. For the moment it should be
-considered experimental: after all I wrote the first version of it in
+considered in alpha status: after all I wrote the first version of it in
three days, including the tests, the documentation and the time to
-learn <a class="reference external" href="http://argparse.googlecode.com">argparse</a>.</p>
+learn <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. At the moment I have already written some powerful
+extensions for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and I plan to release them in the 0.5 release, once
+I finish writing <a class="reference external" href="in-writing">advanced usage document</a> .
+The extensions will be released together with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> but in a separated
+module.</p>
</div>
<div class="section" id="trivia-the-story-behind-the-name">
-<h1><a class="toc-backref" href="#id15">Trivia: the story behind the name</a></h1>
+<h1><a class="toc-backref" href="#id13">Trivia: the story behind the name</a></h1>
<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> project started very humble: I just wanted to make
easy_installable my old <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, and to publish it on PyPI.
The original name of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was optionparser and the idea behind it was
@@ -1249,16 +1240,16 @@ of functions annotations in Python 3.</li>
</ol>
<p>Putting together these two observations with the original idea of inferring the
parser I decided to build an <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object from function
-annotations. The <tt class="docutils literal">optionparser</tt> name was ruled out, since I was
-now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal">argparse_plus</tt> was also ruled out,
+annotations. The <tt class="docutils literal"><span class="pre">optionparser</span></tt> name was ruled out, since I was
+now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal"><span class="pre">argparse_plus</span></tt> was also ruled out,
since the typical usage was completely different from the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> usage.</p>
-<p>I made a research on PyPI and the name clap (Command Line Arguments Parser)
+<p>I made a research on PyPI and the name <em>clap</em> (Command Line Arguments Parser)
was not taken, so I renamed everything to clap. After two days
a <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> module appeared on PyPI &lt;expletives deleted&gt;!</p>
<p>Having little imagination, I decided to rename everything again to plac,
an anagram of clap: since it is a non-existing English name, I hope nobody
will steal it from me!</p>
-<p>That's all, I hope you will enjoy working with plac!</p>
+<p>That's all, I hope you will enjoy working with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>!</p>
</div>
</div>
</body>
diff --git a/plac/doc/plac.pdf b/plac/doc/plac.pdf
index 961fdec..d002352 100644
--- a/plac/doc/plac.pdf
+++ b/plac/doc/plac.pdf
@@ -7,7 +7,7 @@
/F2 3 0 R
/F3 4 0 R
/F4 8 0 R
- /F5 54 0 R >>
+ /F5 50 0 R >>
endobj
% 'F1': class PDFType1Font
2 0 obj
@@ -99,7 +99,7 @@ endobj
/Dest [ 46 0 R
/XYZ
62.69291
- 293.0236
+ 329.0236
0 ]
/Rect [ 62.69291
563.5936
@@ -117,7 +117,7 @@ endobj
/Dest [ 46 0 R
/XYZ
62.69291
- 293.0236
+ 329.0236
0 ]
/Rect [ 527.0227
563.5936
@@ -132,10 +132,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 58 0 R
+ /Dest [ 54 0 R
/XYZ
62.69291
- 579.0236
+ 627.0236
0 ]
/Rect [ 62.69291
545.5936
@@ -150,10 +150,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 58 0 R
+ /Dest [ 54 0 R
/XYZ
62.69291
- 579.0236
+ 627.0236
0 ]
/Rect [ 527.0227
545.5936
@@ -168,10 +168,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 64 0 R
+ /Dest [ 60 0 R
/XYZ
62.69291
- 295.4236
+ 298.2236
0 ]
/Rect [ 62.69291
527.5936
@@ -186,10 +186,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 64 0 R
+ /Dest [ 60 0 R
/XYZ
62.69291
- 295.4236
+ 298.2236
0 ]
/Rect [ 527.0227
527.5936
@@ -204,14 +204,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 76 0 R
+ /Dest [ 72 0 R
/XYZ
62.69291
- 623.8236
+ 253.4236
0 ]
/Rect [ 62.69291
509.5936
- 158.2629
+ 254.3829
521.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -222,10 +222,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 76 0 R
+ /Dest [ 72 0 R
/XYZ
62.69291
- 623.8236
+ 253.4236
0 ]
/Rect [ 527.0227
509.5936
@@ -240,10 +240,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 82 0 R
+ /Dest [ 76 0 R
/XYZ
62.69291
- 765.0236
+ 251.0236
0 ]
/Rect [ 62.69291
491.5936
@@ -258,10 +258,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 82 0 R
+ /Dest [ 76 0 R
/XYZ
62.69291
- 765.0236
+ 251.0236
0 ]
/Rect [ 527.0227
491.5936
@@ -276,10 +276,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 82 0 R
+ /Dest [ 78 0 R
/XYZ
62.69291
- 304.7299
+ 443.4236
0 ]
/Rect [ 62.69291
473.5936
@@ -294,10 +294,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 82 0 R
+ /Dest [ 78 0 R
/XYZ
62.69291
- 304.7299
+ 443.4236
0 ]
/Rect [ 527.0227
473.5936
@@ -312,10 +312,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 89 0 R
+ /Dest [ 86 0 R
/XYZ
62.69291
- 558.6236
+ 695.8236
0 ]
/Rect [ 62.69291
455.5936
@@ -330,10 +330,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 89 0 R
+ /Dest [ 86 0 R
/XYZ
62.69291
- 558.6236
+ 695.8236
0 ]
/Rect [ 527.0227
455.5936
@@ -348,14 +348,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 91 0 R
+ /Dest [ 90 0 R
/XYZ
62.69291
- 474.4849
+ 254.0849
0 ]
/Rect [ 62.69291
437.5936
- 158.8229
+ 153.2929
449.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -366,12 +366,12 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 91 0 R
+ /Dest [ 90 0 R
/XYZ
62.69291
- 474.4849
+ 254.0849
0 ]
- /Rect [ 527.0227
+ /Rect [ 521.4627
437.5936
532.5827
449.5936 ]
@@ -384,14 +384,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 95 0 R
+ /Dest [ 93 0 R
/XYZ
62.69291
- 582.6236
+ 180.6236
0 ]
/Rect [ 62.69291
419.5936
- 153.2929
+ 158.8229
431.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -402,10 +402,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 95 0 R
+ /Dest [ 93 0 R
/XYZ
62.69291
- 582.6236
+ 180.6236
0 ]
/Rect [ 521.4627
419.5936
@@ -420,14 +420,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 106 0 R
+ /Dest [ 97 0 R
/XYZ
62.69291
- 631.8236
+ 152.2236
0 ]
/Rect [ 62.69291
401.5936
- 142.1629
+ 141.6229
413.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -438,10 +438,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 106 0 R
+ /Dest [ 97 0 R
/XYZ
62.69291
- 631.8236
+ 152.2236
0 ]
/Rect [ 521.4627
401.5936
@@ -456,14 +456,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 108 0 R
+ /Dest [ 126 0 R
/XYZ
62.69291
- 695.8236
+ 393.0236
0 ]
/Rect [ 62.69291
383.5936
- 191.5929
+ 194.9529
395.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -474,10 +474,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 108 0 R
+ /Dest [ 126 0 R
/XYZ
62.69291
- 695.8236
+ 393.0236
0 ]
/Rect [ 521.4627
383.5936
@@ -492,14 +492,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 142 0 R
+ /Dest [ 146 0 R
/XYZ
62.69291
765.0236
0 ]
/Rect [ 62.69291
365.5936
- 141.6229
+ 111.5829
377.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -510,7 +510,7 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 142 0 R
+ /Dest [ 146 0 R
/XYZ
62.69291
765.0236
@@ -528,14 +528,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 142 0 R
+ /Dest [ 146 0 R
/XYZ
62.69291
- 276.0236
+ 630.0236
0 ]
/Rect [ 62.69291
347.5936
- 194.9529
+ 219.9529
359.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -546,10 +546,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 142 0 R
+ /Dest [ 146 0 R
/XYZ
62.69291
- 276.0236
+ 630.0236
0 ]
/Rect [ 521.4627
347.5936
@@ -558,75 +558,63 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER30': class LinkAnnotation
+% 'Annot.NUMBER30': class PDFDictionary
35 0 obj
-<< /Border [ 0
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/getopt.html) >>
+ /Border [ 0
0
0 ]
- /Contents ()
- /Dest [ 157 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 62.69291
- 329.5936
- 111.5829
- 341.5936 ]
+ /Rect [ 214.8914
+ 281.5936
+ 246.5585
+ 293.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER31': class LinkAnnotation
+% 'Annot.NUMBER31': class PDFDictionary
36 0 obj
-<< /Border [ 0
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/optparse.html) >>
+ /Border [ 0
0
0 ]
- /Contents ()
- /Dest [ 157 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 521.4627
- 329.5936
- 532.5827
- 341.5936 ]
+ /Rect [ 346.507
+ 281.5936
+ 389.2842
+ 293.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER32': class LinkAnnotation
+% 'Annot.NUMBER32': class PDFDictionary
37 0 obj
-<< /Border [ 0
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
0
0 ]
- /Contents ()
- /Dest [ 157 0 R
- /XYZ
- 62.69291
- 654.0236
- 0 ]
- /Rect [ 62.69291
- 311.5936
- 219.9529
- 323.5936 ]
+ /Rect [ 493.1227
+ 281.5936
+ 531.4956
+ 293.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER33': class LinkAnnotation
+% 'Annot.NUMBER33': class PDFDictionary
38 0 obj
-<< /Border [ 0
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
0
0 ]
- /Contents ()
- /Dest [ 157 0 R
- /XYZ
- 62.69291
- 654.0236
- 0 ]
- /Rect [ 521.4627
- 311.5936
- 532.5827
- 323.5936 ]
+ /Rect [ 346.384
+ 269.5936
+ 388.8477
+ 281.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -634,14 +622,14 @@ endobj
39 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/getopt.html) >>
+ /URI (http://www.welton.it/articles/scalable_systems) >>
/Border [ 0
0
0 ]
- /Rect [ 214.8914
- 245.5936
- 246.5585
- 257.5936 ]
+ /Rect [ 292.1608
+ 227.5936
+ 350.0128
+ 239.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -649,14 +637,14 @@ endobj
40 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 346.507
- 245.5936
- 389.2842
- 257.5936 ]
+ /Rect [ 208.2364
+ 179.5936
+ 229.8923
+ 191.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -668,10 +656,10 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 493.1227
- 245.5936
- 531.4956
- 257.5936 ]
+ /Rect [ 152.7329
+ 155.5936
+ 192.1929
+ 167.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -679,14 +667,14 @@ endobj
42 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 346.384
- 233.5936
- 388.8477
- 245.5936 ]
+ /Rect [ 116.9711
+ 137.5936
+ 139.5794
+ 149.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -694,14 +682,14 @@ endobj
43 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 292.1608
- 191.5936
- 350.0128
- 203.5936 ]
+ /Rect [ 277.9887
+ 137.5936
+ 321.7169
+ 149.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -713,10 +701,10 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 208.2364
- 143.5936
- 229.8923
- 155.5936 ]
+ /Rect [ 504.0394
+ 125.5936
+ 525.3627
+ 137.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -728,10 +716,10 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 152.7329
- 119.5936
- 192.1929
- 131.5936 ]
+ /Rect [ 351.0408
+ 113.5936
+ 390.5008
+ 125.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -778,12 +766,12 @@ endobj
43 0 R
44 0 R
45 0 R ]
- /Contents 177 0 R
+ /Contents 164 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -802,9 +790,9 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 116.9711
+ /Rect [ 247.8817
756.5936
- 139.5794
+ 266.2217
768.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -813,79 +801,19 @@ endobj
48 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 277.9887
- 756.5936
- 321.7169
- 768.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER43': class PDFDictionary
-49 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 504.0394
- 744.5936
- 525.3627
- 756.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER44': class PDFDictionary
-50 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 351.0408
- 732.5936
- 390.5008
- 744.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER45': class PDFDictionary
-51 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 247.8817
- 708.5936
- 266.2217
- 720.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER46': class PDFDictionary
-52 0 obj
-<< /A << /S /URI
- /Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
/Rect [ 62.69291
- 678.5936
+ 726.5936
85.3538
- 690.5936 ]
+ 738.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER47': class PDFDictionary
-53 0 obj
+% 'Annot.NUMBER43': class PDFDictionary
+49 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -893,14 +821,14 @@ endobj
0
0 ]
/Rect [ 124.2211
- 606.5936
+ 654.5936
146.9252
- 618.5936 ]
+ 666.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'F5': class PDFType1Font
-54 0 obj
+50 0 obj
% Font Helvetica-Oblique
<< /BaseFont /Helvetica-Oblique
/Encoding /WinAnsiEncoding
@@ -908,8 +836,8 @@ endobj
/Subtype /Type1
/Type /Font >>
endobj
-% 'Annot.NUMBER48': class PDFDictionary
-55 0 obj
+% 'Annot.NUMBER44': class PDFDictionary
+51 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/getopt.html) >>
@@ -917,14 +845,14 @@ endobj
0
0 ]
/Rect [ 325.341
- 214.3936
+ 262.3936
356.6198
- 226.3936 ]
+ 274.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER49': class PDFDictionary
-56 0 obj
+% 'Annot.NUMBER45': class PDFDictionary
+52 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html) >>
@@ -932,14 +860,14 @@ endobj
0
0 ]
/Rect [ 376.7786
- 214.3936
+ 262.3936
419.1674
- 226.3936 ]
+ 274.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER50': class PDFDictionary
-57 0 obj
+% 'Annot.NUMBER46': class PDFDictionary
+53 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -947,31 +875,27 @@ endobj
0
0 ]
/Rect [ 365.694
- 202.3936
+ 250.3936
408.8281
- 214.3936 ]
+ 262.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page2': class PDFPage
-58 0 obj
+54 0 obj
% Page dictionary
<< /Annots [ 47 0 R
48 0 R
49 0 R
- 50 0 R
51 0 R
52 0 R
- 53 0 R
- 55 0 R
- 56 0 R
- 57 0 R ]
- /Contents 178 0 R
+ 53 0 R ]
+ /Contents 165 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -982,8 +906,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER51': class PDFDictionary
-59 0 obj
+% 'Annot.NUMBER47': class PDFDictionary
+55 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -991,14 +915,14 @@ endobj
0
0 ]
/Rect [ 83.82606
- 609.3936
+ 657.3936
106.0692
- 621.3936 ]
+ 669.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER52': class PDFDictionary
-60 0 obj
+% 'Annot.NUMBER48': class PDFDictionary
+56 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1006,14 +930,14 @@ endobj
0
0 ]
/Rect [ 243.8829
- 597.3936
+ 645.3936
265.0029
- 609.3936 ]
+ 657.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER53': class PDFDictionary
-61 0 obj
+% 'Annot.NUMBER49': class PDFDictionary
+57 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1021,14 +945,14 @@ endobj
0
0 ]
/Rect [ 83.6329
- 460.1936
+ 508.1936
105.6829
- 472.1936 ]
+ 520.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER54': class PDFDictionary
-62 0 obj
+% 'Annot.NUMBER50': class PDFDictionary
+58 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1036,14 +960,14 @@ endobj
0
0 ]
/Rect [ 421.9727
- 460.1936
+ 508.1936
465.1427
- 472.1936 ]
+ 520.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER55': class PDFDictionary
-63 0 obj
+% 'Annot.NUMBER51': class PDFDictionary
+59 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1051,26 +975,26 @@ endobj
0
0 ]
/Rect [ 211.6529
- 310.9936
+ 313.7936
232.7729
- 322.9936 ]
+ 325.7936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page3': class PDFPage
-64 0 obj
+60 0 obj
% Page dictionary
-<< /Annots [ 59 0 R
- 60 0 R
- 61 0 R
- 62 0 R
- 63 0 R ]
- /Contents 179 0 R
+<< /Annots [ 55 0 R
+ 56 0 R
+ 57 0 R
+ 58 0 R
+ 59 0 R ]
+ /Contents 166 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1081,8 +1005,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER56': class PDFDictionary
-65 0 obj
+% 'Annot.NUMBER52': class PDFDictionary
+61 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1096,8 +1020,8 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER57': class PDFDictionary
-66 0 obj
+% 'Annot.NUMBER53': class PDFDictionary
+62 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1111,8 +1035,8 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER58': class PDFDictionary
-67 0 obj
+% 'Annot.NUMBER54': class PDFDictionary
+63 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1126,34 +1050,18 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER59': class PDFDictionary
-68 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 91.59679
- 179.7936
- 109.9368
- 191.7936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
% 'Page4': class PDFPage
-69 0 obj
+64 0 obj
% Page dictionary
-<< /Annots [ 65 0 R
- 66 0 R
- 67 0 R
- 68 0 R ]
- /Contents 180 0 R
+<< /Annots [ 61 0 R
+ 62 0 R
+ 63 0 R ]
+ /Contents 167 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1164,8 +1072,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER60': class PDFDictionary
-70 0 obj
+% 'Annot.NUMBER55': class PDFDictionary
+65 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1173,44 +1081,44 @@ endobj
0
0 ]
/Rect [ 446.1627
- 651.3936
+ 699.3936
464.5027
- 663.3936 ]
+ 711.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER61': class PDFDictionary
-71 0 obj
+% 'Annot.NUMBER56': class PDFDictionary
+66 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 357.8702
- 540.3936
- 416.0058
- 552.3936 ]
+ /Rect [ 62.69291
+ 669.3936
+ 84.28901
+ 681.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER62': class PDFDictionary
-72 0 obj
+% 'Annot.NUMBER57': class PDFDictionary
+67 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
/Border [ 0
0
0 ]
- /Rect [ 182.0729
- 528.3936
- 234.3229
- 540.3936 ]
+ /Rect [ 161.7834
+ 669.3936
+ 217.2895
+ 681.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER63': class PDFDictionary
-73 0 obj
+% 'Annot.NUMBER58': class PDFDictionary
+68 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1218,57 +1126,73 @@ endobj
0
0 ]
/Rect [ 62.69291
- 498.3936
- 84.28901
- 510.3936 ]
+ 268.9936
+ 83.81291
+ 280.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER64': class PDFDictionary
-74 0 obj
+% 'Annot.NUMBER59': class PDFDictionary
+69 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
- 498.3936
- 217.2895
- 510.3936 ]
+ /Rect [ 357.8702
+ 169.9936
+ 416.0058
+ 181.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER65': class PDFDictionary
-75 0 obj
+% 'Annot.NUMBER60': class PDFDictionary
+70 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 182.0729
+ 157.9936
+ 234.3229
+ 169.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER61': class PDFDictionary
+71 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 514.2427
- 295.1936
- 532.2243
- 307.1936 ]
+ /Rect [ 150.5229
+ 145.9936
+ 171.6429
+ 157.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page5': class PDFPage
-76 0 obj
+72 0 obj
% Page dictionary
-<< /Annots [ 70 0 R
- 71 0 R
- 72 0 R
- 73 0 R
- 74 0 R
- 75 0 R ]
- /Contents 181 0 R
+<< /Annots [ 65 0 R
+ 66 0 R
+ 67 0 R
+ 68 0 R
+ 69 0 R
+ 70 0 R
+ 71 0 R ]
+ /Contents 168 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1279,47 +1203,31 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER66': class PDFDictionary
-77 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 183.9662
- 395.7936
- 205.2545
- 407.7936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER67': class PDFDictionary
-78 0 obj
+% 'Annot.NUMBER62': class PDFDictionary
+73 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 184.4029
- 199.3936
- 202.7429
- 211.3936 ]
+ /Rect [ 494.1558
+ 651.3936
+ 515.9027
+ 663.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page6': class PDFPage
-79 0 obj
+74 0 obj
% Page dictionary
-<< /Annots [ 77 0 R
- 78 0 R ]
- /Contents 182 0 R
+<< /Annots [ 73 0 R ]
+ /Contents 169 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1330,8 +1238,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER68': class PDFDictionary
-80 0 obj
+% 'Annot.NUMBER63': class PDFDictionary
+75 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1339,14 +1247,34 @@ endobj
0
0 ]
/Rect [ 62.69291
- 729.5936
- 84.57878
- 741.5936 ]
+ 215.5936
+ 84.62846
+ 227.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER69': class PDFDictionary
-81 0 obj
+% 'Page7': class PDFPage
+76 0 obj
+% Page dictionary
+<< /Annots [ 75 0 R ]
+ /Contents 170 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 163 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER64': class PDFDictionary
+77 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1354,23 +1282,22 @@ endobj
0
0 ]
/Rect [ 110.2829
- 245.2999
+ 383.9936
132.8629
- 257.2999 ]
+ 395.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page7': class PDFPage
-82 0 obj
+% 'Page8': class PDFPage
+78 0 obj
% Page dictionary
-<< /Annots [ 80 0 R
- 81 0 R ]
- /Contents 183 0 R
+<< /Annots [ 77 0 R ]
+ /Contents 171 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1381,53 +1308,53 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER70': class PDFDictionary
-83 0 obj
+% 'Annot.NUMBER65': class PDFDictionary
+79 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 358.6729
- 574.1936
- 379.7929
- 586.1936 ]
+ /Rect [ 303.6429
+ 711.3936
+ 324.7629
+ 723.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER71': class PDFDictionary
-84 0 obj
+% 'Annot.NUMBER66': class PDFDictionary
+80 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 104.3155
- 511.1936
- 143.7755
- 523.1936 ]
+ /Rect [ 157.3904
+ 660.3936
+ 179.9938
+ 672.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER72': class PDFDictionary
-85 0 obj
+% 'Annot.NUMBER67': class PDFDictionary
+81 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 414.3275
- 511.1936
- 453.7875
- 523.1936 ]
+ /Rect [ 184.0634
+ 648.3936
+ 223.5234
+ 660.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER73': class PDFDictionary
-86 0 obj
+% 'Annot.NUMBER68': class PDFDictionary
+82 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1435,92 +1362,73 @@ endobj
0
0 ]
/Rect [ 62.69291
- 499.1936
- 106.6498
- 511.1936 ]
+ 636.3936
+ 102.1529
+ 648.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER74': class PDFDictionary
-87 0 obj
+% 'Annot.NUMBER69': class PDFDictionary
+83 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 189.6004
- 499.1936
- 207.9404
- 511.1936 ]
+ /Rect [ 192.7997
+ 636.3936
+ 237.9391
+ 648.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER75': class PDFDictionary
-88 0 obj
+% 'Annot.NUMBER70': class PDFDictionary
+84 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 355.0429
- 307.1936
- 397.2829
- 319.1936 ]
+ /Rect [ 324.4372
+ 636.3936
+ 342.7772
+ 648.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page8': class PDFPage
-89 0 obj
-% Page dictionary
-<< /Annots [ 83 0 R
- 84 0 R
- 85 0 R
- 86 0 R
- 87 0 R
- 88 0 R ]
- /Contents 184 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 176 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER76': class PDFDictionary
-90 0 obj
+% 'Annot.NUMBER71': class PDFDictionary
+85 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 384.3381
- 439.0549
- 405.6967
- 451.0549 ]
+ /Rect [ 355.0429
+ 444.3936
+ 397.2829
+ 456.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page9': class PDFPage
-91 0 obj
+86 0 obj
% Page dictionary
-<< /Annots [ 90 0 R ]
- /Contents 185 0 R
+<< /Annots [ 79 0 R
+ 80 0 R
+ 81 0 R
+ 82 0 R
+ 83 0 R
+ 84 0 R
+ 85 0 R ]
+ /Contents 172 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1531,8 +1439,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER77': class PDFDictionary
-92 0 obj
+% 'Annot.NUMBER72': class PDFDictionary
+87 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1540,14 +1448,14 @@ endobj
0
0 ]
/Rect [ 338.1568
- 547.1936
+ 218.6549
360.5113
- 559.1936 ]
+ 230.6549 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER78': class PDFDictionary
-93 0 obj
+% 'Annot.NUMBER73': class PDFDictionary
+88 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://www.sqlalchemy.org/) >>
@@ -1555,14 +1463,14 @@ endobj
0
0 ]
/Rect [ 110.6843
- 535.1936
+ 206.6549
169.0343
- 547.1936 ]
+ 218.6549 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER79': class PDFDictionary
-94 0 obj
+% 'Annot.NUMBER74': class PDFDictionary
+89 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html) >>
@@ -1570,24 +1478,24 @@ endobj
0
0 ]
/Rect [ 168.3029
- 523.1936
+ 194.6549
208.8829
- 535.1936 ]
+ 206.6549 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page10': class PDFPage
-95 0 obj
+90 0 obj
% Page dictionary
-<< /Annots [ 92 0 R
- 93 0 R
- 94 0 R ]
- /Contents 186 0 R
+<< /Annots [ 87 0 R
+ 88 0 R
+ 89 0 R ]
+ /Contents 173 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1598,210 +1506,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER80': class PDFDictionary
-96 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 596.3936
- 83.9079
- 608.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER81': class PDFDictionary
-97 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 133.1029
- 596.3936
- 175.4379
- 608.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER82': class PDFDictionary
-98 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 454.1177
- 596.3936
- 496.4527
- 608.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER83': class PDFDictionary
-99 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 455.2227
- 554.3936
- 534.3667
- 566.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER84': class PDFDictionary
-100 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 127.99
- 387.1936
- 149.3857
- 399.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER85': class PDFDictionary
-101 0 obj
+% 'Annot.NUMBER75': class PDFDictionary
+91 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 326.9971
- 273.9936
- 351.8113
- 285.9936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER86': class PDFDictionary
-102 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 409.706
- 261.9936
- 453.2944
- 273.9936 ]
+ /Rect [ 185.0709
+ 145.1936
+ 208.0228
+ 157.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER87': class PDFDictionary
-103 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 259.0928
- 249.9936
- 302.7528
- 261.9936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER88': class PDFDictionary
-104 0 obj
+% 'Annot.NUMBER76': class PDFDictionary
+92 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 258.3129
- 225.9936
- 279.4329
- 237.9936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER89': class PDFDictionary
-105 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
- 207.9936
- 410.5152
- 219.9936 ]
+ /Rect [ 220.5998
+ 133.1936
+ 243.819
+ 145.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page11': class PDFPage
-106 0 obj
-% Page dictionary
-<< /Annots [ 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 ]
- /Contents 187 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 176 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER90': class PDFDictionary
-107 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 106.6216
- 660.3936
- 128.3202
- 672.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page12': class PDFPage
-108 0 obj
+93 0 obj
% Page dictionary
-<< /Annots [ 107 0 R ]
- /Contents 188 0 R
+<< /Annots [ 91 0 R
+ 92 0 R ]
+ /Contents 174 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1812,8 +1557,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER91': class PDFDictionary
-109 0 obj
+% 'Annot.NUMBER77': class PDFDictionary
+94 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1821,14 +1566,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 729.5936
+ 116.7936
84.8789
- 741.5936 ]
+ 128.7936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER92': class PDFDictionary
-110 0 obj
+% 'Annot.NUMBER78': class PDFDictionary
+95 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1836,14 +1581,14 @@ endobj
0
0 ]
/Rect [ 466.5307
- 729.5936
+ 116.7936
509.8367
- 741.5936 ]
+ 128.7936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER93': class PDFDictionary
-111 0 obj
+% 'Annot.NUMBER79': class PDFDictionary
+96 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1851,74 +1596,36 @@ endobj
0
0 ]
/Rect [ 124.3929
- 705.5936
+ 92.79362
163.8529
- 717.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER94': class PDFDictionary
-112 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 684.5936
- 107.7029
- 696.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER95': class PDFDictionary
-113 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 447.7627
- 684.5936
- 486.6727
- 696.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER96': class PDFDictionary
-114 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 493.1227
- 684.5936
- 531.6927
- 696.5936 ]
+ 104.7936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER97': class PDFDictionary
-115 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
- /Border [ 0
+% 'Page12': class PDFPage
+97 0 obj
+% Page dictionary
+<< /Annots [ 94 0 R
+ 95 0 R
+ 96 0 R ]
+ /Contents 175 0 R
+ /MediaBox [ 0
0
- 0 ]
- /Rect [ 232.9652
- 660.5936
- 271.8752
- 672.5936 ]
- /Subtype /Link
- /Type /Annot >>
+ 595.2756
+ 841.8898 ]
+ /Parent 163 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
endobj
-% 'Annot.NUMBER98': class PDFDictionary
-116 0 obj
+% 'Annot.NUMBER80': class PDFDictionary
+98 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1926,14 +1633,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 570.5936
+ 687.5936
127.9329
- 582.5936 ]
+ 699.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER99': class PDFDictionary
-117 0 obj
+% 'Annot.NUMBER81': class PDFDictionary
+99 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1941,14 +1648,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 552.5936
+ 669.5936
107.9337
- 564.5936 ]
+ 681.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER100': class PDFDictionary
-118 0 obj
+% 'Annot.NUMBER82': class PDFDictionary
+100 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1956,14 +1663,14 @@ endobj
0
0 ]
/Rect [ 308.5389
- 552.5936
+ 669.5936
351.8997
- 564.5936 ]
+ 681.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER101': class PDFDictionary
-119 0 obj
+% 'Annot.NUMBER83': class PDFDictionary
+101 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1971,14 +1678,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 510.5936
+ 627.5936
108.3529
- 522.5936 ]
+ 639.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER102': class PDFDictionary
-120 0 obj
+% 'Annot.NUMBER84': class PDFDictionary
+102 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1986,14 +1693,14 @@ endobj
0
0 ]
/Rect [ 277.2428
- 510.5936
+ 627.5936
321.0228
- 522.5936 ]
+ 639.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER103': class PDFDictionary
-121 0 obj
+% 'Annot.NUMBER85': class PDFDictionary
+103 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2001,14 +1708,14 @@ endobj
0
0 ]
/Rect [ 404.5839
- 498.5936
+ 615.5936
426.0657
- 510.5936 ]
+ 627.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER104': class PDFDictionary
-122 0 obj
+% 'Annot.NUMBER86': class PDFDictionary
+104 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2016,14 +1723,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 444.5936
+ 561.5936
108.61
- 456.5936 ]
+ 573.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER105': class PDFDictionary
-123 0 obj
+% 'Annot.NUMBER87': class PDFDictionary
+105 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2031,14 +1738,14 @@ endobj
0
0 ]
/Rect [ 459.2622
- 432.5936
+ 549.5936
481.289
- 444.5936 ]
+ 561.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER106': class PDFDictionary
-124 0 obj
+% 'Annot.NUMBER88': class PDFDictionary
+106 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2046,29 +1753,29 @@ endobj
0
0 ]
/Rect [ 85.69291
- 402.5936
- 107.0573
- 414.5936 ]
+ 519.5936
+ 108.9242
+ 531.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER107': class PDFDictionary
-125 0 obj
+% 'Annot.NUMBER89': class PDFDictionary
+107 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
- /Rect [ 140.1729
- 390.5936
- 158.5129
- 402.5936 ]
+ /Rect [ 340.9248
+ 519.5936
+ 470.1087
+ 531.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER108': class PDFDictionary
-126 0 obj
+% 'Annot.NUMBER90': class PDFDictionary
+108 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2076,14 +1783,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 372.5936
+ 489.5936
107.9247
- 384.5936 ]
+ 501.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER109': class PDFDictionary
-127 0 obj
+% 'Annot.NUMBER91': class PDFDictionary
+109 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2091,44 +1798,74 @@ endobj
0
0 ]
/Rect [ 85.69291
- 360.5936
+ 477.5936
104.0329
- 372.5936 ]
+ 489.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER110': class PDFDictionary
-128 0 obj
+% 'Annot.NUMBER92': class PDFDictionary
+110 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 489.2227
+ 477.5936
+ 532.176
+ 489.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER93': class PDFDictionary
+111 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 85.69291
+ 465.5936
+ 159.6229
+ 477.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER94': class PDFDictionary
+112 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 340.7317
- 327.5936
- 385.1185
- 339.5936 ]
+ /Rect [ 330.9361
+ 444.5936
+ 376.9555
+ 456.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER111': class PDFDictionary
-129 0 obj
+% 'Annot.NUMBER95': class PDFDictionary
+113 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 451.1022
- 327.5936
- 474.369
- 339.5936 ]
+ /Rect [ 446.2044
+ 444.5936
+ 471.1038
+ 456.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER112': class PDFDictionary
-130 0 obj
+% 'Annot.NUMBER96': class PDFDictionary
+114 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -2136,14 +1873,14 @@ endobj
0
0 ]
/Rect [ 321.0443
- 315.5936
+ 432.5936
399.3474
- 327.5936 ]
+ 444.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER113': class PDFDictionary
-131 0 obj
+% 'Annot.NUMBER97': class PDFDictionary
+115 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2151,14 +1888,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 303.5936
+ 420.5936
107.3744
- 315.5936 ]
+ 432.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER114': class PDFDictionary
-132 0 obj
+% 'Annot.NUMBER98': class PDFDictionary
+116 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2166,14 +1903,14 @@ endobj
0
0 ]
/Rect [ 109.0098
- 240.5936
+ 357.5936
131.9967
- 252.5936 ]
+ 369.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER115': class PDFDictionary
-133 0 obj
+% 'Annot.NUMBER99': class PDFDictionary
+117 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2181,14 +1918,14 @@ endobj
0
0 ]
/Rect [ 397.2929
- 216.5936
+ 333.5936
415.6329
- 228.5936 ]
+ 345.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER116': class PDFDictionary
-134 0 obj
+% 'Annot.NUMBER100': class PDFDictionary
+118 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/opterator) >>
@@ -2196,14 +1933,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 195.5936
+ 312.5936
128.4929
- 207.5936 ]
+ 324.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER117': class PDFDictionary
-135 0 obj
+% 'Annot.NUMBER101': class PDFDictionary
+119 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/CLIArgs) >>
@@ -2211,14 +1948,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 177.5936
+ 294.5936
124.5929
- 189.5936 ]
+ 306.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER118': class PDFDictionary
-136 0 obj
+% 'Annot.NUMBER102': class PDFDictionary
+120 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2226,14 +1963,14 @@ endobj
0
0 ]
/Rect [ 464.3898
- 156.5936
+ 273.5936
503.8498
- 168.5936 ]
+ 285.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER119': class PDFDictionary
-137 0 obj
+% 'Annot.NUMBER103': class PDFDictionary
+121 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2241,14 +1978,14 @@ endobj
0
0 ]
/Rect [ 305.0429
- 144.5936
+ 261.5936
323.3829
- 156.5936 ]
+ 273.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER120': class PDFDictionary
-138 0 obj
+% 'Annot.NUMBER104': class PDFDictionary
+122 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2256,14 +1993,14 @@ endobj
0
0 ]
/Rect [ 455.0104
- 126.5936
+ 243.5936
479.9015
- 138.5936 ]
+ 255.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER121': class PDFDictionary
-139 0 obj
+% 'Annot.NUMBER105': class PDFDictionary
+123 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2271,14 +2008,14 @@ endobj
0
0 ]
/Rect [ 303.707
- 114.5936
+ 231.5936
322.047
- 126.5936 ]
+ 243.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER122': class PDFDictionary
-140 0 obj
+% 'Annot.NUMBER106': class PDFDictionary
+124 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2286,14 +2023,14 @@ endobj
0
0 ]
/Rect [ 328.8186
- 114.5936
+ 231.5936
353.3701
- 126.5936 ]
+ 243.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER123': class PDFDictionary
-141 0 obj
+% 'Annot.NUMBER107': class PDFDictionary
+125 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2301,16 +2038,27 @@ endobj
0
0 ]
/Rect [ 62.69291
- 102.5936
+ 219.5936
81.03291
- 114.5936 ]
+ 231.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page13': class PDFPage
-142 0 obj
+126 0 obj
% Page dictionary
-<< /Annots [ 109 0 R
+<< /Annots [ 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
@@ -2326,29 +2074,13 @@ endobj
122 0 R
123 0 R
124 0 R
- 125 0 R
- 126 0 R
- 127 0 R
- 128 0 R
- 129 0 R
- 130 0 R
- 131 0 R
- 132 0 R
- 133 0 R
- 134 0 R
- 135 0 R
- 136 0 R
- 137 0 R
- 138 0 R
- 139 0 R
- 140 0 R
- 141 0 R ]
- /Contents 189 0 R
+ 125 0 R ]
+ /Contents 176 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -2359,68 +2091,128 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER124': class PDFDictionary
-143 0 obj
+% 'Annot.NUMBER108': class PDFDictionary
+127 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 106.199
+ 729.5936
+ 127.4851
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER109': class PDFDictionary
+128 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 86.82623
+ /Rect [ 128.5202
705.5936
- 126.2862
+ 167.9802
717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER125': class PDFDictionary
-144 0 obj
+% 'Annot.NUMBER110': class PDFDictionary
+129 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 415.1627
+ /Rect [ 459.5254
705.5936
- 459.306
+ 504.114
717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER126': class PDFDictionary
-145 0 obj
+% 'Annot.NUMBER111': class PDFDictionary
+130 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 468.9894
+ /Rect [ 514.2427
705.5936
- 492.0127
+ 530.234
717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER127': class PDFDictionary
-146 0 obj
+% 'Annot.NUMBER112': class PDFDictionary
+131 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 156.6329
+ /Rect [ 139.0773
669.5936
- 196.0929
+ 178.5373
681.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER128': class PDFDictionary
-147 0 obj
+% 'Annot.NUMBER113': class PDFDictionary
+132 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 488.2705
+ 669.5936
+ 509.8666
+ 681.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER114': class PDFDictionary
+133 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 333.3598
+ 657.5936
+ 458.0115
+ 669.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER115': class PDFDictionary
+134 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 194.4129
+ 645.5936
+ 215.5329
+ 657.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER116': class PDFDictionary
+135 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2428,14 +2220,14 @@ endobj
0
0 ]
/Rect [ 83.64556
- 618.5936
+ 594.5936
105.7082
- 630.5936 ]
+ 606.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER129': class PDFDictionary
-148 0 obj
+% 'Annot.NUMBER117': class PDFDictionary
+136 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
@@ -2443,14 +2235,14 @@ endobj
0
0 ]
/Rect [ 446.6
- 618.5936
+ 594.5936
502.5727
- 630.5936 ]
+ 606.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER130': class PDFDictionary
-149 0 obj
+% 'Annot.NUMBER118': class PDFDictionary
+137 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2458,14 +2250,14 @@ endobj
0
0 ]
/Rect [ 275.6828
- 606.5936
+ 582.5936
297.3688
- 618.5936 ]
+ 594.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER131': class PDFDictionary
-150 0 obj
+% 'Annot.NUMBER119': class PDFDictionary
+138 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser) >>
@@ -2473,14 +2265,14 @@ endobj
0
0 ]
/Rect [ 77.19665
- 594.5936
+ 570.5936
139.4904
- 606.5936 ]
+ 582.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER132': class PDFDictionary
-151 0 obj
+% 'Annot.NUMBER120': class PDFDictionary
+139 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2488,14 +2280,14 @@ endobj
0
0 ]
/Rect [ 96.54131
- 582.5936
+ 558.5936
139.0255
- 594.5936 ]
+ 570.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER133': class PDFDictionary
-152 0 obj
+% 'Annot.NUMBER121': class PDFDictionary
+140 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2503,14 +2295,14 @@ endobj
0
0 ]
/Rect [ 203.5016
- 549.5936
+ 525.5936
245.8453
- 561.5936 ]
+ 537.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER134': class PDFDictionary
-153 0 obj
+% 'Annot.NUMBER122': class PDFDictionary
+141 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -2518,14 +2310,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 474.5936
+ 450.5936
138.7898
- 486.5936 ]
+ 462.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER135': class PDFDictionary
-154 0 obj
+% 'Annot.NUMBER123': class PDFDictionary
+142 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2533,14 +2325,14 @@ endobj
0
0 ]
/Rect [ 114.6649
- 462.5936
+ 438.5936
154.1249
- 474.5936 ]
+ 450.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER136': class PDFDictionary
-155 0 obj
+% 'Annot.NUMBER124': class PDFDictionary
+143 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2548,14 +2340,14 @@ endobj
0
0 ]
/Rect [ 191.6329
- 450.5936
+ 426.5936
233.8729
- 462.5936 ]
+ 438.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER137': class PDFDictionary
-156 0 obj
+% 'Annot.NUMBER125': class PDFDictionary
+144 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2563,35 +2355,55 @@ endobj
0
0 ]
/Rect [ 263.3429
- 420.5936
+ 396.5936
286.6829
- 432.5936 ]
+ 408.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER126': class PDFDictionary
+145 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 258.5629
+ 348.5936
+ 276.9029
+ 360.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page14': class PDFPage
-157 0 obj
+146 0 obj
% Page dictionary
-<< /Annots [ 143 0 R
+<< /Annots [ 127 0 R
+ 128 0 R
+ 129 0 R
+ 130 0 R
+ 131 0 R
+ 132 0 R
+ 133 0 R
+ 134 0 R
+ 135 0 R
+ 136 0 R
+ 137 0 R
+ 138 0 R
+ 139 0 R
+ 140 0 R
+ 141 0 R
+ 142 0 R
+ 143 0 R
144 0 R
- 145 0 R
- 146 0 R
- 147 0 R
- 148 0 R
- 149 0 R
- 150 0 R
- 151 0 R
- 152 0 R
- 153 0 R
- 154 0 R
- 155 0 R
- 156 0 R ]
- /Contents 190 0 R
+ 145 0 R ]
+ /Contents 177 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 176 0 R
+ /Parent 163 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -2602,233 +2414,209 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R158': class PDFCatalog
-158 0 obj
+% 'R147': class PDFCatalog
+147 0 obj
% Document Root
-<< /Outlines 160 0 R
- /PageLabels 191 0 R
+<< /Outlines 149 0 R
+ /PageLabels 178 0 R
/PageMode /UseNone
- /Pages 176 0 R
+ /Pages 163 0 R
/Type /Catalog >>
endobj
-% 'R159': class PDFInfo
-159 0 obj
+% 'R148': class PDFInfo
+148 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100604064859-01'00')
+ /CreationDate (D:20100611140636-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Plac: Parsing the Command Line the Easy Way) >>
endobj
-% 'R160': class PDFOutlines
-160 0 obj
-<< /Count 15
- /First 161 0 R
- /Last 175 0 R
+% 'R149': class PDFOutlines
+149 0 obj
+<< /Count 13
+ /First 150 0 R
+ /Last 162 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-161 0 obj
+150 0 obj
<< /Dest [ 46 0 R
/XYZ
62.69291
- 293.0236
+ 329.0236
0 ]
- /Next 162 0 R
- /Parent 160 0 R
+ /Next 151 0 R
+ /Parent 149 0 R
/Title (The importance of scaling down) >>
endobj
% 'Outline.1': class OutlineEntryObject
-162 0 obj
-<< /Dest [ 58 0 R
+151 0 obj
+<< /Dest [ 54 0 R
/XYZ
62.69291
- 579.0236
+ 627.0236
0 ]
- /Next 163 0 R
- /Parent 160 0 R
- /Prev 161 0 R
+ /Next 152 0 R
+ /Parent 149 0 R
+ /Prev 150 0 R
/Title (Scripts with required arguments) >>
endobj
% 'Outline.2': class OutlineEntryObject
-163 0 obj
-<< /Dest [ 64 0 R
+152 0 obj
+<< /Dest [ 60 0 R
/XYZ
62.69291
- 295.4236
+ 298.2236
0 ]
- /Next 164 0 R
- /Parent 160 0 R
- /Prev 162 0 R
+ /Next 153 0 R
+ /Parent 149 0 R
+ /Prev 151 0 R
/Title (Scripts with default arguments) >>
endobj
% 'Outline.3': class OutlineEntryObject
-164 0 obj
-<< /Dest [ 76 0 R
+153 0 obj
+<< /Dest [ 72 0 R
/XYZ
62.69291
- 623.8236
+ 253.4236
0 ]
- /Next 165 0 R
- /Parent 160 0 R
- /Prev 163 0 R
- /Title (Scripts with options) >>
+ /Next 154 0 R
+ /Parent 149 0 R
+ /Prev 152 0 R
+ /Title (Scripts with options \(and smart options\)) >>
endobj
% 'Outline.4': class OutlineEntryObject
-165 0 obj
-<< /Dest [ 82 0 R
+154 0 obj
+<< /Dest [ 76 0 R
/XYZ
62.69291
- 765.0236
+ 251.0236
0 ]
- /Next 166 0 R
- /Parent 160 0 R
- /Prev 164 0 R
+ /Next 155 0 R
+ /Parent 149 0 R
+ /Prev 153 0 R
/Title (Scripts with flags) >>
endobj
% 'Outline.5': class OutlineEntryObject
-166 0 obj
-<< /Dest [ 82 0 R
+155 0 obj
+<< /Dest [ 78 0 R
/XYZ
62.69291
- 304.7299
+ 443.4236
0 ]
- /Next 167 0 R
- /Parent 160 0 R
- /Prev 165 0 R
+ /Next 156 0 R
+ /Parent 149 0 R
+ /Prev 154 0 R
/Title (plac for Python 2.X users) >>
endobj
% 'Outline.6': class OutlineEntryObject
-167 0 obj
-<< /Dest [ 89 0 R
+156 0 obj
+<< /Dest [ 86 0 R
/XYZ
62.69291
- 558.6236
+ 695.8236
0 ]
- /Next 168 0 R
- /Parent 160 0 R
- /Prev 166 0 R
+ /Next 157 0 R
+ /Parent 149 0 R
+ /Prev 155 0 R
/Title (More features) >>
endobj
% 'Outline.7': class OutlineEntryObject
-168 0 obj
-<< /Dest [ 91 0 R
+157 0 obj
+<< /Dest [ 90 0 R
/XYZ
62.69291
- 474.4849
+ 254.0849
0 ]
- /Next 169 0 R
- /Parent 160 0 R
- /Prev 167 0 R
- /Title (Keyword arguments) >>
+ /Next 158 0 R
+ /Parent 149 0 R
+ /Prev 156 0 R
+ /Title (A realistic example) >>
endobj
% 'Outline.8': class OutlineEntryObject
-169 0 obj
-<< /Dest [ 95 0 R
+158 0 obj
+<< /Dest [ 93 0 R
/XYZ
62.69291
- 582.6236
+ 180.6236
0 ]
- /Next 170 0 R
- /Parent 160 0 R
- /Prev 168 0 R
- /Title (A realistic example) >>
+ /Next 159 0 R
+ /Parent 149 0 R
+ /Prev 157 0 R
+ /Title (Keyword arguments) >>
endobj
% 'Outline.9': class OutlineEntryObject
-170 0 obj
-<< /Dest [ 106 0 R
- /XYZ
- 62.69291
- 631.8236
- 0 ]
- /Next 171 0 R
- /Parent 160 0 R
- /Prev 169 0 R
- /Title (Advanced usage) >>
-endobj
-% 'Outline.10': class OutlineEntryObject
-171 0 obj
-<< /Dest [ 108 0 R
- /XYZ
- 62.69291
- 695.8236
- 0 ]
- /Next 172 0 R
- /Parent 160 0 R
- /Prev 170 0 R
- /Title (Custom annotation objects) >>
-endobj
-% 'Outline.11': class OutlineEntryObject
-172 0 obj
-<< /Dest [ 142 0 R
+159 0 obj
+<< /Dest [ 97 0 R
/XYZ
62.69291
- 765.0236
+ 152.2236
0 ]
- /Next 173 0 R
- /Parent 160 0 R
- /Prev 171 0 R
+ /Next 160 0 R
+ /Parent 149 0 R
+ /Prev 158 0 R
/Title (plac vs argparse) >>
endobj
-% 'Outline.12': class OutlineEntryObject
-173 0 obj
-<< /Dest [ 142 0 R
+% 'Outline.10': class OutlineEntryObject
+160 0 obj
+<< /Dest [ 126 0 R
/XYZ
62.69291
- 276.0236
+ 393.0236
0 ]
- /Next 174 0 R
- /Parent 160 0 R
- /Prev 172 0 R
+ /Next 161 0 R
+ /Parent 149 0 R
+ /Prev 159 0 R
/Title (plac vs the rest of the world) >>
endobj
-% 'Outline.13': class OutlineEntryObject
-174 0 obj
-<< /Dest [ 157 0 R
+% 'Outline.11': class OutlineEntryObject
+161 0 obj
+<< /Dest [ 146 0 R
/XYZ
62.69291
765.0236
0 ]
- /Next 175 0 R
- /Parent 160 0 R
- /Prev 173 0 R
+ /Next 162 0 R
+ /Parent 149 0 R
+ /Prev 160 0 R
/Title (The future) >>
endobj
-% 'Outline.14': class OutlineEntryObject
-175 0 obj
-<< /Dest [ 157 0 R
+% 'Outline.12': class OutlineEntryObject
+162 0 obj
+<< /Dest [ 146 0 R
/XYZ
62.69291
- 654.0236
+ 630.0236
0 ]
- /Parent 160 0 R
- /Prev 174 0 R
+ /Parent 149 0 R
+ /Prev 161 0 R
/Title (Trivia: the story behind the name) >>
endobj
-% 'R176': class PDFPages
-176 0 obj
+% 'R163': class PDFPages
+163 0 obj
% page tree
<< /Count 14
/Kids [ 46 0 R
- 58 0 R
+ 54 0 R
+ 60 0 R
64 0 R
- 69 0 R
+ 72 0 R
+ 74 0 R
76 0 R
- 79 0 R
- 82 0 R
- 89 0 R
- 91 0 R
- 95 0 R
- 106 0 R
- 108 0 R
- 142 0 R
- 157 0 R ]
+ 78 0 R
+ 86 0 R
+ 90 0 R
+ 93 0 R
+ 97 0 R
+ 126 0 R
+ 146 0 R ]
/Type /Pages >>
endobj
-% 'R177': class PDFStream
-177 0 obj
+% 'R164': class PDFStream
+164 0 obj
% page stream
-<< /Length 8689 >>
+<< /Length 8958 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -2960,7 +2748,7 @@ 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
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (easy_install -U plac) Tj T* ET
Q
Q
q
@@ -2994,41 +2782,13 @@ 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 305.0236 cm
+1 0 0 1 62.69291 341.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 0 255 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 255 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 237 cm
-q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with required arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 237 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 219 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with default arguments) Tj T* ET
+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
@@ -3036,13 +2796,13 @@ q
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
+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 201 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with options) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with required arguments) Tj T* ET
Q
Q
q
@@ -3050,13 +2810,13 @@ q
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 (5) Tj T* -66.44 0 Td ET
+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 183 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with flags) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with default arguments) Tj T* ET
Q
Q
q
@@ -3064,13 +2824,13 @@ q
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
+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 165 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
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with options \(and smart options\)) Tj T* ET
Q
Q
q
@@ -3078,13 +2838,13 @@ q
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
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (5) 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 (More features) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Scripts with flags) Tj T* ET
Q
Q
q
@@ -3092,13 +2852,13 @@ q
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
+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 129 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Keyword arguments) Tj T* ET
+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
@@ -3106,13 +2866,13 @@ q
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
+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 111 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A realistic example) Tj T* ET
+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
@@ -3120,13 +2880,13 @@ q
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
+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 93 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Advanced usage) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A realistic example) Tj T* ET
Q
Q
q
@@ -3134,13 +2894,13 @@ q
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
+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 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
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Keyword arguments) Tj T* ET
Q
Q
q
@@ -3148,7 +2908,7 @@ q
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
+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
@@ -3162,7 +2922,7 @@ q
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 (13) Tj T* -60.88 0 Td ET
+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
@@ -3211,24 +2971,30 @@ q
Q
Q
q
-1 0 0 1 62.69291 272.0236 cm
+1 0 0 1 62.69291 308.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 206.0236 cm
+1 0 0 1 62.69291 242.0236 cm
q
BT 1 0 0 1 0 52.82 Tm 1.50936 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is no want of command line arguments parsers in the Python world. The standard library alone) Tj T* 0 Tw 1.087126 Tw (contains three different modules: ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (\(from the stone age\), ) Tj 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (\(from Python 2.3\) and ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw .223735 Tw 0 0 0 rg (\(from Python 2.7\). All of them are quite powerful and especially ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (is an industrial strength solution;) Tj T* 0 Tw 1.40311 Tw (unfortunately, all of them feature a non-zero learning curve and a certain verbosity. They do not scale) Tj T* 0 Tw (down well enough, at least in my opinion.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 116.0236 cm
+1 0 0 1 62.69291 152.0236 cm
q
BT 1 0 0 1 0 76.82 Tm .051984 Tw 12 TL /F1 10 Tf 0 0 0 rg (It should not be necessary to stress the importance ) Tj 0 0 .501961 rg (scaling down) Tj 0 0 0 rg (; nevertheless most people are obsessed) Tj T* 0 Tw 1.385868 Tw (with features and concerned with the possibility of scaling up, whereas I think that we should be even) Tj T* 0 Tw .996457 Tw (more concerned with the issue of scaling down. This is an old meme in the computing world: programs) Tj T* 0 Tw 2.499984 Tw (should address the common cases simply, simple things should be kept simple, while at the same) Tj T* 0 Tw .535868 Tw (keeping difficult things 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) Tj T* 0 Tw 2.44686 Tw (handle well the simple cases, while retaining the ability to handle complex cases by relying on the) Tj T* 0 Tw (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 98.02362 cm
+q
+BT 1 0 0 1 0 40.82 Tm 1.488221 Tw 12 TL /F1 10 Tf 0 0 0 rg (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 (which hides most of its complexity by using a ) Tj T* 0 Tw .203318 Tw (declarative interface: the argument parser is inferred rather than written down by imperatively. Still, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is ) Tj T* 0 Tw .125984 Tw (surprisingly scalable upwards, even without using the underlying ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. I have been using Python for 8 ) Tj T* 0 Tw 1.618876 Tw (years and in my experience it is extremely unlikely that you will ever need to go beyond the features) Tj T* 0 Tw ET
+Q
+Q
+q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -3239,38 +3005,38 @@ Q
endstream
endobj
-% 'R178': class PDFStream
-178 0 obj
+% 'R165': class PDFStream
+165 0 obj
% page stream
-<< /Length 5349 >>
+<< /Length 4830 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 693.0236 cm
+1 0 0 1 62.69291 741.0236 cm
q
-BT 1 0 0 1 0 64.82 Tm 1.488221 Tw 12 TL /F1 10 Tf 0 0 0 rg (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 (which hides most of its complexity by using a) Tj T* 0 Tw .203318 Tw (declarative interface: the argument parser is inferred rather than written down by imperatively. Still, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is) Tj T* 0 Tw .125984 Tw (surprisingly scalable upwards, even without using the underlying ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. I have been using Python for 8) Tj T* 0 Tw 1.618876 Tw (years and in my experience it is extremely unlikely that you will ever need to go beyond the features) Tj T* 0 Tw 1.776457 Tw (provided by the declarative interface of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (: they should be more than enough for 99.9% of the use) Tj T* 0 Tw (cases.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.776457 Tw 12 TL /F1 10 Tf 0 0 0 rg (provided by the declarative interface of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (: they should be more than enough for 99.9% of the use) Tj T* 0 Tw (cases.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 591.0236 cm
+1 0 0 1 62.69291 639.0236 cm
q
BT 1 0 0 1 0 88.82 Tm 1.540888 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is targetting especially unsophisticated users, programmers, sys-admins, scientists and in general) Tj T* 0 Tw .81284 Tw (people writing throw-away scripts for themselves, choosing the command line interface because it is the) Tj T* 0 Tw .471751 Tw (quick and simple. Such users are not interested in features, they are interested in a small learning curve:) Tj T* 0 Tw .984988 Tw (they just want to be able to write a simple command line tool from a simple specification, not to build a) Tj T* 0 Tw 1.091235 Tw (command line parser by hand. Unfortunately, the modules in the standard library forces them to go the) Tj T* 0 Tw .014104 Tw (hard way. They are designed to implement power user tools and they have a non-trivial learning curve. On) Tj T* 0 Tw 1.584104 Tw (the contrary, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is designed to be simple to use and extremely concise, as the examples below will) Tj T* 0 Tw (show.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 558.0236 cm
+1 0 0 1 62.69291 606.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Scripts with required arguments) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 492.0236 cm
+1 0 0 1 62.69291 540.0236 cm
q
BT 1 0 0 1 0 52.82 Tm .352209 Tw 12 TL /F1 10 Tf 0 0 0 rg (Let me start with the simplest possible thing: a script that takes a single argument and does something to) Tj T* 0 Tw 1.022485 Tw (it. It cannot get simpler than that, unless you consider a script without command line arguments, where) Tj T* 0 Tw .735488 Tw (there is nothing to parse. Still, it is a use case ) Tj /F5 10 Tf (extremely common) Tj /F1 10 Tf (: I need to write scripts like that nearly) Tj T* 0 Tw .486655 Tw (every day, I wrote hundreds of them in the last few years and I have never been happy. Here is a typical) Tj T* 0 Tw (example of code I have been writing by hand for years:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 290.8236 cm
+1 0 0 1 62.69291 338.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3291,7 +3057,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 186.8236 cm
+1 0 0 1 62.69291 234.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.67881 Tw (should not exists. Actually I think the language should recognize the main function and pass to it the) Tj T* 0 Tw 3.096905 Tw (command line arguments automatically; 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 .69881 Tw (does not help for this incredibly common use case. Using ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (and ) Tj 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (does not help, since they) Tj T* 0 Tw .894104 Tw (are intended to manage options and not positional arguments; the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module helps a bit and it is) Tj T* 0 Tw (able to reduce the boilerplate from nine lines to six lines:) Tj T* ET
Q
@@ -3307,11 +3073,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 132 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# example2.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn\)) Tj T* ( # ...) Tj T* T* ET
+BT 1 0 0 1 0 113.71 Tm /F4 10 Tf 12 TL (# example2.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import argparse) Tj T* ( p = argparse.ArgumentParser\(\)) Tj T* ( p.add_argument\('dsn'\)) Tj T* ET
Q
Q
Q
@@ -3328,14 +3094,14 @@ Q
endstream
endobj
-% 'R179': class PDFStream
-179 0 obj
+% 'R166': class PDFStream
+166 0 obj
% page stream
-<< /Length 3933 >>
+<< /Length 4076 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 679.8236 cm
+1 0 0 1 62.69291 727.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3345,31 +3111,31 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (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
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ( arg = p.parse_args\(\)) Tj T* ( main\(arg.dsn\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 623.8236 cm
+1 0 0 1 62.69291 671.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 40.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 2.206303 Tw (switch to Python 2.7, which at the time of this writing is just about to be released, for many years.) Tj T* 0 Tw .678488 Tw (Moreover, it just feels too complex to instantiate a class and to define a parser by hand for such a trivial) Tj T* 0 Tw (task.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 593.8236 cm
+1 0 0 1 62.69291 641.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.123145 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (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 476.6236 cm
+1 0 0 1 62.69291 524.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3390,13 +3156,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 444.6236 cm
+1 0 0 1 62.69291 492.6236 cm
q
BT 1 0 0 1 0 16.82 Tm .929986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (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 327.4236 cm
+1 0 0 1 62.69291 459.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3406,37 +3172,58 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
+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 ($ python example3.py -h) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 330.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 120 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
+BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (usage: example3.py [-h] dsn) Tj T* T* (Do something with the database) 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 307.4236 cm
+1 0 0 1 62.69291 310.2236 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 274.4236 cm
+1 0 0 1 62.69291 277.2236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Scripts with default arguments) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 244.4236 cm
+1 0 0 1 62.69291 247.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.33436 Tw (The need to have suitable defaults for command line arguments is quite common. For instance I have) Tj T* 0 Tw (encountered this use case at work hundreds of times:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 91.22362 cm
+1 0 0 1 62.69291 94.02362 cm
q
q
1 0 0 1 0 0 cm
@@ -3466,10 +3253,10 @@ Q
endstream
endobj
-% 'R180': class PDFStream
-180 0 obj
+% 'R167': class PDFStream
+167 0 obj
% page stream
-<< /Length 4277 >>
+<< /Length 3963 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -3554,7 +3341,7 @@ 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
Q
Q
q
-1 0 0 1 62.69291 196.2236 cm
+1 0 0 1 62.69291 232.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3564,19 +3351,21 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 180 re B*
+n -6 -6 468.6898 144 re B*
Q
q
-BT 1 0 0 1 0 161.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# example6.py) Tj T* (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* ( # ...) 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
+0 0 0 rg
+BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (# example7.py) Tj T* (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* ( # ...) Tj T* T* (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 164.2236 cm
+1 0 0 1 62.69291 212.2236 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 then you get the following nice usage message:) Tj T* ET
+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
@@ -3590,11 +3379,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 468.6898 108 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (usage: example7.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (Run the given scripts on the database) Tj T* T* ET
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (usage: example7.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (Run the given scripts on the database) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( scripts) Tj T* T* ET
Q
Q
Q
@@ -3611,14 +3400,14 @@ Q
endstream
endobj
-% 'R181': class PDFStream
-181 0 obj
+% 'R168': class PDFStream
+168 0 obj
% page stream
-<< /Length 4903 >>
+<< /Length 4987 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 679.8236 cm
+1 0 0 1 62.69291 727.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3628,48 +3417,63 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (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
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (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 635.8236 cm
+1 0 0 1 62.69291 683.8236 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 .899988 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 the 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 602.8236 cm
+1 0 0 1 62.69291 641.8236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Scripts with options) Tj T* ET
+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 512.8236 cm
+1 0 0 1 62.69291 611.8236 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.02311 Tw (a hundred\), compared to the number of scripts with positional arguments I wrote \(certainly more than a) Tj T* 0 Tw .177045 Tw (thousand of them\). Still, this use case cannot be neglected. The standard library modules \(all of them\) are) Tj T* 0 Tw 2.30686 Tw (quite verbose when it comes to specifying the options and frankly I have never used them directly.) Tj T* 0 Tw 3.10561 Tw (Instead, I have always relied on an old recipe of mine, the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe, which provides a) Tj T* 0 Tw 2.369982 Tw (convenient wrapper over ) Tj 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (. Alternatively, in the simplest cases, I have just performed the) Tj T* 0 Tw (parsing by hand.) Tj T* ET
+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 470.8236 cm
+1 0 0 1 62.69291 458.6236 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
+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 144 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (# example7_.py) Tj T* (from datetime import datetime) Tj T* T* (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ( "Run the given scripts on the database") Tj T* ( for script in scripts:) Tj T* ( print\('executing %s' % script\)) Tj T* ( # ...) Tj T* T* (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 440.8236 cm
+1 0 0 1 62.69291 426.6236 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
+BT 1 0 0 1 0 16.82 Tm .17528 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here the arguments of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function have been annotated with strings which are intented to be used) Tj T* 0 Tw (in the help message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 323.6236 cm
+1 0 0 1 62.69291 285.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3679,24 +3483,36 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
+n -6 -6 468.6898 132 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (# example8.py) Tj T* (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
+BT 1 0 0 1 0 113.71 Tm /F4 10 Tf 12 TL (usage: example7_.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (Run the given scripts on the database) Tj T* T* (positional arguments:) Tj T* ( dsn Database dsn) Tj T* ( scripts SQL 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 255.6236 cm
+1 0 0 1 62.69291 265.4236 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 .358409 Tw ('c'\)) Tj /F1 10 Tf (: the first string is the help string which will appear in the usage message, the second string tell ) Tj 0 0 .501961 rg (plac) Tj T* 0 Tw .560988 Tw 0 0 0 rg (that ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (is an option and the third string that it can be abbreviated with the letter ) Tj /F4 10 Tf (c) Tj /F1 10 Tf (. Of course, the) Tj T* 0 Tw .89284 Tw (long option format \() Tj /F4 10 Tf (--command=) Tj /F1 10 Tf (\) comes from the argument name. The resulting usage message is the) Tj T* 0 Tw (following:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize much more complex annotations, as I will show in the next paragraphs.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 126.4236 cm
+1 0 0 1 62.69291 232.4236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Scripts with options \(and smart options\)) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 142.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.02311 Tw (a hundred\), compared to the number of scripts with positional arguments I wrote \(certainly more than a) Tj T* 0 Tw .177045 Tw (thousand of them\). Still, this use case cannot be neglected. The standard library modules \(all of them\) are) Tj T* 0 Tw 2.30686 Tw (quite verbose when it comes to specifying the options and frankly I have never used them directly.) Tj T* 0 Tw 3.10561 Tw (Instead, I have always relied on an old recipe of mine, the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe, which provides a) Tj T* 0 Tw 2.369982 Tw (convenient wrapper over ) Tj 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (. Alternatively, in the simplest cases, I have just performed the) Tj T* 0 Tw (parsing by hand. In ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the parser is inferred by the function annotations. Here is an example:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 97.22362 cm
q
q
1 0 0 1 0 0 cm
@@ -3706,21 +3522,14 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
+n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (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) Tj T* ( SQL query) Tj T* ET
-Q
-Q
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (# example8.py) Tj T* (def main\(command: \("SQL query", 'option', 'c'\), dsn\):) Tj T* ET
Q
Q
Q
-q
-1 0 0 1 62.69291 106.4236 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
@@ -3734,14 +3543,14 @@ Q
endstream
endobj
-% 'R182': class PDFStream
-182 0 obj
+% 'R169': class PDFStream
+169 0 obj
% page stream
-<< /Length 5073 >>
+<< /Length 4202 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 691.8236 cm
+1 0 0 1 62.69291 679.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3751,24 +3560,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
+n -6 -6 468.6898 84 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
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ( 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
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 623.8236 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
+BT 1 0 0 1 0 40.82 Tm .929213 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here the argument ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (has been annotated with the tuple ) Tj /F4 10 Tf (\("SQL query", 'option', 'c'\)) Tj /F1 10 Tf (:) Tj T* 0 Tw .62683 Tw (the first string is the help string which will appear in the usage message, the second string tells ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (that) Tj T* 0 Tw .931894 Tw /F4 10 Tf (command ) Tj /F1 10 Tf (is an option and the third string that there is also a short form of the option ) Tj /F4 10 Tf (-c) Tj /F1 10 Tf (, the long form) Tj T* 0 Tw (being ) Tj /F4 10 Tf (--command=) Tj /F1 10 Tf (. The usage message is the following:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 566.6236 cm
+1 0 0 1 62.69291 494.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3778,24 +3587,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# example8_.py) Tj 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 plac; plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (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 546.6236 cm
+1 0 0 1 62.69291 474.6236 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
+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 501.4236 cm
+1 0 0 1 62.69291 393.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3805,25 +3615,45 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 72 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python3 example8_.py dsn) Tj T* (executing 'select * from table' on dsn) Tj T* ET
+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 481.4236 cm
+1 0 0 1 62.69291 337.4236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .268935 Tw 12 TL /F1 10 Tf 0 0 0 rg (The third argument in the function annotation can be omitted: in such case it will be assumed to be ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (.) Tj T* 0 Tw 2.839213 Tw (The consequence is that the usual dichotomy between long and short options \(GNU-style options\)) Tj T* 0 Tw .396235 Tw (disappears: we get ) Tj /F5 10 Tf (smart options) Tj /F1 10 Tf (, which have the single character prefix of short options and behave like) Tj T* 0 Tw (both long and short options, since they can be abbreviated. Here is an example featuring smart options:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 244.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 84 re B*
+Q
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
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# example6.py) Tj T* (def main\(dsn, command: \("SQL query", 'option'\)\):) Tj T* ( print\('executing %r on %s' % \(command, dsn\)\)) Tj T* T* (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 424.2236 cm
+1 0 0 1 62.69291 127.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -3833,24 +3663,42 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 108 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
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (usage: example6.py [-h] [-command 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* ( -command COMMAND SQL query) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 380.2236 cm
+1 0 0 1 62.69291 107.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 .16832 Tw (practicality beats purity, so ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to use smart defaults; in particular you can omit the third argument) Tj T* 0 Tw (and write:) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (The following are all valid invocations ot the script:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 323.0236 cm
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (6) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R170': class PDFStream
+170 0 obj
+% page stream
+<< /Length 4357 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 679.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3860,31 +3708,51 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 84 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'\),) Tj T* ( dsn: \("Database dsn", 'positional'\)\):) Tj T* ( ...) Tj T* ET
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ($ python3 example6.py -c "select" dsn) Tj T* (executing 'select' on dsn) Tj T* ($ python3 example6.py -com "select" dsn) Tj T* (executing 'select' on dsn) Tj T* ($ python3 example6.py -command="select" dsn) Tj T* (executing 'select' on dsn) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 279.0236 cm
+1 0 0 1 62.69291 659.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.123876 Tw 12 TL /F1 10 Tf 0 0 0 rg (When omitted, the third argument is assumed to be the first letter of the variable name for options and) Tj T* 0 Tw .334983 Tw (flags, and ) Tj /F4 10 Tf (None ) Tj /F1 10 Tf (for positional arguments. Moreover, smart enough to convert help messages into tuples;) Tj T* 0 Tw (in other words, you can just write ) Tj /F4 10 Tf ("Database dsn" ) Tj /F1 10 Tf (instead of ) Tj /F4 10 Tf (\("Database dsn", 'positional'\)) Tj /F1 10 Tf (.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that the form ) Tj /F4 10 Tf (-command=SQL ) Tj /F1 10 Tf (is recognized only for the full option, not for its abbreviations:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 261.0236 cm
+1 0 0 1 62.69291 602.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 48 re B*
+Q
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (I should notice that varargs \(starred-arguments\) can be annotated too; here is an example:) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ python3 example6.py -com="select" dsn) Tj T* (usage: example6.py [-h] [-command COMMAND] dsn) Tj T* (example6.py: error: unrecognized arguments: -com=select) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 215.8236 cm
+1 0 0 1 62.69291 570.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.724987 Tw 12 TL /F1 10 Tf 0 0 0 rg (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 (. However, it is possible to) Tj T* 0 Tw (specify a non-trivial default. Here is an example:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 477.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3894,24 +3762,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 84 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
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# example8_.py) Tj T* (def main\(dsn, command: \("SQL query", 'option'\)='select * from table'\):) Tj T* ( print\('executing %r on %s' % \(command, dsn\)\)) Tj T* T* (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 195.8236 cm
+1 0 0 1 62.69291 457.4236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (This 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
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Notice that the default value appears in the help message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 138.6236 cm
+1 0 0 1 62.69291 328.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3921,47 +3790,57 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 120 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
+BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (usage: example8_.py [-h] [-command select * from table] 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* ( -command select * from table) Tj T* ( SQL query) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 308.2236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (When you run the script and you do not pass the ) Tj /F4 10 Tf (-command ) Tj /F1 10 Tf (option, the default query will be executed:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 263.0236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (6) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python3 example8_.py dsn) Tj T* (executing 'select * from table' on dsn) Tj T* ET
+Q
+Q
+Q
Q
Q
-
-endstream
-
-endobj
-% 'R183': class PDFStream
-183 0 obj
-% page stream
-<< /Length 4742 >>
-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
+1 0 0 1 62.69291 230.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Scripts with flags) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 714.0236 cm
+1 0 0 1 62.69291 200.0236 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
+BT 1 0 0 1 0 16.82 Tm .815542 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize flags, i.e. boolean options which are ) Tj /F4 10 Tf (True ) Tj /F1 10 Tf (if they are passed to the command) Tj T* 0 Tw (line 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 589.1299 cm
+1 0 0 1 62.69291 98.27714 cm
q
q
.96447 0 0 .96447 0 0 cm
@@ -3971,18 +3850,56 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 486 120 re B*
+n -6 -6 486 96 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (# example9.py) Tj T* T* (def main\(verbose: \('prints more info', 'flag', 'v'\), dsn: 'connection string'\):) Tj T* ( if verbose:) Tj T* ( print\('connecting to %s' % dsn\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL (# example9.py) Tj T* T* (def main\(verbose: \('prints more info', 'flag', 'v'\), dsn: 'connection string'\):) Tj T* ( if verbose:) Tj T* ( print\('connecting to %s' % dsn\)) Tj T* ( # ...) Tj T* T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 471.9299 cm
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (7) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R171': class PDFStream
+171 0 obj
+% page stream
+<< /Length 4910 >>
+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 (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 610.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4003,7 +3920,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 426.7299 cm
+1 0 0 1 62.69291 565.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4024,31 +3941,31 @@ Q
Q
Q
q
-1 0 0 1 62.69291 382.7299 cm
+1 0 0 1 62.69291 521.4236 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 316.7299 cm
+1 0 0 1 62.69291 455.4236 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 as it is required by the Python syntax.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 283.7299 cm
+1 0 0 1 62.69291 422.4236 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 217.7299 cm
+1 0 0 1 62.69291 356.4236 cm
q
BT 1 0 0 1 0 52.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 .304724 Tw (before we think to migrate to Python 3. I am pretty much sure most Pythonistas are in the same situation.) Tj T* 0 Tw 1.459984 Tw (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 Python) Tj T* 0 Tw .226098 Tw (2.3\). There is no magic involved; you just need to add the annotations by hand. For instance the annotate) Tj T* 0 Tw (function declaration) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 172.5299 cm
+1 0 0 1 62.69291 311.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4069,14 +3986,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 152.5299 cm
+1 0 0 1 62.69291 291.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (is equivalent to the following code:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 95.32986 cm
+1 0 0 1 62.69291 210.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -4086,35 +4003,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 72 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* (main.__annotations__ = dict\() Tj T* ET
+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 56.69291 56.69291 cm
+1 0 0 1 62.69291 166.0236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (7) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 28.82 Tm .536098 Tw 12 TL /F1 10 Tf 0 0 0 rg (One should be careful to match the keys of the annotation 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
-
-endstream
-
-endobj
-% 'R184': class PDFStream
-184 0 obj
-% page stream
-<< /Length 5748 >>
-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
+1 0 0 1 62.69291 96.82362 cm
q
q
1 0 0 1 0 0 cm
@@ -4124,24 +4030,35 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 60 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* ET
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (@plac.annotations\() Tj T* ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 683.8236 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 28.82 Tm .536098 Tw 12 TL /F1 10 Tf 0 0 0 rg (One should be careful to match the keys of the annotation 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
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R172': class PDFStream
+172 0 obj
+% page stream
+<< /Length 6179 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 602.6236 cm
+1 0 0 1 62.69291 739.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4151,39 +4068,39 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
+n -6 -6 468.6898 24 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL (@plac.annotations\() Tj T* ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* ET
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ( ...) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 570.6236 cm
+1 0 0 1 62.69291 707.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.422164 Tw 12 TL /F1 10 Tf 0 0 0 rg (In the rest of this article I will assume that you are using Python 2.X with ) Tj /F4 10 Tf (X >) Tj (= 4 ) Tj /F1 10 Tf (and I will use the) Tj T* 0 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 (runs even on Python 2.3.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.846077 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 X >) Tj (= 4 and I will use the) Tj T* 0 Tw /F4 10 Tf (plac.annotations ) Tj /F1 10 Tf (decorator. Notice however that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (runs even on Python 2.3.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 537.6236 cm
+1 0 0 1 62.69291 674.8236 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 483.6236 cm
+1 0 0 1 62.69291 620.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm .486179 Tw 12 TL /F1 10 Tf 0 0 0 rg (Even if 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 T* 0 Tw 1.356303 Tw /F5 10 Tf (hours ) Tj /F1 10 Tf (of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (, it 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 T* 0 Tw 1.71686 Tw 0 0 .501961 rg (argparse ) Tj 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) Tj T* 0 Tw (annotation is a 5-tuple of the form) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm 1.483488 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the goals of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is to have a learning curve of ) Tj /F5 10 Tf (minutes ) Tj /F1 10 Tf (for its core features, compared to the) Tj T* 0 Tw 1.152093 Tw (learning curve of ) Tj /F5 10 Tf (hours ) Tj /F1 10 Tf (of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. In order to reach this goal, I have ) Tj /F5 10 Tf (not ) Tj /F1 10 Tf (sacrificed all the features of) Tj T* 0 Tw 2.89936 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually a lot of ) Tj 0 0 .501961 rg (argparse ) Tj 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) Tj T* 0 Tw (annotations, but in general an annotation is a 6-tuple of the form) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 477.6236 cm
+1 0 0 1 62.69291 614.8236 cm
Q
q
-1 0 0 1 62.69291 465.6236 cm
+1 0 0 1 62.69291 602.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -4198,40 +4115,40 @@ q
Q
Q
q
-1 0 0 1 62.69291 465.6236 cm
+1 0 0 1 62.69291 602.8236 cm
Q
q
-1 0 0 1 62.69291 423.6236 cm
+1 0 0 1 62.69291 560.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.068735 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 a string in the set { ) Tj /F4 10 Tf ("flag") Tj /F1 10 Tf (, ) Tj /F4 10 Tf ("option") Tj /F1 10 Tf (, ) Tj /F4 10 Tf ("positional") Tj /F1 10 Tf (},) Tj T* 0 Tw 1.711163 Tw /F4 10 Tf (abbrev ) Tj /F1 10 Tf (is a one-character string, ) Tj /F4 10 Tf (type ) Tj /F1 10 Tf (is a callable taking a string in input, ) Tj /F4 10 Tf (choices ) Tj /F1 10 Tf (is a discrete) Tj T* 0 Tw (sequence of values and ) Tj /F4 10 Tf (metavar ) Tj /F1 10 Tf (is a string.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 393.6236 cm
+1 0 0 1 62.69291 530.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.05061 Tw 12 TL /F4 10 Tf 0 0 0 rg (type ) Tj /F1 10 Tf (is used to automagically convert the command line arguments from the string type to any Python) Tj T* 0 Tw (type; by default there is no convertion and ) Tj /F4 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.05061 Tw 12 TL /F4 10 Tf 0 0 0 rg (type ) Tj /F1 10 Tf (is used to automagically convert the command line arguments from the string type to any Python) Tj T* 0 Tw (type; by default there is no conversion and ) Tj /F4 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 363.6236 cm
+1 0 0 1 62.69291 500.8236 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 321.6236 cm
+1 0 0 1 62.69291 458.8236 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 1.056654 Tw (metavar is ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (: this means that the name in the usage message is the same as the argument name,) Tj T* 0 Tw (unless the argument has a default and in such a case is equal to the stringified form of the default.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 303.6236 cm
+1 0 0 1 62.69291 440.8236 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 \(taken 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 95.24669 cm
+1 0 0 1 62.69291 232.4467 cm
q
q
.976496 0 0 .976496 0 0 cm
@@ -4245,98 +4162,71 @@ n -6 -6 480 204 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 185.71 Tm /F4 10 Tf 12 TL (# example10.py) Tj T* (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* ( print\(result\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 185.71 Tm /F4 10 Tf 12 TL (# example10.py) Tj T* (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* ( return result) Tj T* T* (if __name__ == '__main__':) Tj T* ( print\(plac.call\(main\)[0]\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 200.4467 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 16.82 Tm 1.304987 Tw 12 TL /F1 10 Tf 0 0 0 rg (Usually the main function of a script works by side effects and returns ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (; in this example instead I) Tj T* 0 Tw (choose to return the number and to print it in the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block.) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R185': class PDFStream
-185 0 obj
-% page stream
-<< /Length 3603 >>
-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
+1 0 0 1 62.69291 170.4467 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .169431 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that ) Tj /F5 10 Tf (plac.call returns a list of strings) Tj /F1 10 Tf (: in particular, it returns a single-element list if the main function) Tj T* 0 Tw (returns a single non-None element \(as in this example\) or an empty list if the main function returns ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 611.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 132 re B*
-Q
+1 0 0 1 62.69291 152.4467 cm
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 579.8236 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
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 486.4849 cm
+1 0 0 1 62.69291 95.24669 cm
q
q
-.87797 0 0 .87797 0 0 cm
+1 0 0 1 0 0 cm
q
-1 0 0 1 6.6 7.517338 cm
+1 0 0 1 6.6 6.6 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 534 96 re B*
+n -6 -6 468.6898 48 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
+BT 1 0 0 1 0 29.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* ET
Q
Q
Q
Q
-q
-1 0 0 1 62.69291 453.4849 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Keyword arguments) Tj T* ET
-Q
Q
q
-1 0 0 1 62.69291 423.4849 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 16.82 Tm .238651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.4, if your main function has keyword arguments, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (recognizes arguments of the) Tj T* 0 Tw (form ) Tj /F4 10 Tf ("name=value" ) Tj /F1 10 Tf (in the command line. Here is an example:) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (9) Tj T* -238.1649 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R173': class PDFStream
+173 0 obj
+% page stream
+<< /Length 4996 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 258.2849 cm
+1 0 0 1 62.69291 667.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4346,70 +4236,51 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+n -6 -6 468.6898 96 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (# example12.py) Tj T* (import plac) Tj T* T* (@plac.annotations\() Tj T* ( opt=\('some option', 'option'\),) Tj T* ( args='default arguments',) Tj T* ( kw='keyword arguments'\)) Tj T* (def main\(opt, *args, **kw\):) Tj T* ( print\(opt, args, kw\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL 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 238.2849 cm
+1 0 0 1 62.69291 635.8236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the generated usage message:) Tj T* ET
+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 109.0849 cm
+1 0 0 1 62.69291 542.4849 cm
q
q
-1 0 0 1 0 0 cm
+.87797 0 0 .87797 0 0 cm
q
-1 0 0 1 6.6 6.6 cm
+1 0 0 1 6.6 7.517338 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
+n -6 -6 534 96 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]) Tj T* T* (positional arguments:) Tj T* ( args default arguments) Tj T* ( kw keyword arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -o, --opt OPT some option) Tj T* ET
-Q
+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
-1 0 0 1 62.69291 89.0849 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is how you call the script:) Tj T* ET
-Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 486.4849 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (9) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 40.82 Tm 1.535984 Tw 12 TL /F1 10 Tf 0 0 0 rg (If the main function returns a generic number of elements, the elements returned by ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (are) Tj T* 0 Tw .113735 Tw (stringified by invoking ) Tj /F4 10 Tf (str ) Tj /F1 10 Tf (on each of them. The reason is to simplify testing: a plac-based command-line) Tj T* 0 Tw 1.773876 Tw (interface can be tested by simply comparing lists of strings in input and lists of strings in output. For) Tj T* 0 Tw (instance a doctest may look like this:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R186': class PDFStream
-186 0 obj
-% page stream
-<< /Length 4139 >>
-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
+1 0 0 1 62.69291 429.2849 cm
q
q
1 0 0 1 0 0 cm
@@ -4419,24 +4290,23 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 48 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python example12.py 1 2 kw1=1 kw2=2 --opt=0) Tj T* (\('0', \('1', '2'\), {'kw1': '1', 'kw2': '2'}\)) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import example10) Tj T* (>) Tj (>) Tj (>) Tj ( plac.call\(example10.main, ['add', '1', '2']\)) Tj T* (['3.0']) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
+1 0 0 1 62.69291 409.2849 cm
q
-BT 1 0 0 1 0 16.82 Tm 2.133735 Tw 12 TL /F1 10 Tf 0 0 0 rg (When using keyword arguments, one must be careful to use names which are not alreay taken; for) Tj T* 0 Tw (instance in this examples the name ) Tj /F4 10 Tf (opt ) Tj /F1 10 Tf (is taken:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (plac.call ) Tj /F1 10 Tf (works for generators too:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 638.6236 cm
+1 0 0 1 62.69291 328.0849 cm
q
q
1 0 0 1 0 0 cm
@@ -4446,65 +4316,41 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 72 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ python example12.py 1 2 kw1=1 kw2=2 opt=0) Tj T* (usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]) Tj T* (example12.py: error: colliding keyword arguments: opt) Tj T* ET
+BT 1 0 0 1 0 53.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( def main\(n\):) Tj T* (... for i in range\(int\(n\)\):) Tj T* (... yield i) Tj T* (>) Tj (>) Tj (>) Tj ( plac.call\(main, ['3']\)) Tj T* (['0', '1', '2']) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 594.6236 cm
+1 0 0 1 62.69291 296.0849 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 28.82 Tm /F1 10 Tf 12 TL 1.024104 Tw (The names taken are the names of the flags, of the options, and of the positional arguments, excepted) Tj T* 0 Tw .60561 Tw (varargs and keywords. This limitation is a consequence of the way the argument names are managed in) Tj T* 0 Tw (function calls by the Python language.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .91811 Tw 12 TL /F1 10 Tf 0 0 0 rg (However, you should notice that ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (is ) Tj /F5 10 Tf (eager) Tj /F1 10 Tf (, not lazy: the generator is exhausted by the call.) Tj T* 0 Tw (This behavior avoids mistakes like forgetting of applying ) Tj /F4 10 Tf (list\(result\) ) Tj /F1 10 Tf (to the result of a ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 561.6236 cm
+1 0 0 1 62.69291 266.0849 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A realistic example) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .414983 Tw 12 TL /F1 10 Tf 0 0 0 rg (This behavior makes testing easier and supports the ) Tj /F5 10 Tf (yield-is-print ) Tj /F1 10 Tf (pattern: just replace the occurrences of) Tj T* 0 Tw /F4 10 Tf (print ) Tj /F1 10 Tf (with ) Tj /F4 10 Tf (yield ) Tj /F1 10 Tf (in the main function and you will get an easy to test interface.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 519.6236 cm
-q
-BT 1 0 0 1 0 28.82 Tm 1.234488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a more realistic script using most of the features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to run SQL queries on a database by) Tj T* 0 Tw .930697 Tw (relying on ) Tj 0 0 .501961 rg (SQLAlchemy) Tj 0 0 0 rg (. Notice the usage of the ) Tj /F4 10 Tf (type ) Tj /F1 10 Tf (feature to automagically convert a SQLAlchemy) Tj T* 0 Tw (connection string into a ) Tj 0 0 .501961 rg (SqlSoup ) Tj 0 0 0 rg (object:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 186.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
+1 0 0 1 62.69291 233.0849 cm
q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 324 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 305.71 Tm /F4 10 Tf 12 TL (# dbcli.py) Tj T* (import plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (@plac.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.bind.execute\(file\(script\).read\(\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A realistic example) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 166.4236 cm
+1 0 0 1 62.69291 191.0849 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
+BT 1 0 0 1 0 28.82 Tm 1.234488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a more realistic script using most of the features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to run SQL queries on a database by) Tj T* 0 Tw .930697 Tw (relying on ) Tj 0 0 .501961 rg (SQLAlchemy) Tj 0 0 0 rg (. Notice the usage of the ) Tj /F4 10 Tf (type ) Tj /F1 10 Tf (feature to automagically convert a SQLAlchemy) Tj T* 0 Tw (connection string into a ) Tj 0 0 .501961 rg (SqlSoup ) Tj 0 0 0 rg (object:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 97.22362 cm
+1 0 0 1 62.69291 97.8849 cm
q
q
1 0 0 1 0 0 cm
@@ -4514,11 +4360,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 468.6898 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (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* ET
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# dbcli.py) Tj T* (import plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (@plac.annotations\() Tj T* ( db=\("Connection string", 'positional', None, SqlSoup\),) Tj T* ET
Q
Q
Q
@@ -4535,14 +4381,14 @@ Q
endstream
endobj
-% 'R187': class PDFStream
-187 0 obj
+% 'R174': class PDFStream
+174 0 obj
% page stream
-<< /Length 5945 >>
+<< /Length 3892 >>
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
+1 0 0 1 62.69291 475.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4552,69 +4398,31 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
+n -6 -6 468.6898 288 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (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, --sqlcmd SQL SQL command) Tj T* ( -d, --delimiter | Column separator) Tj T* ET
+BT 1 0 0 1 0 269.71 Tm /F4 10 Tf 12 TL ( 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* ( yield 'Working on %s' % db.bind.url) Tj T* T* ( if sqlcmd:) Tj T* ( result = db.bind.execute\(sqlcmd\)) Tj T* ( if header: # print the header) Tj T* ( yield delimiter.join\(result.keys\(\)\)) Tj T* ( for row in result: # print the rows) Tj T* ( yield delimiter.join\(map\(str, row\)\)) Tj T* T* ( for script in scripts:) Tj T* ( db.bind.execute\(file\(script\).read\(\)\)) Tj T* ( yield 'executed %s' % script) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 610.8236 cm
+1 0 0 1 62.69291 407.8236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Advanced usage) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .609398 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can see the ) Tj /F5 10 Tf (yield-is-print ) Tj /F1 10 Tf (pattern here: instead of using ) Tj /F4 10 Tf (print ) Tj /F1 10 Tf (in the main function, we use ) Tj /F4 10 Tf (yield) Tj /F1 10 Tf (,) Tj T* 0 Tw .654431 Tw (and we perform the print in the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block. The advantage of the pattern is that the test becomes) Tj T* 0 Tw 2.757984 Tw (trivial: had we performed the printing in the main function, tje test would have involved redirecting) Tj T* 0 Tw 3.359213 Tw /F4 10 Tf (sys.stdout ) Tj /F1 10 Tf (to a ) Tj /F4 10 Tf (StringIO ) Tj /F1 10 Tf (object and we know that redirecting ) Tj /F4 10 Tf (sys.stdout ) Tj /F1 10 Tf (is always ugly,) Tj T* 0 Tw (especially in multithreaded situations.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 580.8236 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 538.8236 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 1.435976 Tw /F4 10 Tf ('==SUPPRESS==' ) Tj /F1 10 Tf (as help string \(or ) Tj /F4 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use ) Tj 0 0 .501961 rg (argparse.FileType) Tj T* 0 Tw 0 0 0 rg (directly.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 484.8236 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 415.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 60 re B*
-Q
+1 0 0 1 62.69291 389.8236 cm
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 335.6236 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 .275703 Tw (typical user of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (will be happy with the defaults and would not want to change them; still it is possible if) Tj T* 0 Tw .365542 Tw (she wants to. For instance, by setting the ) Tj /F4 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add a comment to the) Tj T* 0 Tw .602339 Tw (usage message \(by default the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function is used as description\). It is also possible) Tj T* 0 Tw .322988 Tw (to change the option prefix; for instance if your script must run under Windows and you want to use "/" as) Tj T* 0 Tw (option prefix you can add the lines:) Tj T* ET
+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 290.4236 cm
+1 0 0 1 62.69291 212.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4624,46 +4432,33 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 168 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
+BT 1 0 0 1 0 149.71 Tm /F4 10 Tf 12 TL (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 222.4236 cm
+1 0 0 1 62.69291 192.6236 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 1.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
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (I leave as an exercise for the reader to write doctests for this example.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 192.4236 cm
+1 0 0 1 62.69291 159.6236 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
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Keyword arguments) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 99.22362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
-Q
+1 0 0 1 62.69291 117.6236 cm
q
-BT 1 0 0 1 0 65.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* ET
-Q
-Q
-Q
+BT 1 0 0 1 0 28.82 Tm 1.831984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.4, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (supports keyword arguments. In practice that means that if your main) Tj T* 0 Tw 2.099213 Tw (function has keyword arguments, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (treats specially arguments of the form ) Tj /F4 10 Tf ("name=value" ) Tj /F1 10 Tf (in the) Tj T* 0 Tw (command line. Here is an example:) Tj T* ET
Q
Q
q
@@ -4677,14 +4472,14 @@ Q
endstream
endobj
-% 'R188': class PDFStream
-188 0 obj
+% 'R175': class PDFStream
+175 0 obj
% page stream
-<< /Length 4227 >>
+<< /Length 3855 >>
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
+1 0 0 1 62.69291 535.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4694,42 +4489,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-BT 1 0 0 1 0 17.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (formatter_class=) Tj (<) Tj (class 'argparse.HelpFormatter') Tj (>) Tj (, conflict_handler='error',) Tj T* (add_help=True\)) Tj T* ET
-Q
-Q
-Q
-Q
+n -6 -6 468.6898 228 re B*
Q
q
-1 0 0 1 62.69291 707.8236 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
+0 0 0 rg
+BT 1 0 0 1 0 209.71 Tm /F4 10 Tf 12 TL (# example12.py) Tj T* (import plac) Tj T* T* (@plac.annotations\() Tj T* ( opt=\('some option', 'option'\),) Tj T* ( args='default arguments',) Tj T* ( kw='keyword arguments'\)) Tj T* (def main\(opt, *args, **kw\):) Tj T* ( if opt:) Tj T* ( yield 'opt=%s' % opt) Tj T* ( if args:) Tj T* ( yield 'args=%s' % str\(args\)) Tj T* ( if kw:) Tj T* ( yield 'kw=%s' % kw) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
Q
-q
-1 0 0 1 62.69291 674.8236 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 644.8236 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 614.8236 cm
+1 0 0 1 62.69291 515.8236 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
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the generated usage message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 485.6236 cm
+1 0 0 1 62.69291 386.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4743,21 +4521,21 @@ 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 (# annotations.py) Tj T* (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
+BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL (usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]) Tj T* T* (positional arguments:) Tj T* ( args default arguments) Tj T* ( kw keyword arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -opt OPT some option) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 465.6236 cm
+1 0 0 1 62.69291 366.6236 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
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is how you call the script:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 288.4236 cm
+1 0 0 1 62.69291 297.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4767,25 +4545,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 168 re B*
+n -6 -6 468.6898 60 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 149.71 Tm /F4 10 Tf 12 TL (# example11.py) Tj T* (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
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ($ python example12.py -o X a1 a2 name=value) Tj T* (opt=X) Tj T* (args=\('a1', 'a2'\)) Tj T* (kw={'name': 'value'}) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 268.4236 cm
+1 0 0 1 62.69291 265.4236 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
+BT 1 0 0 1 0 16.82 Tm 2.133735 Tw 12 TL /F1 10 Tf 0 0 0 rg (When using keyword arguments, one must be careful to use names which are not alreay taken; for) Tj T* 0 Tw (instance in this examples the name ) Tj /F4 10 Tf (opt ) Tj /F1 10 Tf (is taken:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 139.2236 cm
+1 0 0 1 62.69291 208.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4795,85 +4572,60 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
+n -6 -6 468.6898 48 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
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ python example12.py 1 2 kw1=1 kw2=2 opt=0) Tj T* (usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]) Tj T* (example12.py: error: colliding keyword arguments: opt) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 95.22362 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 56.69291 56.69291 cm
+1 0 0 1 62.69291 164.2236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 28.82 Tm /F1 10 Tf 12 TL 1.024104 Tw (The names taken are the names of the flags, of the options, and of the positional arguments, excepted) Tj T* 0 Tw .60561 Tw (varargs and keywords. This limitation is a consequence of the way the argument names are managed in) Tj T* 0 Tw (function calls by the Python language.) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R189': class PDFStream
-189 0 obj
-% page stream
-<< /Length 9833 >>
-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
+1 0 0 1 62.69291 131.2236 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 702.0236 cm
+1 0 0 1 62.69291 89.22362 cm
q
BT 1 0 0 1 0 28.82 Tm 1.065988 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 an) Tj T* 0 Tw .177126 Tw (easy way. In particular you should be aware of the following limitations/differences \(the following assumes) Tj T* 0 Tw (knowledge of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 696.0236 cm
-Q
-q
-1 0 0 1 62.69291 696.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 39 cm
+1 0 0 1 56.69291 56.69291 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
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R176': class PDFStream
+176 0 obj
+% page stream
+<< /Length 8408 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 642.0236 cm
+1 0 0 1 62.69291 759.0236 cm
Q
q
-1 0 0 1 62.69291 642.0236 cm
+1 0 0 1 62.69291 759.0236 cm
Q
q
-1 0 0 1 62.69291 564.0236 cm
+1 0 0 1 62.69291 681.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4893,13 +4645,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 564.0236 cm
+1 0 0 1 62.69291 681.0236 cm
Q
q
-1 0 0 1 62.69291 564.0236 cm
+1 0 0 1 62.69291 681.0236 cm
Q
q
-1 0 0 1 62.69291 522.0236 cm
+1 0 0 1 62.69291 639.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4919,13 +4671,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 522.0236 cm
+1 0 0 1 62.69291 639.0236 cm
Q
q
-1 0 0 1 62.69291 522.0236 cm
+1 0 0 1 62.69291 639.0236 cm
Q
q
-1 0 0 1 62.69291 456.0236 cm
+1 0 0 1 62.69291 573.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4945,13 +4697,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 456.0236 cm
+1 0 0 1 62.69291 573.0236 cm
Q
q
-1 0 0 1 62.69291 456.0236 cm
+1 0 0 1 62.69291 573.0236 cm
Q
q
-1 0 0 1 62.69291 414.0236 cm
+1 0 0 1 62.69291 531.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4971,13 +4723,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 414.0236 cm
+1 0 0 1 62.69291 531.0236 cm
Q
q
-1 0 0 1 62.69291 414.0236 cm
+1 0 0 1 62.69291 531.0236 cm
Q
q
-1 0 0 1 62.69291 384.0236 cm
+1 0 0 1 62.69291 501.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4990,20 +4742,20 @@ 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
+BT 1 0 0 1 0 16.82 Tm 2.111318 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does support subparsers, but you must read the ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (to see how it) Tj T* 0 Tw (works.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 384.0236 cm
+1 0 0 1 62.69291 501.0236 cm
Q
q
-1 0 0 1 62.69291 384.0236 cm
+1 0 0 1 62.69291 501.0236 cm
Q
q
-1 0 0 1 62.69291 342.0236 cm
+1 0 0 1 62.69291 459.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5016,44 +4768,44 @@ 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
+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 .406651 Tw 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Notice however that the ability to define your own annotation objects \(again, see the ) Tj 0 0 .501961 rg (advanced) Tj T* 0 Tw (usage document) Tj 0 0 0 rg (\) may mitigate the need for custom actions.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 342.0236 cm
+1 0 0 1 62.69291 459.0236 cm
Q
q
-1 0 0 1 62.69291 342.0236 cm
+1 0 0 1 62.69291 459.0236 cm
Q
q
-1 0 0 1 62.69291 288.0236 cm
+1 0 0 1 62.69291 405.0236 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
+BT 1 0 0 1 0 40.82 Tm 3.779431 Tw 12 TL /F1 10 Tf 0 0 0 rg (I should stress 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 255.0236 cm
+1 0 0 1 62.69291 372.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac vs the rest of the world) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 213.0236 cm
+1 0 0 1 62.69291 330.0236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.866905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Originally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (boasted about being "the easiest command-line arguments parser in the world". Since) Tj T* 0 Tw .306457 Tw (then, people started pointing out to me various projects which are based on the same idea \(extracting the) Tj T* 0 Tw (parser from the main function signature\) and are arguably even easier than ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 207.0236 cm
+1 0 0 1 62.69291 324.0236 cm
Q
q
-1 0 0 1 62.69291 207.0236 cm
+1 0 0 1 62.69291 324.0236 cm
Q
q
-1 0 0 1 62.69291 189.0236 cm
+1 0 0 1 62.69291 306.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5073,13 +4825,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 189.0236 cm
+1 0 0 1 62.69291 306.0236 cm
Q
q
-1 0 0 1 62.69291 189.0236 cm
+1 0 0 1 62.69291 306.0236 cm
Q
q
-1 0 0 1 62.69291 171.0236 cm
+1 0 0 1 62.69291 288.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5099,19 +4851,19 @@ q
Q
Q
q
-1 0 0 1 62.69291 171.0236 cm
+1 0 0 1 62.69291 288.0236 cm
Q
q
-1 0 0 1 62.69291 171.0236 cm
+1 0 0 1 62.69291 288.0236 cm
Q
q
-1 0 0 1 62.69291 141.0236 cm
+1 0 0 1 62.69291 258.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.136457 Tw 12 TL /F1 10 Tf 0 0 0 rg (Luckily for me none of such projects had the idea of using function annotations and ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (; as a) Tj T* 0 Tw (consequence, they are no match for the capabilities of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 99.02362 cm
+1 0 0 1 62.69291 216.0236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.551163 Tw 12 TL /F1 10 Tf 0 0 0 rg (Of course, there are tons of other libraries to parse the command line. For instance ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (by Matthew) Tj T* 0 Tw 1.211567 Tw (Frazier which appeared on PyPI just the day before ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (; ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (is fine but it is certainly not easier than) Tj T* 0 Tw 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
Q
@@ -5127,10 +4879,10 @@ Q
endstream
endobj
-% 'R190': class PDFStream
-190 0 obj
+% 'R177': class PDFStream
+177 0 obj
% page stream
-<< /Length 4763 >>
+<< /Length 5266 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -5140,31 +4892,31 @@ 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 666.0236 cm
+1 0 0 1 62.69291 642.0236 cm
q
-BT 1 0 0 1 0 64.82 Tm .166098 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently plac is around 140 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 1.092339 Tw (experimental: after all I wrote the first version of it in three days, including the tests, the documentation) Tj T* 0 Tw (and the time to learn ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (.) Tj T* ET
+BT 1 0 0 1 0 88.82 Tm .166098 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is around 170 lines of code, not counting blanks, comments and docstrings. I do not plan to) Tj T* 0 Tw .388626 Tw (extend the core much in the future. The idea is to keep the module short: it is and it should remain a little) Tj T* 0 Tw 2.348651 Tw (wrapper over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually I have thought about contributing the core back to ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac) Tj T* 0 Tw .22311 Tw 0 0 0 rg (becomes successfull and gains a reasonable number of users. For the moment it should be considered in) Tj T* 0 Tw .127882 Tw (alpha status: after all I wrote the first version of it in three days, including the tests, the documentation and) Tj T* 0 Tw .476098 Tw (the time to learn ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. At the moment I have already written some powerful extensions for ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and I) Tj T* 0 Tw .600574 Tw (plan to release them in the 0.5 release, once I finish writing ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (. The extensions) Tj T* 0 Tw (will be released together with ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (but in a separated module.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 633.0236 cm
+1 0 0 1 62.69291 609.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 567.0236 cm
+1 0 0 1 62.69291 543.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 .244198 Tw (out the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module, since I knew 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 561.0236 cm
+1 0 0 1 62.69291 537.0236 cm
Q
q
-1 0 0 1 62.69291 561.0236 cm
+1 0 0 1 62.69291 537.0236 cm
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 507.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5184,13 +4936,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 507.0236 cm
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 507.0236 cm
Q
q
-1 0 0 1 62.69291 501.0236 cm
+1 0 0 1 62.69291 477.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5211,35 +4963,34 @@ q
Q
Q
q
-1 0 0 1 62.69291 501.0236 cm
+1 0 0 1 62.69291 477.0236 cm
Q
q
-1 0 0 1 62.69291 501.0236 cm
+1 0 0 1 62.69291 477.0236 cm
Q
q
-1 0 0 1 62.69291 447.0236 cm
+1 0 0 1 62.69291 423.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 2.085984 Tw (now 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) Tj T* 0 Tw (completely 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 417.0236 cm
+1 0 0 1 62.69291 393.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 (expletives deleted) Tj (>) Tj (!) Tj T* ET
+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 ) Tj /F5 10 Tf (clap ) Tj /F1 10 Tf (\(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 (expletives deleted) Tj (>) Tj (!) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 387.0236 cm
+1 0 0 1 62.69291 363.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .877209 Tw (Having little imagination, I decided to rename everything again to plac, an anagram of clap: since it is a) Tj T* 0 Tw (non-existing English name, I hope nobody will steal it from me!) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 369.0236 cm
+1 0 0 1 62.69291 345.0236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (That's all, I hope you will enjoy working with plac!) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (That's all, I hope you will enjoy working with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (!) Tj T* ET
Q
Q
q
@@ -5253,124 +5004,124 @@ Q
endstream
endobj
-% 'R191': class PDFPageLabels
-191 0 obj
+% 'R178': class PDFPageLabels
+178 0 obj
% Document Root
<< /Nums [ 0
- 192 0 R
+ 179 0 R
1
- 193 0 R
+ 180 0 R
2
- 194 0 R
+ 181 0 R
3
- 195 0 R
+ 182 0 R
4
- 196 0 R
+ 183 0 R
5
- 197 0 R
+ 184 0 R
6
- 198 0 R
+ 185 0 R
7
- 199 0 R
+ 186 0 R
8
- 200 0 R
+ 187 0 R
9
- 201 0 R
+ 188 0 R
10
- 202 0 R
+ 189 0 R
11
- 203 0 R
+ 190 0 R
12
- 204 0 R
+ 191 0 R
13
- 205 0 R ] >>
+ 192 0 R ] >>
endobj
-% 'R192': class PDFPageLabel
-192 0 obj
+% 'R179': class PDFPageLabel
+179 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R193': class PDFPageLabel
-193 0 obj
+% 'R180': class PDFPageLabel
+180 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R194': class PDFPageLabel
-194 0 obj
+% 'R181': class PDFPageLabel
+181 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R195': class PDFPageLabel
-195 0 obj
+% 'R182': class PDFPageLabel
+182 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R196': class PDFPageLabel
-196 0 obj
+% 'R183': class PDFPageLabel
+183 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R197': class PDFPageLabel
-197 0 obj
+% 'R184': class PDFPageLabel
+184 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R198': class PDFPageLabel
-198 0 obj
+% 'R185': class PDFPageLabel
+185 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R199': class PDFPageLabel
-199 0 obj
+% 'R186': class PDFPageLabel
+186 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R200': class PDFPageLabel
-200 0 obj
+% 'R187': class PDFPageLabel
+187 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R201': class PDFPageLabel
-201 0 obj
+% 'R188': class PDFPageLabel
+188 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R202': class PDFPageLabel
-202 0 obj
+% 'R189': class PDFPageLabel
+189 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R203': class PDFPageLabel
-203 0 obj
+% 'R190': class PDFPageLabel
+190 0 obj
% None
<< /S /D
/St 12 >>
endobj
-% 'R204': class PDFPageLabel
-204 0 obj
+% 'R191': class PDFPageLabel
+191 0 obj
% None
<< /S /D
/St 13 >>
endobj
-% 'R205': class PDFPageLabel
-205 0 obj
+% 'R192': class PDFPageLabel
+192 0 obj
% None
<< /S /D
/St 14 >>
endobj
xref
-0 206
+0 193
0000000000 65535 f
0000000113 00000 n
0000000258 00000 n
@@ -5399,192 +5150,179 @@ xref
0000005595 00000 n
0000005838 00000 n
0000006081 00000 n
-0000006325 00000 n
-0000006569 00000 n
-0000006813 00000 n
-0000007057 00000 n
-0000007301 00000 n
-0000007545 00000 n
-0000007789 00000 n
-0000008033 00000 n
-0000008277 00000 n
-0000008521 00000 n
-0000008765 00000 n
-0000009008 00000 n
-0000009270 00000 n
-0000009533 00000 n
-0000009783 00000 n
-0000010032 00000 n
-0000010298 00000 n
-0000010550 00000 n
-0000010785 00000 n
-0000011450 00000 n
-0000011702 00000 n
-0000011952 00000 n
-0000012204 00000 n
-0000012454 00000 n
-0000012706 00000 n
-0000012957 00000 n
-0000013196 00000 n
-0000013391 00000 n
-0000013652 00000 n
-0000013916 00000 n
-0000014150 00000 n
-0000014549 00000 n
-0000014801 00000 n
-0000015053 00000 n
-0000015304 00000 n
-0000015554 00000 n
-0000015791 00000 n
-0000016145 00000 n
-0000016394 00000 n
-0000016646 00000 n
-0000016898 00000 n
-0000017135 00000 n
-0000017480 00000 n
-0000017732 00000 n
-0000018020 00000 n
-0000018308 00000 n
-0000018560 00000 n
-0000018848 00000 n
-0000019085 00000 n
-0000019448 00000 n
-0000019700 00000 n
-0000019937 00000 n
-0000020264 00000 n
-0000020516 00000 n
-0000020753 00000 n
-0000021080 00000 n
-0000021332 00000 n
-0000021582 00000 n
-0000021832 00000 n
-0000022082 00000 n
-0000022334 00000 n
-0000022569 00000 n
-0000022932 00000 n
-0000023169 00000 n
-0000023487 00000 n
-0000023739 00000 n
-0000023985 00000 n
-0000024248 00000 n
-0000024584 00000 n
-0000024835 00000 n
-0000025085 00000 n
-0000025335 00000 n
-0000025651 00000 n
-0000025902 00000 n
-0000026155 00000 n
-0000026405 00000 n
-0000026656 00000 n
-0000026909 00000 n
-0000027183 00000 n
-0000027589 00000 n
-0000027828 00000 n
-0000028148 00000 n
-0000028400 00000 n
-0000028651 00000 n
-0000028902 00000 n
-0000029155 00000 n
-0000029420 00000 n
-0000029671 00000 n
-0000029936 00000 n
-0000030187 00000 n
-0000030441 00000 n
-0000030693 00000 n
-0000030947 00000 n
-0000031199 00000 n
-0000031453 00000 n
-0000031705 00000 n
-0000031958 00000 n
-0000032212 00000 n
-0000032466 00000 n
-0000032720 00000 n
-0000032974 00000 n
-0000033226 00000 n
-0000033479 00000 n
-0000033768 00000 n
-0000034020 00000 n
-0000034274 00000 n
-0000034528 00000 n
-0000034787 00000 n
-0000035044 00000 n
-0000035296 00000 n
-0000035550 00000 n
-0000035808 00000 n
-0000036060 00000 n
-0000036318 00000 n
-0000036557 00000 n
-0000037198 00000 n
-0000037450 00000 n
-0000037701 00000 n
-0000037955 00000 n
-0000038207 00000 n
-0000038461 00000 n
-0000038748 00000 n
-0000039002 00000 n
-0000039313 00000 n
-0000039565 00000 n
-0000039817 00000 n
-0000040106 00000 n
-0000040358 00000 n
-0000040610 00000 n
-0000040853 00000 n
-0000041290 00000 n
-0000041454 00000 n
-0000041728 00000 n
-0000041857 00000 n
-0000042051 00000 n
-0000042262 00000 n
-0000042472 00000 n
-0000042672 00000 n
-0000042870 00000 n
-0000043075 00000 n
-0000043268 00000 n
-0000043465 00000 n
-0000043664 00000 n
-0000043860 00000 n
-0000044067 00000 n
-0000044265 00000 n
-0000044476 00000 n
-0000044668 00000 n
-0000044851 00000 n
-0000045083 00000 n
-0000053875 00000 n
-0000059327 00000 n
-0000063363 00000 n
-0000067743 00000 n
-0000072749 00000 n
-0000077925 00000 n
-0000082770 00000 n
-0000088621 00000 n
-0000092327 00000 n
-0000096569 00000 n
-0000102617 00000 n
-0000106947 00000 n
-0000116883 00000 n
-0000121753 00000 n
-0000122035 00000 n
-0000122114 00000 n
-0000122193 00000 n
-0000122272 00000 n
-0000122351 00000 n
-0000122430 00000 n
-0000122509 00000 n
-0000122588 00000 n
-0000122667 00000 n
-0000122746 00000 n
-0000122826 00000 n
-0000122906 00000 n
-0000122986 00000 n
-0000123066 00000 n
+0000006324 00000 n
+0000006567 00000 n
+0000006811 00000 n
+0000007055 00000 n
+0000007299 00000 n
+0000007543 00000 n
+0000007787 00000 n
+0000008030 00000 n
+0000008292 00000 n
+0000008555 00000 n
+0000008805 00000 n
+0000009054 00000 n
+0000009320 00000 n
+0000009572 00000 n
+0000009822 00000 n
+0000010074 00000 n
+0000010324 00000 n
+0000010576 00000 n
+0000010811 00000 n
+0000011476 00000 n
+0000011728 00000 n
+0000011979 00000 n
+0000012218 00000 n
+0000012413 00000 n
+0000012674 00000 n
+0000012938 00000 n
+0000013172 00000 n
+0000013535 00000 n
+0000013787 00000 n
+0000014039 00000 n
+0000014290 00000 n
+0000014540 00000 n
+0000014777 00000 n
+0000015131 00000 n
+0000015380 00000 n
+0000015632 00000 n
+0000015869 00000 n
+0000016205 00000 n
+0000016457 00000 n
+0000016709 00000 n
+0000016997 00000 n
+0000017249 00000 n
+0000017537 00000 n
+0000017825 00000 n
+0000018062 00000 n
+0000018434 00000 n
+0000018671 00000 n
+0000018989 00000 n
+0000019226 00000 n
+0000019544 00000 n
+0000019781 00000 n
+0000020099 00000 n
+0000020351 00000 n
+0000020603 00000 n
+0000020853 00000 n
+0000021103 00000 n
+0000021353 00000 n
+0000021605 00000 n
+0000021840 00000 n
+0000022212 00000 n
+0000022464 00000 n
+0000022710 00000 n
+0000022973 00000 n
+0000023309 00000 n
+0000023561 00000 n
+0000023798 00000 n
+0000024125 00000 n
+0000024376 00000 n
+0000024626 00000 n
+0000024862 00000 n
+0000025198 00000 n
+0000025448 00000 n
+0000025700 00000 n
+0000025951 00000 n
+0000026204 00000 n
+0000026455 00000 n
+0000026708 00000 n
+0000026959 00000 n
+0000027211 00000 n
+0000027464 00000 n
+0000027744 00000 n
+0000027997 00000 n
+0000028250 00000 n
+0000028529 00000 n
+0000028809 00000 n
+0000029060 00000 n
+0000029313 00000 n
+0000029601 00000 n
+0000029852 00000 n
+0000030105 00000 n
+0000030359 00000 n
+0000030618 00000 n
+0000030875 00000 n
+0000031127 00000 n
+0000031381 00000 n
+0000031639 00000 n
+0000031891 00000 n
+0000032149 00000 n
+0000032388 00000 n
+0000032977 00000 n
+0000033230 00000 n
+0000033482 00000 n
+0000033733 00000 n
+0000033986 00000 n
+0000034238 00000 n
+0000034492 00000 n
+0000034773 00000 n
+0000035027 00000 n
+0000035281 00000 n
+0000035568 00000 n
+0000035822 00000 n
+0000036133 00000 n
+0000036385 00000 n
+0000036637 00000 n
+0000036926 00000 n
+0000037178 00000 n
+0000037430 00000 n
+0000037688 00000 n
+0000037927 00000 n
+0000038414 00000 n
+0000038578 00000 n
+0000038852 00000 n
+0000038981 00000 n
+0000039175 00000 n
+0000039386 00000 n
+0000039596 00000 n
+0000039818 00000 n
+0000040016 00000 n
+0000040221 00000 n
+0000040414 00000 n
+0000040613 00000 n
+0000040810 00000 n
+0000041007 00000 n
+0000041218 00000 n
+0000041410 00000 n
+0000041593 00000 n
+0000041823 00000 n
+0000050884 00000 n
+0000055817 00000 n
+0000059996 00000 n
+0000064062 00000 n
+0000069152 00000 n
+0000073457 00000 n
+0000077917 00000 n
+0000082930 00000 n
+0000089212 00000 n
+0000094311 00000 n
+0000098306 00000 n
+0000102264 00000 n
+0000110775 00000 n
+0000116148 00000 n
+0000116430 00000 n
+0000116509 00000 n
+0000116588 00000 n
+0000116667 00000 n
+0000116746 00000 n
+0000116825 00000 n
+0000116904 00000 n
+0000116983 00000 n
+0000117062 00000 n
+0000117141 00000 n
+0000117221 00000 n
+0000117301 00000 n
+0000117381 00000 n
+0000117461 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(m\265o\026\216:\\\226;\037\223\273\220>\243\351) (m\265o\026\216:\\\226;\037\223\273\220>\243\351)]
+ [(\2315\367\261S5[w\201\312b\3121\246-\033) (\2315\367\261S5[w\201\312b\3121\246-\033)]
- /Info 159 0 R
- /Root 158 0 R
- /Size 206 >>
+ /Info 148 0 R
+ /Root 147 0 R
+ /Size 193 >>
startxref
-123115
+117510
%%EOF
diff --git a/plac/doc/plac.txt b/plac/doc/plac.txt
index 9c3b1d9..88e85a2 100644
--- a/plac/doc/plac.txt
+++ b/plac/doc/plac.txt
@@ -6,7 +6,7 @@ Plac: Parsing the Command Line the Easy Way
:Requires: Python 2.3+
:Download page: http://pypi.python.org/pypi/plac
:Project page: http://micheles.googlecode.com/hg/plac/doc/plac.html
-:Installation: ``easy_install plac``
+:Installation: ``easy_install -U plac``
:License: BSD license
.. contents::
@@ -104,13 +104,9 @@ 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
+
+.. include:: example3.help
+ :literal:
This is only the tip of the iceberg: plac_ is able to do much more than that.
@@ -145,12 +141,10 @@ 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
+.. include:: example7.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 then you get the following nice usage message:
+Here is the usage message:
.. include:: example7.help
:literal:
@@ -160,8 +154,31 @@ the command line arguments parser to use from the signature of the main
function*. This is the whole idea behind plac_: if the intent is clear,
let's the machine take care of the details.
-Scripts with options
----------------------------------------
+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:: example7_.py
+ :literal:
+
+Here the arguments of the ``main`` function have been annotated with
+strings which are intented to be used in the help message:
+
+.. include:: example7_.help
+ :literal:
+
+plac_ is able to recognize much more complex annotations, as
+I will show in the next paragraphs.
+
+
+Scripts with options (and smart options)
+-----------------------------------------
It is surprising how few command line scripts with options I have
written over the years (probably less than a hundred), compared to the
@@ -172,28 +189,18 @@ 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.
-
-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:
+performed the parsing by hand. In plac_ the parser is inferred by the
+function annotations. Here is an example:
.. include:: example8.py
:literal:
-As you see, the argument ``command`` has been annotated with the tuple
+Here 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, the second string tell plac_
-that ``command`` is an option and the third string that it can be
-abbreviated with the letter ``c``. Of course, the long option format
-(``--command=``) comes from the argument name. The resulting usage
-message is the following:
+which will appear in the usage message, the second string tells plac_
+that ``command`` is an option and the third string that there is also
+a short form of the option ``-c``, the long form being ``--command=``.
+The usage message is the following:
.. include:: example8.help
:literal:
@@ -206,57 +213,57 @@ Here are two examples of usage::
$ 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:
+The third argument in the function annotation can be omitted: in such
+case it will be assumed to be ``None``. The consequence is that
+the usual dichotomy between long and short options (GNU-style options)
+disappears: we get *smart options*, which have the single character prefix
+of short options and behave like both long and short options, since
+they can be abbreviated. Here is an example featuring smart options:
-.. include:: example8_.py
+.. include:: example6.py
:literal:
-Now if you do not pass the ``command option``, the
-default query will be executed::
-
- $ python3 example8_.py dsn
- executing 'select * from table' on dsn
+.. include:: example6.help
+ :literal:
+
+The following are all valid invocations ot the script::
-Positional argument can be annotated too::
+ $ python3 example6.py -c "select" dsn
+ executing 'select' on dsn
+ $ python3 example6.py -com "select" dsn
+ executing 'select' on dsn
+ $ python3 example6.py -command="select" dsn
+ executing 'select' on dsn
- def main(command: ("SQL query", 'option', 'c'),
- dsn: ("Database dsn", 'positional', None)):
- ...
+Notice that the form ``-command=SQL`` is recognized only for the full
+option, not for its abbreviations::
-Of course explicit is better than implicit, an no special cases are
-special enough, but sometimes practicality beats purity, so plac_ is
-able to use smart defaults; in particular you can omit the third argument
-and write::
+ $ python3 example6.py -com="select" dsn
+ usage: example6.py [-h] [-command COMMAND] dsn
+ example6.py: error: unrecognized arguments: -com=select
- def main(command: ("SQL query", 'option'),
- dsn: ("Database dsn", 'positional')):
- ...
+If the option is not passed, the variable ``command``
+will get the value ``None``. However, it is possible to specify a non-trivial
+default. Here is an example:
-When omitted, the third argument is assumed to be the first letter of
-the variable name for options and flags, and ``None`` for positional
-arguments. Moreover, smart enough to convert help messages into tuples;
-in other words, you can just write ``"Database dsn"`` instead of
-``("Database dsn", 'positional')``.
+.. include:: example8_.py
+ :literal:
-I should notice that varargs (starred-arguments) can be annotated too;
-here is an example::
+Notice that the default value appears in the help message:
- def main(dsn: "Database dsn", *scripts: "SQL scripts"):
- ...
+.. include:: example8_.help
+ :literal:
-This is a valid signature for plac_, which will recognize the help strings
-for both ``dsn`` and ``scripts``::
+When you run the script and you do not pass the ``-command`` option, the
+default query will be executed::
- positional arguments:
- dsn Database dsn
- scripts SQL scripts
+ $ python3 example8_.py dsn
+ executing 'select * from table' on dsn
Scripts with flags
--------------------
-plac_ also recognizes flags, i.e. boolean options which are
+plac_ is able to recognize 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:
@@ -320,18 +327,18 @@ people with Python 2.4 available the simplest way is to use the
...
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_ runs even on Python 2.3.
+X >= 4 and I will use the ``plac.annotations`` decorator. Notice however
+that plac_ runs even on Python 2.3.
More features
--------------------------------------------------
-Even if one of the goals of plac is to have a learning curve of
-*minutes*, compared to the learning curve of *hours* of
-argparse_, it 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
+One of the goals of plac_ is to have a learning curve of *minutes* for
+its core features, compared to the learning curve of *hours* of
+argparse_. In order to reach this goal, I have *not* sacrificed 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 6-tuple of the form
``(help, kind, abbrev, type, choices, metavar)``
@@ -342,7 +349,7 @@ one-character string, ``type`` is a callable taking a string in input,
``type`` is used to automagically convert the command line arguments
from the string type to any Python type; by default there is no
-convertion and ``type=None``.
+conversion and ``type=None``.
``choices`` is used to restrict the number of the valid
options; by default there is no restriction i.e. ``choices=None``.
@@ -359,6 +366,16 @@ argparse_ documentation):
.. include:: example10.py
:literal:
+
+Usually the main function of a script works by side effects and returns
+``None``; in this example instead I choose to return the number and to
+print it in the ``__main__`` block.
+
+Notice that *plac.call returns a list of strings*: in particular, it
+returns a single-element list if the main function returns a single
+non-None element (as in this example) or an empty list if the main
+function returns ``None``.
+
Here is the usage:
.. include:: example10.help
@@ -375,38 +392,33 @@ to the usage message. Here are a couple of examples of use::
usage: example10.py [-h] {add,mul} [n [n ...]]
example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul')
-Keyword arguments
----------------------------------------
+If the main function returns a generic number of elements,
+the elements returned by ``plac.call`` are stringified by invoking
+``str`` on each of them.
+The reason is to simplify testing: a plac-based
+command-line interface can be tested by simply comparing lists of
+strings in input and lists of strings in output.
+For instance a doctest may look like this:
-Starting from release 0.4, if your main function has keyword arguments,
-plac_ recognizes arguments of the form ``"name=value"`` in the command line.
-Here is an example:
+>>> import example10
+>>> plac.call(example10.main, ['add', '1', '2'])
+['3.0']
-.. include:: example12.py
- :literal:
-
-Here is the generated usage message:
+``plac.call`` works for generators too:
-.. include:: example12.help
- :literal:
+>>> def main(n):
+... for i in range(int(n)):
+... yield i
+>>> plac.call(main, ['3'])
+['0', '1', '2']
-Here is how you call the script::
-
- $ python example12.py 1 2 kw1=1 kw2=2 --opt=0
- ('0', ('1', '2'), {'kw1': '1', 'kw2': '2'})
+However, you should notice that ``plac.call`` is *eager*, not lazy:
+the generator is exhausted by the call. This behavior avoids mistakes like
+forgetting of applying ``list(result)`` to the result of a ``plac.call``.
-When using keyword arguments, one must be careful to use names which
-are not alreay taken; for instance in this examples the name ``opt``
-is taken::
-
- $ python example12.py 1 2 kw1=1 kw2=2 opt=0
- usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
- example12.py: error: colliding keyword arguments: opt
-
-The names taken are the names of the flags, of the options, and of the
-positional arguments, excepted varargs and keywords. This limitation
-is a consequence of the way the argument names are managed in function calls
-by the Python language.
+This behavior makes testing easier and supports the *yield-is-print*
+pattern: just replace the occurrences of ``print`` with ``yield`` in
+the main function and you will get an easy to test interface.
A realistic example
---------------------------------------
@@ -419,103 +431,57 @@ string into a SqlSoup_ object:
.. include:: dbcli.py
:literal:
+You can see the *yield-is-print* pattern here: instead of using
+``print`` in the main function, we use ``yield``, and we perform the
+print in the ``__main__`` block. The advantage of the pattern is that
+the test becomes trivial: had we performed the printing in the main
+function, tje test would have involved redirecting ``sys.stdout`` to a
+``StringIO`` object and we know that redirecting ``sys.stdout`` is
+always ugly, especially in multithreaded situations.
+
Here is the usage message:
.. include:: dbcli.help
:literal:
-Advanced usage
-----------------------------------------------------
-
-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.
+I leave as an exercise for the reader to write doctests for this
+example.
-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
+Keyword arguments
+---------------------------------------
-::
+Starting from release 0.4, plac_ supports keyword arguments. In
+practice that means that if your main function has keyword arguments,
+plac_ treats specially arguments of the form ``"name=value"`` in the
+command line. Here is an example:
- 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 defaults and would not want to change them; 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
+.. include:: example12.py
:literal:
-You can use such annotations objects as follows:
+Here is the generated usage message:
-.. include:: example11.py
+.. include:: example12.help
:literal:
-Here is the usage message you get:
+Here is how you call the script::
-.. include:: example11.help
- :literal:
+ $ python example12.py -o X a1 a2 name=value
+ opt=X
+ args=('a1', 'a2')
+ kw={'name': 'value'}
+
+When using keyword arguments, one must be careful to use names which
+are not alreay taken; for instance in this examples the name ``opt``
+is taken::
+
+ $ python example12.py 1 2 kw1=1 kw2=2 opt=0
+ usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
+ example12.py: error: colliding keyword arguments: opt
-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.
+The names taken are the names of the flags, of the options, and of the
+positional arguments, excepted varargs and keywords. This limitation
+is a consequence of the way the argument names are managed in function calls
+by the Python language.
plac vs argparse
---------------------------------------------
@@ -525,12 +491,6 @@ all of the features of argparse_ in an easy way. In particular you
should be aware of the following limitations/differences (the
following assumes knowledge of argparse_):
-- 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 the destination concept: the destination
coincides with the name of the argument, always. This restriction
has some drawbacks. For instance, suppose you want to define a long
@@ -558,15 +518,15 @@ following assumes knowledge of argparse_):
cases of interest to me are covered by plac_ and did not feel the need
to increase the learning curve by adding direct support for ``nargs``.
-- plac_ does not support subparsers directly. For the moment, this
- looks like a feature too advanced for the goals of plac_.
+- plac_ does support subparsers, but you must read the `advanced usage
+ document`_ to see how it works.
- 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.
+ that the ability to define your own annotation objects (again, see
+ the `advanced usage document`_) may mitigate the need for custom actions.
-I should stress again that if you want to access all of the argparse_ features
+I should stress 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()``,
@@ -597,15 +557,19 @@ easier than plac_.
The future
-------------------------------
-Currently plac is around 140 lines of code, not counting blanks,
-comments and docstrings. I do not plan to extend it much in the
+Currently plac_ is around 170 lines of code, not counting blanks,
+comments and docstrings. I do not plan to extend the core 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
+contributing the core 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 the first version of it in
+considered in alpha status: after all I wrote the first version of it in
three days, including the tests, the documentation and the time to
-learn argparse_.
+learn argparse_. At the moment I have already written some powerful
+extensions for plac_ and I plan to release them in the 0.5 release, once
+I finish writing `advanced usage document`_ .
+The extensions will be released together with plac_ but in a separated
+module.
Trivia: the story behind the name
-----------------------------------------
@@ -629,7 +593,7 @@ annotations. The ``optionparser`` name was ruled out, since I was
now 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)
+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 <expletives deleted>!
@@ -637,7 +601,7 @@ Having little imagination, I decided to rename everything again to plac,
an anagram of clap: since it is a non-existing English name, I hope nobody
will steal it from me!
-That's all, I hope you will enjoy working with plac!
+That's all, I hope you will enjoy working with plac_!
.. _argparse: http://argparse.googlecode.com
.. _optparse: http://docs.python.org/library/optparse.html
@@ -653,3 +617,4 @@ That's all, I hope you will enjoy working with plac!
.. _SqlSoup: http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html
.. _CLIArgs: http://pypi.python.org/pypi/CLIArgs
.. _opterator: http://pypi.python.org/pypi/opterator
+.. _advanced usage document: in-writing
diff --git a/plac/doc/plac_adv.html b/plac/doc/plac_adv.html
new file mode 100644
index 0000000..26d6cdf
--- /dev/null
+++ b/plac/doc/plac_adv.html
@@ -0,0 +1,904 @@
+<?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.5: http://docutils.sourceforge.net/" />
+<title>Testing and scripting your applications with plac</title>
+<style type="text/css">
+
+.first {
+ margin-top: 0 }
+
+.last {
+ margin-bottom: 0 }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+dd {
+ margin-bottom: 0.5em }
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.attention, div.caution, div.danger, div.error, div.hint,
+div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.hint p.admonition-title, div.important p.admonition-title,
+div.note p.admonition-title, div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em }
+
+div.footer, div.header {
+ font-size: smaller }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+hr {
+ width: 75% }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font-family: serif ;
+ font-size: 100% }
+
+pre.line-block {
+ font-family: serif ;
+ font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+ background-color: #eeeeee }
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option-argument {
+ font-style: italic }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+table {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.citation {
+ border-left: solid thin gray ;
+ padding-left: 0.5ex }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.footnote {
+ border-left: solid thin black ;
+ padding-left: 0.5ex }
+
+td, th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+th.docinfo-name, th.field-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ font-size: 100% }
+
+tt {
+ background-color: #eeeeee }
+
+ul.auto-toc {
+ list-style-type: none }
+
+
+/*
+Additional styles for "modern"-style of DocFactory.
+
+:Author: Gunnar Schwant
+:Contact: g.schwant@gmx.de
+*/
+
+.first {
+ font-size: 10pt }
+
+.last {
+ font-size: 10pt }
+
+a {
+ text-decoration: none }
+
+a.reference {
+ color: #00009F }
+
+a:hover {
+ background-color: #00009F ;
+ color: white }
+
+body {
+ font-family: arial,helvetica,univers ;
+ font-size: 10pt ;
+ padding-top: 0.6cm ;
+ margin-left:0.5cm ;
+ margin-right:0.5cm ;
+ margin-bottom:0.5cm }
+
+dd {
+ font-size: 10pt ;
+ padding-top: 0.1cm
+}
+
+dt {
+ font-size: 10pt ;
+ font-weight: bold ;
+ background-color: #6FC7FB ;
+ padding-left: 0.1cm ;
+ padding-top: 0.1cm ;
+ padding-bottom: 0.1cm }
+
+div.abstract {
+ font-size: 10pt }
+
+div.abstract p.topic-title {
+ font-size: 10pt }
+
+div.attention, div.caution, div.danger, div.error, div.hint,
+div.important, div.note, div.tip, div.warning {
+ font-size: 10pt }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ margin-top: 0em ;
+ font-size: 12pt ;
+ font-family: arial,helvetica,univers }
+
+div.dedication {
+ font-size: 10pt }
+
+div.dedication p.topic-title {
+ font-size: 10pt }
+
+div.figure {
+ font-size: 10pt }
+
+div.footer, div.header {
+ font-size: 8pt }
+
+div.system-messages {
+ font-size: 10pt }
+
+div.system-messages h1 {
+ font-size: 12pt }
+
+div.system-message {
+ font-size: 10pt }
+
+div.system-message p.system-message-title {
+ font-size: 10pt }
+
+div.topic {
+ font-size: 10pt }
+
+h1, h2, h3, h4, h5, h6 {
+ padding-top: 0.5cm ;
+ page-break-after: avoid ;
+ font-family: arial,helvetica,univers }
+
+h1 {
+ font-size: 18pt }
+
+h1.title {
+ color: white ;
+ background-color: #00009F ;
+ padding-top: 0cm }
+
+h2 {
+ font-size: 16pt }
+
+h2.subtitle {
+ padding-top: 0cm }
+
+h3 {
+ font-size: 14pt }
+
+h4 {
+ font-size: 12pt }
+
+h5, h6 {
+ font-size: 10pt }
+
+hr {
+ width: 100%;
+ page-break-after: always }
+
+li {
+ padding-top: 1mm ;
+ padding-bottom: 1mm }
+
+ol.simple, ul.simple {
+ font-size: 10pt }
+
+ol.arabic {
+ font-size: 10pt }
+
+ol.loweralpha {
+ font-size: 10pt }
+
+ol.upperalpha {
+ font-size: 10pt }
+
+ol.lowerroman {
+ font-size: 10pt }
+
+ol.upperroman {
+ font-size: 10pt }
+
+p.caption {
+ font-size: 10pt }
+
+p.credits {
+ font-style: italic ;
+ font-size: 8pt }
+
+p.label {
+ font-size: 10pt }
+
+p.topic-title {
+ font-size: 10pt }
+
+pre.address {
+ font-family: arial,helvetica,univers ;
+ font-size: 10pt }
+
+pre.line-block {
+ font-size: 10pt }
+
+pre.literal-block, pre.doctest-block {
+ border-width: 1pt ;
+ border-style: solid ;
+ border-color: #999999 ;
+ color: #0000C0 ;
+ background-color: #ffffe0 ;
+ font-size: 9pt }
+
+span.classifier {
+ font-size: 10pt ;
+ font-family: arial,helvetica,univers }
+
+span.classifier-delimiter {
+ font-size: 10pt ;
+ font-family: arial,helvetica,univers }
+
+span.field-argument {
+ font-size: 10pt }
+
+span.interpreted {
+ font-size: 10pt ;
+ font-family: arial,helvetica,univers }
+
+span.option-argument {
+ font-size: 10pt }
+
+span.problematic {
+ font-size: 10pt }
+
+table {
+ font-size: 10pt ;
+ border-collapse: collapse ;
+ border-width: 1.5pt ;
+ border-color: #003366 }
+
+table.citation {
+ font-size: 10pt }
+
+table.docinfo {
+ font-size: 10pt }
+
+table.footnote {
+ font-size: 8pt ;
+ text-align: left }
+
+table.table {
+ width: 100% }
+
+th {
+ border-width: 1.5pt }
+
+td {
+ border-width: 1pt }
+
+td, th {
+ font-size: 10pt ;
+ border-style: thin ;
+ border-color: #003366 }
+
+td.docinfo-name, th.field-name {
+ font-size: 10pt }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ font-size: 10pt }
+
+</style>
+</head>
+<body>
+<div class="document" id="testing-and-scripting-your-applications-with-plac">
+<h1 class="title">Testing and scripting your applications with plac</h1>
+
+<div class="section" id="introduction">
+<h1>Introduction</h1>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> has been designed to be simple to use for simple stuff, but in
+truth it is a quite advanced tool with a field of applicability
+which far outreaches the specific domain of command-line arguments parsers.
+In reality <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is a generic tool to write domain specific
+languages (DSL).
+This document explains how you can use plac to test your application, and
+how you can use it to provide a scripting interface to your application.
+Notice that your application does not need to be a command-line
+application: you can use <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> whenever you have an API with strings
+in input and strings in output.</p>
+</div>
+<div class="section" id="testing-applications-with-plac">
+<h1>Testing applications with plac</h1>
+<p>In the standard usage, <tt class="docutils literal"><span class="pre">plac.call</span></tt> is called only once on the main
+function; however in the tests it quite natural to invoke <tt class="docutils literal"><span class="pre">plac.call</span></tt>
+multiple times on the same function with different arguments.
+For instance, suppose you want to store the configuration of
+your application into a Python shelve; then, you may want to write
+a command-line tool to edit your configuration, i.e. a shelve
+interface. A possible implementation could be the following:</p>
+<pre class="literal-block">
+import shelve
+import plac
+
+&#64;plac.annotations(
+ help=('show help', 'flag'),
+ all=('show all parameters in the shelve', 'flag'),
+ clear=('clear the shelve', 'flag'),
+ delete=('delete an element', 'option'),
+ filename=('filename of the shelve', 'option'),
+ params='names of the parameters in the shelve',
+ setters='setters param=value')
+def ishelve(help, all, clear, delete, filename='conf.shelve',
+ *params, **setters):
+ sh = shelve.open(filename)
+ try:
+ if help:
+ yield 'Special commands:'
+ yield 'help, all, clear, delete'
+ elif all:
+ for param, name in sh.items():
+ yield '%s=%s' % (param, name)
+ elif clear:
+ sh.clear()
+ yield 'cleared the shelve'
+ elif delete:
+ try:
+ del sh[delete]
+ except KeyError:
+ yield '%s: not found' % delete
+ else:
+ yield 'deleted %s' % delete
+ for param in params:
+ try:
+ yield sh[param]
+ except KeyError:
+ yield '%s: not found' % param
+ for param, value in setters.items():
+ sh[param] = value
+ yield 'setting %s=%s' % (param, value)
+ finally:
+ sh.close()
+
+ishelve.add_help = False # there is a custom help
+
+if __name__ == '__main__':
+ for output in plac.call(ishelve):
+ print(output)
+
+</pre>
+<p>You can write the tests for such implementation as follows:</p>
+<pre class="literal-block">
+import plac
+from ishelve import ishelve
+
+def test():
+ assert plac.call(ishelve, []) == []
+ assert plac.call(ishelve, ['--clear']) == ['cleared the shelve']
+ assert plac.call(ishelve, ['a=1']) == ['setting a=1']
+ assert plac.call(ishelve, ['a']) == ['1']
+ assert plac.call(ishelve, ['--delete=a']) == ['deleted a']
+ assert plac.call(ishelve, ['a']) == ['a: not found']
+
+
+</pre>
+<p>There is a small optimization here: once <tt class="docutils literal"><span class="pre">plac.call(func)</span></tt>
+has been called, a <tt class="docutils literal"><span class="pre">.p</span></tt> attribute is attached to <tt class="docutils literal"><span class="pre">func</span></tt>, containing
+the parser associated to the function annotations. The second time
+<tt class="docutils literal"><span class="pre">plac.call(func)</span></tt> is invoked, the parser is re-used.</p>
+</div>
+<div class="section" id="writing-command-line-interpreters-with-plac">
+<h1>Writing command-line interpreters with plac</h1>
+<p>Apart from testing, there is another typical use case where <tt class="docutils literal"><span class="pre">plac.call</span></tt>
+is invoked multiple times, in the implementation of command interpreters.
+For instance, you could define an interative interpreter on top
+of <tt class="docutils literal"><span class="pre">ishelve</span></tt> as follows:</p>
+<pre class="literal-block">
+import plac
+from ishelve import ishelve
+
+ishelve.prefix_chars = '.'
+ishelve.add_help = False
+
+&#64;plac.annotations(
+ interactive=('start interactive interface', 'flag'))
+def main(interactive, *args):
+ if interactive:
+ import shlex
+ while True:
+ try:
+ line = raw_input('i&gt; ')
+ except EOFError:
+ break
+ cmd = shlex.split(line)
+ for out in plac.call(ishelve, cmd):
+ print(out)
+ else:
+ plac.call(ishelve, args)
+
+if __name__ == '__main__':
+ plac.call(main)
+
+</pre>
+<p>Here is an usage session, usinng <tt class="docutils literal"><span class="pre">rlwrap</span></tt> to enable readline features:</p>
+<pre class="literal-block">
+$ rlwrap python shelve_interpreter.py -i
+
+i&gt; ..clear
+cleared the shelve
+i&gt; a=1
+setting a=1
+i&gt; a
+1
+i&gt; b=2
+setting b=2
+i&gt; a b
+1
+2
+i&gt; ..delete a
+deleted a
+i&gt; a
+a: not found
+i&gt; ..all
+b=2
+i&gt; [CTRL-D]
+</pre>
+<p>As you see, it is possibly to write command interpreters directly on top of
+<tt class="docutils literal"><span class="pre">plac.call</span></tt> and it is not particularly difficult. However, the devil
+is in the details (I mean error management) and my recommendation, if
+you want to implement an interpreter of commands, is to use the
+class <tt class="docutils literal"><span class="pre">plac.Interpreter</span></tt> which is especially suited for this
+task. <tt class="docutils literal"><span class="pre">plac.Interpreter</span></tt> is available only if you are using a recent
+version of Python (&gt;= 2.5), because it is a context manager object
+to be used with the <tt class="docutils literal"><span class="pre">with</span></tt> statement. The only important method
+of <tt class="docutils literal"><span class="pre">plac.Interpreter</span></tt> is the <tt class="docutils literal"><span class="pre">.send</span></tt> method, which takes a
+string in input and returns a string in output. Internally the input string
+is splitted with <tt class="docutils literal"><span class="pre">shlex.split</span></tt> and passed to <tt class="docutils literal"><span class="pre">plac.call</span></tt>,
+with some trick to manage exceptions correctly. Moreover long options
+are managed with a single prefix character.</p>
+<pre class="literal-block">
+&quot;&quot;&quot;Call this script with rlwrap and you will be happy&quot;&quot;&quot;
+from __future__ import with_statement
+from plac_shell import Interpreter
+from shelve_interface import interpreter
+
+if __name__ == '__main__':
+ with Interpreter(interpreter) as i:
+ while True:
+ try:
+ line = raw_input('i&gt; ')
+ except EOFError:
+ break
+ print(i.send(line))
+
+</pre>
+</div>
+<div class="section" id="multi-parsers">
+<h1>Multi-parsers</h1>
+<p>As we saw, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to infer an arguments parser from the
+signature of a function. In addition, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is also able to infer a
+multi-parser from a container of commands, by inferring the subparsers
+from the commands. That is useful if you want to implement
+<em>subcommands</em> (a familiar example of a command-line application
+featuring subcommands is subversion).</p>
+<p>A container of commands is any object with a <tt class="docutils literal"><span class="pre">.commands</span></tt> attribute
+listing a set of functions or methods which are valid commands. In
+particular, a Python module is a perfect container of commands. As an
+example, consider the following module implementing a fake Version
+Control System:</p>
+<pre class="literal-block">
+&quot;A Fake Version Control System&quot;
+
+import plac
+
+commands = 'checkout', 'commit', 'status'
+
+&#64;plac.annotations(
+ url=('url of the source code', 'positional'))
+def checkout(url):
+ return ('checkout ', url)
+
+&#64;plac.annotations(
+ message=('commit message', 'option'))
+def commit(message):
+ return ('commit ', message)
+
+&#64;plac.annotations(quiet=('summary information', 'flag'))
+def status(quiet):
+ return ('status ', quiet)
+
+if __name__ == '__main__':
+ import __main__
+ print(plac.call(__main__))
+
+</pre>
+<p>Here is the usage message:</p>
+<pre class="literal-block">
+usage: vcs.py [-h] {status,commit,checkout} ...
+
+A Fake Version Control System
+
+optional arguments:
+ -h, --help show this help message and exit
+
+subcommands:
+ {status,commit,checkout}
+ -h to get additional help
+
+</pre>
+<p>If the commands are completely independent, a module is a good fit for
+a method container. In other situations, it is best to use a custom
+class. For instance, suppose you want to store the configuration of
+your application into a Python shelve; then, you may want to write
+a command-line tool to edit your configuration, i.e. a shelve
+interface:</p>
+<pre class="literal-block">
+import shelve
+import plac
+
+# error checking is missing: this is left to the reader
+class ShelveInterface(object):
+ &quot;A minimal interface over a shelve object&quot;
+ commands = 'set', 'show', 'show_all', 'delete'
+ def __init__(self, fname):
+ self.fname = fname
+ self.sh = shelve.open(fname)
+ def set(self, name, value):
+ &quot;set name value&quot;
+ yield 'setting %s=%s' % (name, value)
+ self.sh[name] = value
+ def show(self, *names):
+ &quot;show given parameters&quot;
+ for name in names:
+ yield '%s = %s\n' % (name, self.sh[name])
+ def show_all(self):
+ &quot;show all parameters&quot;
+ for name in self.sh:
+ yield '%s = %s\n' % (name, self.sh[name])
+ def delete(self, name=None):
+ &quot;delete given parameter (or everything)&quot;
+ if name is None:
+ yield 'deleting everything'
+ self.sh.clear()
+ else:
+ yield 'deleting %s' % name
+ del self.sh[name]
+
+if __name__ == '__main__':
+ interface = ShelveInterface('conf.shelve')
+ try:
+ for output in plac.call(interface):
+ print(output)
+ finally:
+ interface.sh.close()
+
+</pre>
+<p>Here is a session of usage on an Unix-like operating system:</p>
+<pre class="literal-block">
+$ alias conf=&quot;python shelve_interface.py&quot;
+$ conf set a pippo
+setting a=pippo
+$ conf set b lippo
+setting b=lippo
+$ conf show_all
+b = lippo
+a = pippo
+$ conf show a b
+a = pippo
+b = lippo
+$ conf delete a
+deleting a
+$ conf show_all
+b = lippo
+</pre>
+<p>Technically a multi-parser is a parser object with an attribute <tt class="docutils literal"><span class="pre">.subp</span></tt>
+which is a dictionary of subparsers; each of the methods listed in
+the attribute <tt class="docutils literal"><span class="pre">.commands</span></tt> corresponds to a subparser inferred from
+the method signature. The original object gets a <tt class="docutils literal"><span class="pre">.p</span></tt> attribute
+containing the main parser which is associated to an internal function
+which dispatches on the right method depending on the method name.</p>
+<p>Here is the usage message:</p>
+<pre class="literal-block">
+import plac
+
+class FVCS(object):
+ &quot;A Fake Version Control System&quot;
+ commands = 'checkout', 'commit', 'status', 'help'
+
+ &#64;plac.annotations(
+ name=('a recognized command', 'positional', None, str, commands))
+ def help(self, name):
+ self.p.subp[name].print_help()
+
+ &#64;plac.annotations(
+ url=('url of the source code', 'positional'))
+ def checkout(self, url):
+ print('checkout', url)
+
+ def commit(self):
+ print('commit')
+
+ &#64;plac.annotations(quiet=('summary information', 'flag'))
+ def status(self, quiet):
+ print('status', quiet)
+
+main = FVCS()
+
+if __name__ == '__main__':
+ plac.call(main)
+
+</pre>
+<pre class="literal-block">
+usage: example13.py [-h] {status,commit,checkout,help} ...
+
+A Fake Version Control System
+
+optional arguments:
+ -h, --help show this help message and exit
+
+subcommands:
+ {status,commit,checkout,help}
+ -h to get additional help
+
+</pre>
+</div>
+<div class="section" id="advanced-usage">
+<h1>Advanced usage</h1>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> relies on a <a class="reference external" href="http://argparse.googlecode.com">argparse</a> for all of the heavy lifting work and it is
+possible to leverage on <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features directly or indirectly.</p>
+<p>For instance, you can make invisible an argument in the usage message
+simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or
+<tt class="docutils literal"><span class="pre">argparse.SUPPRESS</span></tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a>
+directly.</p>
+<p>It is also possible to pass options to the underlying
+<tt class="docutils literal"><span class="pre">argparse.ArgumentParser</span></tt> object (currently it accepts the default
+arguments <tt class="docutils literal"><span class="pre">description</span></tt>, <tt class="docutils literal"><span class="pre">epilog</span></tt>, <tt class="docutils literal"><span class="pre">prog</span></tt>, <tt class="docutils literal"><span class="pre">usage</span></tt>,
+<tt class="docutils literal"><span class="pre">add_help</span></tt>, <tt class="docutils literal"><span class="pre">argument_default</span></tt>, <tt class="docutils literal"><span class="pre">parents</span></tt>, <tt class="docutils literal"><span class="pre">prefix_chars</span></tt>,
+<tt class="docutils literal"><span class="pre">fromfile_prefix_chars</span></tt>, <tt class="docutils literal"><span class="pre">conflict_handler</span></tt>, <tt class="docutils literal"><span class="pre">formatter_class</span></tt>).
+It is enough to set such attributes on the <tt class="docutils literal"><span class="pre">main</span></tt> function. For
+instance</p>
+<pre class="literal-block">
+def main(...):
+ pass
+
+main.add_help = False
+</pre>
+<p>disable the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This is not
+particularly elegant, but I assume the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be
+happy with the defaults and would not want to change them; still it is
+possible if she wants to. For instance, by setting the <tt class="docutils literal"><span class="pre">description</span></tt>
+attribute, it is possible to add a comment to the usage message (by
+default the docstring of the <tt class="docutils literal"><span class="pre">main</span></tt> 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 &quot;/&quot;
+as option prefix you can add the line:</p>
+<pre class="literal-block">
+main.prefix_chars='/-'
+</pre>
+<p><tt class="docutils literal"><span class="pre">prefix_chars</span></tt> is an <a class="reference external" href="http://argparse.googlecode.com">argparse</a> feature. The first prefix char (<tt class="docutils literal"><span class="pre">/</span></tt>) is used
+as the default in the construction of both short and long options;
+the second prefix char (<tt class="docutils literal"><span class="pre">-</span></tt>) is kept to keep the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> option
+working: however you can disable it and reimplement it if you like.
+For instance, here is how you could reimplement the <tt class="docutils literal"><span class="pre">help</span></tt> command
+in the Fake VCS example:</p>
+<pre class="literal-block">
+import plac
+from example13 import FVCS
+
+class VCS_with_help(FVCS):
+ commands = FVCS.commands + ('help',)
+
+ &#64;plac.annotations(
+ name=('a recognized command', 'positional', None, str, commands))
+ def help(self, name):
+ self.p.subp[name].print_help()
+
+main = VCS_with_help()
+
+if __name__ == '__main__':
+ plac.call(main)
+
+</pre>
+<p>Internally <tt class="docutils literal"><span class="pre">plac.call</span></tt> uses
+<tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> and adds the parser as an attribute <tt class="docutils literal"><span class="pre">.p</span></tt>.
+This also happers for multiparsers and you can take advantage of
+the <tt class="docutils literal"><span class="pre">.p</span></tt> attribute to invoke <tt class="docutils literal"><span class="pre">argparse.ArgumentParser</span></tt> methods.</p>
+<p>Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> 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 <tt class="docutils literal"><span class="pre">main</span></tt> function for you. For simplicity,
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic of that kind.</p>
+<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
+invoking the <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> utility function:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import plac
+&gt;&gt;&gt; def main(arg):
+... pass
+...
+&gt;&gt;&gt; print plac.parser_from(main)
+ArgumentParser(prog='', usage=None, description=None, version=None,
+formatter_class=&lt;class 'argparse.HelpFormatter'&gt;, conflict_handler='error',
+add_help=True)
+</pre>
+<p>I use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> in the unit tests of the module, but regular
+users should never need to use it, since the parser is also available
+as an attribute of the main function.</p>
+</div>
+<div class="section" id="custom-annotation-objects">
+<h1>Custom annotation objects</h1>
+<p>Internally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses an <tt class="docutils literal"><span class="pre">Annotation</span></tt> class to convert the tuples
+in the function signature into annotation objects, i.e. objects with
+six attributes <tt class="docutils literal"><span class="pre">help,</span> <span class="pre">kind,</span> <span class="pre">short,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar</span></tt>.</p>
+<p>Advanced users can implement their own annotation objects.
+For instance, here is an example of how you could implement annotations for
+positional arguments:</p>
+<pre class="literal-block">
+# annotations.py
+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
+
+</pre>
+<p>You can use such annotations objects as follows:</p>
+<pre class="literal-block">
+# example11.py
+import plac
+from annotations import Positional
+
+&#64;plac.annotations(
+ i=Positional(&quot;This is an int&quot;, int),
+ n=Positional(&quot;This is a float&quot;, float),
+ rest=Positional(&quot;Other arguments&quot;))
+def main(i, n, *rest):
+ print(i, n, rest)
+
+if __name__ == '__main__':
+ import plac; plac.call(main)
+
+</pre>
+<p>Here is the usage message you get:</p>
+<pre class="literal-block">
+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
+
+</pre>
+<p>You can go on and define <tt class="docutils literal"><span class="pre">Option</span></tt> and <tt class="docutils literal"><span class="pre">Flag</span></tt> 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.</p>
+</div>
+</div>
+</body>
+</html>
diff --git a/plac/doc/plac_adv.pdf b/plac/doc/plac_adv.pdf
new file mode 100644
index 0000000..d0b207f
--- /dev/null
+++ b/plac/doc/plac_adv.pdf
@@ -0,0 +1,1543 @@
+%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 7 0 R
+ /F4 13 0 R >>
+endobj
+% 'F1': class PDFType1Font
+2 0 obj
+% Font Helvetica
+<< /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'F2': class PDFType1Font
+3 0 obj
+% Font Helvetica-Bold
+<< /BaseFont /Helvetica-Bold
+ /Encoding /WinAnsiEncoding
+ /Name /F2
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER1': class PDFDictionary
+4 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 693.5936
+ 84.3779
+ 705.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER2': class PDFDictionary
+5 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 93.0279
+ 669.5936
+ 115.0329
+ 681.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER3': class PDFDictionary
+6 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 633.5936
+ 83.81291
+ 645.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F3': class PDFType1Font
+7 0 obj
+% Font Courier
+<< /BaseFont /Courier
+ /Encoding /WinAnsiEncoding
+ /Name /F3
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Page1': class PDFPage
+8 0 obj
+% Page dictionary
+<< /Annots [ 4 0 R
+ 5 0 R
+ 6 0 R ]
+ /Contents 40 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Page2': class PDFPage
+9 0 obj
+% Page dictionary
+<< /Contents 41 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Page3': class PDFPage
+10 0 obj
+% Page dictionary
+<< /Contents 42 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER4': class PDFDictionary
+11 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 117.8279
+ 729.5936
+ 139.5429
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER5': class PDFDictionary
+12 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 503.6477
+ 729.5936
+ 525.3627
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F4': class PDFType1Font
+13 0 obj
+% Font Helvetica-Oblique
+<< /BaseFont /Helvetica-Oblique
+ /Encoding /WinAnsiEncoding
+ /Name /F4
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Page4': class PDFPage
+14 0 obj
+% Page dictionary
+<< /Annots [ 11 0 R
+ 12 0 R ]
+ /Contents 43 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Page5': class PDFPage
+15 0 obj
+% Page dictionary
+<< /Contents 44 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER6': class PDFDictionary
+16 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 119.9936
+ 83.9079
+ 131.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER7': class PDFDictionary
+17 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 133.1029
+ 119.9936
+ 175.4379
+ 131.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER8': class PDFDictionary
+18 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 454.1177
+ 119.9936
+ 496.4527
+ 131.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page6': class PDFPage
+19 0 obj
+% Page dictionary
+<< /Annots [ 16 0 R
+ 17 0 R
+ 18 0 R ]
+ /Contents 45 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER9': class PDFDictionary
+20 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 455.2227
+ 744.5936
+ 534.3667
+ 756.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER10': class PDFDictionary
+21 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 127.99
+ 577.3936
+ 149.3857
+ 589.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER11': class PDFDictionary
+22 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 166.3664
+ 476.1936
+ 209.1976
+ 488.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER12': class PDFDictionary
+23 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 307.9178
+ 176.9936
+ 351.5999
+ 188.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER13': class PDFDictionary
+24 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 317.2329
+ 152.9936
+ 338.3529
+ 164.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER14': class PDFDictionary
+25 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
+ 134.9936
+ 410.5152
+ 146.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page7': class PDFPage
+26 0 obj
+% Page dictionary
+<< /Annots [ 20 0 R
+ 21 0 R
+ 22 0 R
+ 23 0 R
+ 24 0 R
+ 25 0 R ]
+ /Contents 46 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER15': class PDFDictionary
+27 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 106.6216
+ 576.3936
+ 128.3202
+ 588.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page8': class PDFPage
+28 0 obj
+% Page dictionary
+<< /Annots [ 27 0 R ]
+ /Contents 47 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Page9': class PDFPage
+29 0 obj
+% Page dictionary
+<< /Contents 48 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 39 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'R30': class PDFCatalog
+30 0 obj
+% Document Root
+<< /Outlines 32 0 R
+ /PageLabels 49 0 R
+ /PageMode /UseNone
+ /Pages 39 0 R
+ /Type /Catalog >>
+endobj
+% 'R31': class PDFInfo
+31 0 obj
+<< /Author ()
+ /CreationDate (D:20100611140831-01'00')
+ /Keywords ()
+ /Producer (ReportLab http://www.reportlab.com)
+ /Subject (\(unspecified\))
+ /Title (Testing and scripting your applications with plac) >>
+endobj
+% 'R32': class PDFOutlines
+32 0 obj
+<< /Count 6
+ /First 33 0 R
+ /Last 38 0 R
+ /Type /Outlines >>
+endobj
+% 'Outline.0': class OutlineEntryObject
+33 0 obj
+<< /Dest [ 8 0 R
+ /XYZ
+ 62.69291
+ 729.0236
+ 0 ]
+ /Next 34 0 R
+ /Parent 32 0 R
+ /Title (Introduction) >>
+endobj
+% 'Outline.1': class OutlineEntryObject
+34 0 obj
+<< /Dest [ 8 0 R
+ /XYZ
+ 62.69291
+ 618.0236
+ 0 ]
+ /Next 35 0 R
+ /Parent 32 0 R
+ /Prev 33 0 R
+ /Title (Testing applications with plac) >>
+endobj
+% 'Outline.2': class OutlineEntryObject
+35 0 obj
+<< /Dest [ 9 0 R
+ /XYZ
+ 62.69291
+ 390.6236
+ 0 ]
+ /Next 36 0 R
+ /Parent 32 0 R
+ /Prev 34 0 R
+ /Title (Writing command-line interpreters with plac) >>
+endobj
+% 'Outline.3': class OutlineEntryObject
+36 0 obj
+<< /Dest [ 14 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Next 37 0 R
+ /Parent 32 0 R
+ /Prev 35 0 R
+ /Title (Multi-parsers) >>
+endobj
+% 'Outline.4': class OutlineEntryObject
+37 0 obj
+<< /Dest [ 19 0 R
+ /XYZ
+ 62.69291
+ 155.4236
+ 0 ]
+ /Next 38 0 R
+ /Parent 32 0 R
+ /Prev 36 0 R
+ /Title (Advanced usage) >>
+endobj
+% 'Outline.5': class OutlineEntryObject
+38 0 obj
+<< /Dest [ 28 0 R
+ /XYZ
+ 62.69291
+ 611.8236
+ 0 ]
+ /Parent 32 0 R
+ /Prev 37 0 R
+ /Title (Custom annotation objects) >>
+endobj
+% 'R39': class PDFPages
+39 0 obj
+% page tree
+<< /Count 9
+ /Kids [ 8 0 R
+ 9 0 R
+ 10 0 R
+ 14 0 R
+ 15 0 R
+ 19 0 R
+ 26 0 R
+ 28 0 R
+ 29 0 R ]
+ /Type /Pages >>
+endobj
+% 'R40': class PDFStream
+40 0 obj
+% page stream
+<< /Length 3854 >>
+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 9.64 Tm 2.664882 0 Td 24 TL /F2 20 Tf 0 0 0 rg (Testing and scripting your applications with plac) Tj T* -2.664882 0 Td 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 (Introduction) 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 .564989 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (has been designed to be simple to use for simple stuff, but in truth it is a quite advanced tool with a) Tj T* 0 Tw 1.986905 Tw (field of applicability which far outreaches the specific domain of command-line arguments parsers. In) Tj T* 0 Tw .884985 Tw (reality ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is a generic tool to write domain specific languages \(DSL\). This document explains how you) Tj T* 0 Tw 1.766303 Tw (can use plac to test your application, and how you can use it to provide a scripting interface to your) Tj T* 0 Tw 1.384651 Tw (application. Notice that your application does not need to be a command-line application: you can use) Tj T* 0 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (whenever you have an API with strings in input and strings in output.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 597.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Testing applications with plac) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 531.0236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .352209 Tw 12 TL /F1 10 Tf 0 0 0 rg (In the standard usage, ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (is called only once on the main function; however in the tests it quite) Tj T* 0 Tw .297126 Tw (natural to invoke ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (multiple times on the same function with different arguments. For instance,) Tj T* 0 Tw .436457 Tw (suppose you want to store the configuration of your application into a Python shelve; then, you may want) Tj T* 0 Tw .683318 Tw (to write a command-line tool to edit your configuration, i.e. a shelve interface. A possible implementation) Tj T* 0 Tw (could be the following:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 89.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 432 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 413.71 Tm /F3 10 Tf 12 TL (import shelve) Tj T* (import plac) Tj T* T* (@plac.annotations\() Tj T* ( help=\('show help', 'flag'\),) Tj T* ( all=\('show all parameters in the shelve', 'flag'\),) Tj T* ( clear=\('clear the shelve', 'flag'\),) Tj T* ( delete=\('delete an element', 'option'\),) Tj T* ( filename=\('filename of the shelve', 'option'\),) Tj T* ( params='names of the parameters in the shelve',) Tj T* ( setters='setters param=value'\)) Tj T* (def ishelve\(help, all, clear, delete, filename='conf.shelve',) Tj T* ( *params, **setters\):) Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if help:) Tj T* ( yield 'Special commands:') Tj T* ( yield 'help, all, clear, delete') Tj T* ( elif all:) Tj T* ( for param, name in sh.items\(\):) Tj T* ( yield '%s=%s' % \(param, name\)) Tj T* ( elif clear:) Tj T* ( sh.clear\(\)) Tj T* ( yield 'cleared the shelve') Tj T* ( elif delete:) Tj T* ( try:) Tj T* ( del sh[delete]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % delete) Tj T* ( else:) Tj T* ( yield 'deleted %s' % delete) Tj T* ( for param in params:) Tj T* ( try:) Tj T* ( yield sh[param]) Tj T* ( except KeyError:) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (1) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R41': class PDFStream
+41 0 obj
+% page stream
+<< /Length 3706 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 607.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 156 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 137.71 Tm /F3 10 Tf 12 TL ( yield '%s: not found' % param ) Tj T* ( for param, value in setters.items\(\):) Tj T* ( sh[param] = value) Tj T* ( yield 'setting %s=%s' % \(param, value\)) Tj T* ( finally:) Tj T* ( sh.close\(\)) Tj T* T* (ishelve.add_help = False # there is a custom help) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(ishelve\):) Tj T* ( print\(output\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 587.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can write the tests for such implementation as follows:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 446.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 132 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (import plac) Tj T* (from ishelve import ishelve) Tj T* T* (def test\(\):) Tj T* ( assert plac.call\(ishelve, []\) == []) Tj T* ( assert plac.call\(ishelve, ['--clear']\) == ['cleared the shelve']) Tj T* ( assert plac.call\(ishelve, ['a=1']\) == ['setting a=1']) Tj T* ( assert plac.call\(ishelve, ['a']\) == ['1']) Tj T* ( assert plac.call\(ishelve, ['--delete=a']\) == ['deleted a']) Tj T* ( assert plac.call\(ishelve, ['a']\) == ['a: not found']) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 402.6236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .344651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a small optimization here: once ) Tj /F3 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (has been called, a ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute is attached) Tj T* 0 Tw 7.140814 Tw (to ) Tj /F3 10 Tf (func) Tj /F1 10 Tf (, containing the parser associated to the function annotations. The second time) Tj T* 0 Tw /F3 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (is invoked, the parser is re-used.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 369.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Writing command-line interpreters with plac) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 327.6236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .677485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Apart from testing, there is another typical use case where ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (is invoked multiple times, in the) Tj T* 0 Tw .334269 Tw (implementation of command interpreters. For instance, you could define an interative interpreter on top of) Tj T* 0 Tw /F3 10 Tf (ishelve ) Tj /F1 10 Tf (as follows:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 90.42362 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 228 re B*
+Q
+q
+BT 1 0 0 1 0 209.71 Tm 12 TL /F3 10 Tf 0 0 0 rg (import plac) Tj T* (from ishelve import ishelve) Tj T* T* (ishelve.prefix_chars = '.') Tj T* (ishelve.add_help = False) Tj T* T* (@plac.annotations\() Tj T* ( interactive=\('start interactive interface', 'flag'\)\)) Tj T* (def main\(interactive, *args\):) Tj T* ( if interactive:) Tj T* ( import shlex) Tj T* ( while True:) Tj T* ( try:) Tj T* ( line = raw_input\('i) Tj (>) Tj ( '\)) Tj T* ( except EOFError:) Tj T* ( break) Tj T* ( cmd = shlex.split\(line\)) Tj T* ( for out in plac.call\(ishelve, cmd\):) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (2) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R42': class PDFStream
+42 0 obj
+% page stream
+<< /Length 3636 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 679.8236 cm
+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 84 re B*
+Q
+q
+BT 1 0 0 1 0 65.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ( print\(out\)) Tj T* ( else:) Tj T* ( plac.call\(ishelve, args\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) 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 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Here is an usage session, usinng ) Tj /F3 10 Tf (rlwrap ) Tj /F1 10 Tf (to enable readline features:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 398.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 252 re B*
+Q
+q
+BT 1 0 0 1 0 233.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ rlwrap python shelve_interpreter.py -i) Tj T* T* (i) Tj (>) Tj ( ..clear) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* (1) Tj T* (i) Tj (>) Tj ( b=2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( a b) Tj T* (1) Tj T* (2) Tj T* (i) Tj (>) Tj ( ..delete a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( ..all) Tj T* (b=2) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 282.6236 cm
+q
+BT 1 0 0 1 0 100.82 Tm 1.991654 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see, it is possibly to write command interpreters directly on top of ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (and it is not) Tj T* 0 Tw 5.022126 Tw (particularly difficult. However, the devil is in the details \(I mean error management\) and my) Tj T* 0 Tw 5.139269 Tw (recommendation, if you want to implement an interpreter of commands, is to use the class) Tj T* 0 Tw .05229 Tw /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (which is especially suited for this task. ) Tj /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is available only if you) Tj T* 0 Tw .454488 Tw (are using a recent version of Python \() Tj (>) Tj (= 2.5\), because it is a context manager object to be used with the) Tj T* 0 Tw .38229 Tw /F3 10 Tf (with ) Tj /F1 10 Tf (statement. The only important method of ) Tj /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is the ) Tj /F3 10 Tf (.send ) Tj /F1 10 Tf (method, which takes) Tj T* 0 Tw .057209 Tw (a string in input and returns a string in output. Internally the input string is splitted with ) Tj /F3 10 Tf (shlex.split ) Tj /F1 10 Tf (and) Tj T* 0 Tw 2.706136 Tw (passed to ) Tj /F3 10 Tf (plac.call) Tj /F1 10 Tf (, with some trick to manage exceptions correctly. Moreover long options are) Tj T* 0 Tw (managed with a single prefix character.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 105.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 168 re B*
+Q
+q
+BT 1 0 0 1 0 149.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ("""Call this script with rlwrap and you will be happy""") Tj T* (from __future__ import with_statement) Tj T* (from plac_shell import Interpreter) Tj T* (from shelve_interface import interpreter) Tj T* T* (if __name__ == '__main__':) Tj T* ( with Interpreter\(interpreter\) as i:) Tj T* ( while True:) Tj T* ( try:) Tj T* ( line = raw_input\('i) Tj (>) Tj ( '\)) Tj T* ( except EOFError:) Tj T* ( break) Tj T* ( print\(i.send\(line\)\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (3) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R43': class PDFStream
+43 0 obj
+% page stream
+<< /Length 3539 >>
+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 (Multi-parsers) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 690.0236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .594988 Tw 12 TL /F1 10 Tf 0 0 0 rg (As we saw, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to infer an arguments parser from the signature of a function. In addition, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is) Tj T* 0 Tw 1.96186 Tw (also able to infer a multi-parser from a container of commands, by inferring the subparsers from the) Tj T* 0 Tw .352651 Tw (commands. That is useful if you want to implement ) Tj /F4 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line) Tj T* 0 Tw (application featuring subcommands is subversion\).) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 648.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .636457 Tw 12 TL /F1 10 Tf 0 0 0 rg (A container of commands is any object with a ) Tj /F3 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or methods) Tj T* 0 Tw 1.50936 Tw (which are valid commands. In particular, a Python module is a perfect container of commands. As an) Tj T* 0 Tw (example, consider the following module implementing a fake Version Control System:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 350.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 288 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 269.71 Tm /F3 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\() Tj T* ( url=\('url of the source code', 'positional'\)\)) Tj T* (def checkout\(url\):) Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\() Tj T* ( message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( return \('commit ', message\)) Tj T* T* (@plac.annotations\(quiet=\('summary information', 'flag'\)\)) Tj T* (def status\(quiet\):) Tj T* ( return \('status ', quiet\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import __main__) Tj T* ( print\(plac.call\(__main__\)\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 330.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 189.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 132 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (usage: vcs.py [-h] {status,commit,checkout} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* T* (subcommands:) Tj T* ( {status,commit,checkout}) Tj T* ( -h to get additional help) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 133.6236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 40.82 Tm /F1 10 Tf 12 TL 1.614104 Tw (If the commands are completely independent, a module is a good fit for a method container. In other) Tj T* 0 Tw .876654 Tw (situations, it is best to use a custom class. For instance, suppose you want to store the configuration of) Tj T* 0 Tw 2.040574 Tw (your application into a Python shelve; then, you may want to write a command-line tool to edit your) Tj T* 0 Tw (configuration, i.e. a shelve interface:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (4) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R44': class PDFStream
+44 0 obj
+% page stream
+<< /Length 2642 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 295.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 468 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 449.71 Tm /F3 10 Tf 12 TL (import shelve) Tj T* (import plac) Tj T* T* (# error checking is missing: this is left to the reader) Tj T* (class ShelveInterface\(object\):) Tj T* ( "A minimal interface over a shelve object") Tj T* ( commands = 'set', 'show', 'show_all', 'delete') Tj T* ( def __init__\(self, fname\):) Tj T* ( self.fname = fname) Tj T* ( self.sh = shelve.open\(fname\)) Tj T* ( def set\(self, name, value\):) Tj T* ( "set name value") Tj T* ( yield 'setting %s=%s' % \(name, value\)) Tj T* ( self.sh[name] = value) Tj T* ( def show\(self, *names\):) Tj T* ( "show given parameters") Tj T* ( for name in names:) Tj T* ( yield '%s = %s\\n' % \(name, self.sh[name]\)) Tj T* ( def show_all\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s\\n' % \(name, self.sh[name]\)) Tj T* ( def delete\(self, name=None\):) Tj T* ( "delete given parameter \(or everything\)") Tj T* ( if name is None:) Tj T* ( yield 'deleting everything') Tj T* ( self.sh.clear\(\)) Tj T* ( else:) Tj T* ( yield 'deleting %s' % name) Tj T* ( del self.sh[name]) Tj T* T* (if __name__ == '__main__':) Tj T* ( interface = ShelveInterface\('conf.shelve'\)) Tj T* ( try:) Tj T* ( for output in plac.call\(interface\):) Tj T* ( print\(output\)) Tj T* ( finally:) Tj T* ( interface.sh.close\(\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 275.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is a session of usage on an Unix-like operating system:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 98.62362 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 168 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 149.71 Tm /F3 10 Tf 12 TL ($ alias conf="python shelve_interface.py") Tj T* ($ conf set a pippo) Tj T* (setting a=pippo) Tj T* ($ conf set b lippo) Tj T* (setting b=lippo) Tj T* ($ conf show_all) Tj T* (b = lippo) Tj T* (a = pippo) Tj T* ($ conf show a b) Tj T* (a = pippo) Tj T* (b = lippo) Tj T* ($ conf delete a) Tj T* (deleting a) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (5) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R45': class PDFStream
+45 0 obj
+% page stream
+<< /Length 3421 >>
+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 /F3 10 Tf 12 TL ($ conf show_all) Tj T* (b = lippo) 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 40.82 Tm .581235 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a multi-parser is a parser object with an attribute ) Tj /F3 10 Tf (.subp ) Tj /F1 10 Tf (which is a dictionary of subparsers;) Tj T* 0 Tw 1.757984 Tw (each of the methods listed in the attribute ) Tj /F3 10 Tf (.commands ) Tj /F1 10 Tf (corresponds to a subparser inferred from the) Tj T* 0 Tw .593984 Tw (method signature. The original object gets a ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute containing the main parser which is associated) Tj T* 0 Tw (to an internal function which dispatches on the right method depending on the method name.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 653.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 308.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 336 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 317.71 Tm /F3 10 Tf 12 TL (import plac) Tj T* T* (class FVCS\(object\):) Tj T* ( "A Fake Version Control System") Tj T* ( commands = 'checkout', 'commit', 'status', 'help') Tj T* T* ( @plac.annotations\() Tj T* ( name=\('a recognized command', 'positional', None, str, commands\)\)) Tj T* ( def help\(self, name\):) Tj T* ( self.p.subp[name].print_help\(\)) Tj T* T* ( @plac.annotations\() Tj T* ( url=\('url of the source code', 'positional'\)\)) Tj T* ( def checkout\(self, url\):) Tj T* ( print\('checkout', url\)) Tj T* T* ( def commit\(self\):) Tj T* ( print\('commit'\)) Tj T* T* ( @plac.annotations\(quiet=\('summary information', 'flag'\)\)) Tj T* ( def status\(self, quiet\):) Tj T* ( print\('status', quiet\)) Tj T* T* (main = FVCS\(\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 167.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 132 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (usage: example13.py [-h] {status,commit,checkout,help} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* T* (subcommands:) Tj T* ( {status,commit,checkout,help}) Tj T* ( -h to get additional help) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 134.4236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Advanced usage) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 104.4236 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 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (6) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R46': class PDFStream
+46 0 obj
+% page stream
+<< /Length 5867 >>
+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 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 1.435976 Tw /F3 10 Tf ('==SUPPRESS==' ) Tj /F1 10 Tf (as help string \(or ) Tj /F3 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use ) Tj 0 0 .501961 rg (argparse.FileType) Tj T* 0 Tw 0 0 0 rg (directly.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 675.0236 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 /F3 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F3 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F3 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 605.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 /F3 10 Tf 12 TL (def main\(...\):) Tj T* ( pass) Tj T* T* (main.add_help = False) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 525.8236 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 /F3 10 Tf (-h, --help) Tj /F1 10 Tf (. This is not particularly elegant, but I assume the) Tj T* 0 Tw .275703 Tw (typical user of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (will be happy with the defaults and would not want to change them; still it is possible if) Tj T* 0 Tw .365542 Tw (she wants to. For instance, by setting the ) Tj /F3 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add a comment to the) Tj T* 0 Tw .602339 Tw (usage message \(by default the docstring of the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function is used as description\). It is also possible) Tj T* 0 Tw .322988 Tw (to change the option prefix; for instance if your script must run under Windows and you want to use "/" as) Tj T* 0 Tw (option prefix you can add the line:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 492.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 24 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL (main.prefix_chars='/-') Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 436.6236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .591163 Tw 12 TL /F3 10 Tf 0 0 0 rg (prefix_chars ) Tj /F1 10 Tf (is an ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (feature. The first prefix char \() Tj /F3 10 Tf (/) Tj /F1 10 Tf (\) is used as the default in the construction) Tj T* 0 Tw .266098 Tw (of both short and long options; the second prefix char \() Tj /F3 10 Tf (-) Tj /F1 10 Tf (\) is kept to keep the ) Tj /F3 10 Tf (-h/--help ) Tj /F1 10 Tf (option working:) Tj T* 0 Tw .107209 Tw (however you can disable it and reimplement it if you like. For instance, here is how you could reimplement) Tj T* 0 Tw (the ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (command in the Fake VCS example:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 235.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 192 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 173.71 Tm /F3 10 Tf 12 TL (import plac) Tj T* (from example13 import FVCS) Tj T* T* (class VCS_with_help\(FVCS\):) Tj T* ( commands = FVCS.commands + \('help',\)) Tj T* T* ( @plac.annotations\() Tj T* ( name=\('a recognized command', 'positional', None, str, commands\)\)) Tj T* ( def help\(self, name\):) Tj T* ( self.p.subp[name].print_help\(\)) Tj T* T* (main = VCS_with_help\(\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 191.4236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.938443 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (uses ) Tj /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and adds the parser as an attribute ) Tj /F3 10 Tf (.p) Tj /F1 10 Tf (. This also) Tj T* 0 Tw 8.165366 Tw (happers for multiparsers and you can take advantage of the ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute to invoke) Tj T* 0 Tw /F3 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (methods.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 149.4236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.442126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Interested readers should read the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to understand the meaning of the other) Tj T* 0 Tw .771567 Tw (options. If there is a set of options that you use very often, you may consider writing a decorator adding) Tj T* 0 Tw (such options to the ) Tj /F3 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 119.4236 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 /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (7) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R47': class PDFStream
+47 0 obj
+% page stream
+<< /Length 3975 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 655.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+BT 1 0 0 1 0 89.71 Tm 12 TL /F3 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 623.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .365542 Tw 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F3 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* 0 Tw (since the parser is also available as an attribute of the main function.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 590.8236 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 560.8236 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 /F3 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 /F3 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 530.8236 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 401.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 120 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL (# annotations.py) Tj T* (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
+1 0 0 1 62.69291 381.6236 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 204.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 168 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 149.71 Tm /F3 10 Tf 12 TL (# example11.py) Tj T* (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
+1 0 0 1 62.69291 184.4236 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
+q
+1 0 0 1 62.69291 91.22362 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 65.71 Tm /F3 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R48': class PDFStream
+48 0 obj
+% page stream
+<< /Length 1026 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 715.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 48 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 29.71 Tm /F3 10 Tf 12 TL 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 28.82 Tm .713516 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can go on and define ) Tj /F3 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F3 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 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (9) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R49': class PDFPageLabels
+49 0 obj
+% Document Root
+<< /Nums [ 0
+ 50 0 R
+ 1
+ 51 0 R
+ 2
+ 52 0 R
+ 3
+ 53 0 R
+ 4
+ 54 0 R
+ 5
+ 55 0 R
+ 6
+ 56 0 R
+ 7
+ 57 0 R
+ 8
+ 58 0 R ] >>
+endobj
+% 'R50': class PDFPageLabel
+50 0 obj
+% None
+<< /S /D
+ /St 1 >>
+endobj
+% 'R51': class PDFPageLabel
+51 0 obj
+% None
+<< /S /D
+ /St 2 >>
+endobj
+% 'R52': class PDFPageLabel
+52 0 obj
+% None
+<< /S /D
+ /St 3 >>
+endobj
+% 'R53': class PDFPageLabel
+53 0 obj
+% None
+<< /S /D
+ /St 4 >>
+endobj
+% 'R54': class PDFPageLabel
+54 0 obj
+% None
+<< /S /D
+ /St 5 >>
+endobj
+% 'R55': class PDFPageLabel
+55 0 obj
+% None
+<< /S /D
+ /St 6 >>
+endobj
+% 'R56': class PDFPageLabel
+56 0 obj
+% None
+<< /S /D
+ /St 7 >>
+endobj
+% 'R57': class PDFPageLabel
+57 0 obj
+% None
+<< /S /D
+ /St 8 >>
+endobj
+% 'R58': class PDFPageLabel
+58 0 obj
+% None
+<< /S /D
+ /St 9 >>
+endobj
+xref
+0 59
+0000000000 65535 f
+0000000113 00000 n
+0000000246 00000 n
+0000000411 00000 n
+0000000598 00000 n
+0000000847 00000 n
+0000001096 00000 n
+0000001334 00000 n
+0000001493 00000 n
+0000001808 00000 n
+0000002087 00000 n
+0000002381 00000 n
+0000002632 00000 n
+0000002871 00000 n
+0000003051 00000 n
+0000003361 00000 n
+0000003655 00000 n
+0000003905 00000 n
+0000004154 00000 n
+0000004389 00000 n
+0000004722 00000 n
+0000005038 00000 n
+0000005288 00000 n
+0000005538 00000 n
+0000005788 00000 n
+0000006040 00000 n
+0000006312 00000 n
+0000006673 00000 n
+0000006910 00000 n
+0000007211 00000 n
+0000007492 00000 n
+0000007651 00000 n
+0000007912 00000 n
+0000008037 00000 n
+0000008209 00000 n
+0000008414 00000 n
+0000008632 00000 n
+0000008821 00000 n
+0000009011 00000 n
+0000009181 00000 n
+0000009359 00000 n
+0000013314 00000 n
+0000017121 00000 n
+0000020858 00000 n
+0000024498 00000 n
+0000027241 00000 n
+0000030763 00000 n
+0000036731 00000 n
+0000040807 00000 n
+0000041938 00000 n
+0000042135 00000 n
+0000042212 00000 n
+0000042289 00000 n
+0000042366 00000 n
+0000042443 00000 n
+0000042520 00000 n
+0000042597 00000 n
+0000042674 00000 n
+0000042751 00000 n
+trailer
+<< /ID
+ % ReportLab generated PDF document -- digest (http://www.reportlab.com)
+ [(\030N\026;L\241\300\254\011\265\273\363\247\201 N) (\030N\026;L\241\300\254\011\265\273\363\247\201 N)]
+
+ /Info 31 0 R
+ /Root 30 0 R
+ /Size 59 >>
+startxref
+42798
+%%EOF
diff --git a/plac/doc/plac_adv.txt b/plac/doc/plac_adv.txt
new file mode 100644
index 0000000..16a974b
--- /dev/null
+++ b/plac/doc/plac_adv.txt
@@ -0,0 +1,279 @@
+Testing and scripting your applications with plac
+=========================================================
+
+Introduction
+-----------------------------------------------------
+
+plac_ has been designed to be simple to use for simple stuff, but in
+truth it is a quite advanced tool with a field of applicability
+which far outreaches the specific domain of command-line arguments parsers.
+In reality plac_ is a generic tool to write domain specific
+languages (DSL).
+This document explains how you can use plac to test your application, and
+how you can use it to provide a scripting interface to your application.
+Notice that your application does not need to be a command-line
+application: you can use plac_ whenever you have an API with strings
+in input and strings in output.
+
+Testing applications with plac
+-------------------------------------------
+
+In the standard usage, ``plac.call`` is called only once on the main
+function; however in the tests it quite natural to invoke ``plac.call``
+multiple times on the same function with different arguments.
+For instance, suppose you want to store the configuration of
+your application into a Python shelve; then, you may want to write
+a command-line tool to edit your configuration, i.e. a shelve
+interface. A possible implementation could be the following:
+
+.. include:: ishelve.py
+ :literal:
+
+You can write the tests for such implementation as follows:
+
+.. include:: test_ishelve.py
+ :literal:
+
+There is a small optimization here: once ``plac.call(func)``
+has been called, a ``.p`` attribute is attached to ``func``, containing
+the parser associated to the function annotations. The second time
+``plac.call(func)`` is invoked, the parser is re-used.
+
+Writing command-line interpreters with plac
+-----------------------------------------------
+
+Apart from testing, there is another typical use case where ``plac.call``
+is invoked multiple times, in the implementation of command interpreters.
+For instance, you could define an interative interpreter on top
+of ``ishelve`` as follows:
+
+.. include:: shelve_interpreter.py
+ :literal:
+
+Here is an usage session, usinng ``rlwrap`` to enable readline features::
+
+ $ rlwrap python shelve_interpreter.py -i
+
+ i> ..clear
+ cleared the shelve
+ i> a=1
+ setting a=1
+ i> a
+ 1
+ i> b=2
+ setting b=2
+ i> a b
+ 1
+ 2
+ i> ..delete a
+ deleted a
+ i> a
+ a: not found
+ i> ..all
+ b=2
+ i> [CTRL-D]
+
+As you see, it is possibly to write command interpreters directly on top of
+``plac.call`` and it is not particularly difficult. However, the devil
+is in the details (I mean error management) and my recommendation, if
+you want to implement an interpreter of commands, is to use the
+class ``plac.Interpreter`` which is especially suited for this
+task. ``plac.Interpreter`` is available only if you are using a recent
+version of Python (>= 2.5), because it is a context manager object
+to be used with the ``with`` statement. The only important method
+of ``plac.Interpreter`` is the ``.send`` method, which takes a
+string in input and returns a string in output. Internally the input string
+is splitted with ``shlex.split`` and passed to ``plac.call``,
+with some trick to manage exceptions correctly. Moreover long options
+are managed with a single prefix character.
+
+.. include:: shelve_cli.py
+ :literal:
+
+Multi-parsers
+----------------------------------------
+
+As we saw, plac_ is able to infer an arguments parser from the
+signature of a function. In addition, plac_ is also able to infer a
+multi-parser from a container of commands, by inferring the subparsers
+from the commands. That is useful if you want to implement
+*subcommands* (a familiar example of a command-line application
+featuring subcommands is subversion).
+
+A container of commands is any object with a ``.commands`` attribute
+listing a set of functions or methods which are valid commands. In
+particular, a Python module is a perfect container of commands. As an
+example, consider the following module implementing a fake Version
+Control System:
+
+.. include:: vcs.py
+ :literal:
+
+Here is the usage message:
+
+.. include:: vcs.help
+ :literal:
+
+If the commands are completely independent, a module is a good fit for
+a method container. In other situations, it is best to use a custom
+class. For instance, suppose you want to store the configuration of
+your application into a Python shelve; then, you may want to write
+a command-line tool to edit your configuration, i.e. a shelve
+interface:
+
+.. include:: shelve_interface2.py
+ :literal:
+
+Here is a session of usage on an Unix-like operating system::
+
+ $ alias conf="python shelve_interface.py"
+ $ conf set a pippo
+ setting a=pippo
+ $ conf set b lippo
+ setting b=lippo
+ $ conf show_all
+ b = lippo
+ a = pippo
+ $ conf show a b
+ a = pippo
+ b = lippo
+ $ conf delete a
+ deleting a
+ $ conf show_all
+ b = lippo
+
+Technically a multi-parser is a parser object with an attribute ``.subp``
+which is a dictionary of subparsers; each of the methods listed in
+the attribute ``.commands`` corresponds to a subparser inferred from
+the method signature. The original object gets a ``.p`` attribute
+containing the main parser which is associated to an internal function
+which dispatches on the right method depending on the method name.
+
+Here is the usage message:
+
+.. include:: example13.py
+ :literal:
+
+.. include:: example13.help
+ :literal:
+
+Advanced usage
+----------------------------------------------------
+
+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 defaults and would not want to change them; 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 line::
+
+ main.prefix_chars='/-'
+
+``prefix_chars`` is an argparse_ feature. The first prefix char (``/``) is used
+as the default in the construction of both short and long options;
+the second prefix char (``-``) is kept to keep the ``-h/--help`` option
+working: however you can disable it and reimplement it if you like.
+For instance, here is how you could reimplement the ``help`` command
+in the Fake VCS example:
+
+.. include:: example14.py
+ :literal:
+
+Internally ``plac.call`` uses
+``plac.parser_from`` and adds the parser as an attribute ``.p``.
+This also happers for multiparsers and you can take advantage of
+the ``.p`` attribute to invoke ``argparse.ArgumentParser`` methods.
+
+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, since the parser is also available
+as an attribute of the main function.
+
+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:
+
+.. include:: example11.help
+ :literal:
+
+You can go on and define ``Option`` and ``Flag`` classes, if you like.
+Using custom annotation objects you could do advanced things like extracting the
+annotations from a configuration file or from a database, but I expect such
+use cases to be quite rare: the default mechanism should work
+pretty well for most users.
+
+.. _argparse: http://argparse.googlecode.com
+.. _optparse: http://docs.python.org/library/optparse.html
+.. _getopt: http://docs.python.org/library/getopt.html
+.. _optionparse: http://code.activestate.com/recipes/278844-parsing-the-command-line/
+.. _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
+.. _argparse.FileType: http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType
+.. _Clap: http://pypi.python.org/pypi/Clap/0.7
+.. _OptionParser: http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser
+.. _SQLAlchemy: http://www.sqlalchemy.org/
+.. _SqlSoup: http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html
+.. _CLIArgs: http://pypi.python.org/pypi/CLIArgs
+.. _opterator: http://pypi.python.org/pypi/opterator
+.. _advanced usage document: in-writing
diff --git a/plac/doc/vcs.help b/plac/doc/vcs.help
new file mode 100644
index 0000000..5d7ac77
--- /dev/null
+++ b/plac/doc/vcs.help
@@ -0,0 +1,10 @@
+usage: vcs.py [-h] {status,commit,checkout} ...
+
+A Fake Version Control System
+
+optional arguments:
+ -h, --help show this help message and exit
+
+subcommands:
+ {status,commit,checkout}
+ -h to get additional help
diff --git a/plac/doc/vcs.py b/plac/doc/vcs.py
new file mode 100644
index 0000000..a893fd9
--- /dev/null
+++ b/plac/doc/vcs.py
@@ -0,0 +1,23 @@
+"A Fake Version Control System"
+
+import plac
+
+commands = 'checkout', 'commit', 'status'
+
+@plac.annotations(
+ url=('url of the source code', 'positional'))
+def checkout(url):
+ return ('checkout ', url)
+
+@plac.annotations(
+ message=('commit message', 'option'))
+def commit(message):
+ return ('commit ', message)
+
+@plac.annotations(quiet=('summary information', 'flag'))
+def status(quiet):
+ return ('status ', quiet)
+
+if __name__ == '__main__':
+ import __main__
+ print(plac.call(__main__))
diff --git a/plac/plac.py b/plac/plac.py
index 458e128..31bc89b 100644
--- a/plac/plac.py
+++ b/plac/plac.py
@@ -25,227 +25,13 @@
"""
plac, the easiest Command Line Arguments Parser in the world.
-See plac/doc.html for the documentation.
+See doc/plac.pdf for the documentation.
"""
-# this module should be kept Python 2.3 compatible
__version__ = '0.5.0'
-import re, sys, inspect, argparse
+import sys
+from plac_core import *
-if sys.version >= '3':
- from inspect import getfullargspec
-else:
- class getfullargspec(object):
- "A quick and dirty replacement for getfullargspec for Python 2.X"
- def __init__(self, f):
- self.args, self.varargs, self.varkw, self.defaults = \
- inspect.getargspec(f)
- self.annotations = getattr(f, '__annotations__', {})
-try:
- set
-except NameError: # Python 2.3
- from sets import Set as set
-
-def annotations(**ann):
- """
- Returns a decorator annotating a function with the given annotations.
- This is a trick to support function annotations in Python 2.X.
- """
- def annotate(f):
- fas = getfullargspec(f)
- args = fas.args
- if fas.varargs:
- args.append(fas.varargs)
- if fas.varkw:
- args.append(fas.varkw)
- for argname in ann:
- if argname not in args:
- raise NameError(
- 'Annotating non-existing argument: %s' % argname)
- f.__annotations__ = ann
- return f
- return annotate
-
-def is_annotation(obj):
- """
- An object is an annotation object if it has the attributes
- help, kind, abbrev, type, choices, metavar.
- """
- return (hasattr(obj, 'help') and hasattr(obj, 'kind') and
- hasattr(obj, 'abbrev') and hasattr(obj, 'type')
- and hasattr(obj, 'choices') and hasattr(obj, 'metavar'))
-
-class Annotation(object):
- def __init__(self, help="", kind="positional", abbrev=None, type=None,
- choices=None, metavar=None):
- assert kind in ('positional', 'option', 'flag'), kind
- if kind == "positional":
- assert abbrev is None, abbrev
- self.help = help
- self.kind = kind
- self.abbrev = abbrev
- self.type = type
- self.choices = choices
- self.metavar = metavar
-
- def from_(cls, obj):
- "Helper to convert an object into an annotation, if needed"
- if is_annotation(obj):
- return obj # do nothing
- elif hasattr(obj, '__iter__') and not isinstance(obj, str):
- return cls(*obj)
- return cls(obj)
- from_ = classmethod(from_)
-
-NONE = object() # sentinel use to signal the absence of a default
-
-PARSER_CFG = getfullargspec(argparse.ArgumentParser.__init__).args[1:]
-# the default arguments accepted by an ArgumentParser object
-
-class PlacHelpFormatter(argparse.HelpFormatter):
- "Custom HelpFormatter which does not displau the default value twice"
-
- def _format_action_invocation(self, action):
- if not action.option_strings:
- return self._metavar_formatter(action, action.dest)(1)[0]
- long_short = tuple(action.option_strings)
- if action.nargs == 0: # format is -s, --long
- return '%s, %s' % long_short
- else: # format is -s, --long ARGS
- default = action.dest.upper()
- args_string = self._format_args(action, default)
- return '%s, %s %s' % (long_short + (args_string,))
-
-def _parser_from(func, baseparser=None, **cfg):
- """
- Extract the arguments from the attributes of the passed function
- (or bound method) and return an ArgumentParser instance. As a side
- effect, adds a .p attribute to func.
- """
- cfg.setdefault('description', func.__doc__)
- cfg.setdefault('formatter_class', PlacHelpFormatter)
- for n, v in vars(func).items():
- if n in PARSER_CFG: # arguments of ArgumentParser
- cfg[n] = v
- p = baseparser or argparse.ArgumentParser(**cfg)
- p.func = func
- f = p.argspec = getfullargspec(func)
- if inspect.ismethod(func):
- del f.args[0] # remove self
- try:
- func.im_func.p = p # Python 2.X
- except AttributeError:
- func.__func__.p = p # Python 2.3
- else:
- func.p = p
- defaults = f.defaults or ()
- n_args = len(f.args)
- n_defaults = len(defaults)
- alldefaults = (NONE,) * (n_args - n_defaults) + defaults
- short_prefix = getattr(func, 'short_prefix', '-')
- long_prefix = getattr(func, 'long_prefix', '--')
- for name, default in zip(f.args, alldefaults):
- ann = f.annotations.get(name, ())
- a = Annotation.from_(ann)
- metavar = a.metavar
- if default is NONE:
- dflt = None
- else:
- dflt = default
- if a.kind in ('option', 'flag'):
- short = short_prefix + (a.abbrev or name[0])
- long = long_prefix + name
- elif default is NONE: # required argument
- p.add_argument(name, help=a.help, type=a.type, choices=a.choices,
- metavar=metavar)
- else: # default argument
- p.add_argument(name, nargs='?', help=a.help, default=dflt,
- type=a.type, choices=a.choices, metavar=metavar)
- if a.kind == 'option':
- if default is not NONE:
- metavar = metavar or str(default)
- p.add_argument(short, long, help=a.help, default=dflt,
- type=a.type, choices=a.choices, metavar=metavar)
- elif a.kind == 'flag':
- if default is not NONE and default is not False:
- raise TypeError('Flag %r wants default False, got %r' %
- (name, default))
- p.add_argument(short, long, action='store_true', help=a.help)
- if f.varargs:
- a = Annotation.from_(f.annotations.get(f.varargs, ()))
- p.add_argument(f.varargs, nargs='*', help=a.help, default=[],
- type=a.type, metavar=a.metavar)
- if f.varkw:
- a = Annotation.from_(f.annotations.get(f.varkw, ()))
- p.add_argument(f.varkw, nargs='*', help=a.help, default={},
- type=a.type, metavar=a.metavar)
- return p
-
-def parser_from(obj, baseparser=None, **cfg):
- """
- obj can be a function, a bound method, or a generic object with a
- .commands attribute. Returns an ArgumentParser with attributes
- .func and .argspec, or a multi-parser with attribute .sub.
- """
- if hasattr(obj, 'p'): # the underlying parser has been generated already
- return obj.p
- elif hasattr(obj, 'commands'): # an object with commands
- commands = obj.commands
- elif inspect.isfunction(obj) or inspect.ismethod(obj): # error if not func
- return _parser_from(obj, baseparser, **cfg)
- p = obj.p = baseparser or argparse.ArgumentParser(**cfg)
- subparsers = p.add_subparsers(
- title='subcommands', help='-h to get additional help')
- p.subp = {}
- for cmd in commands:
- method = getattr(obj, cmd)
- p.subp[cmd] = _parser_from(method, subparsers.add_parser(cmd), **cfg)
- return p
-
-def _extract_kwargs(args):
- "Returns two lists: regular args and name=value args"
- arglist = []
- kwargs = {}
- for arg in args:
- match = re.match(r'([a-zA-Z_]\w*)=', arg)
- if match:
- name = match.group(1)
- kwargs[name] = arg[len(name)+1:]
- else:
- arglist.append(arg)
- return arglist, kwargs
-
-def parser_call(p, arglist):
- """
- Given a parser, calls its underlying callable with the arglist.
- Works also for multiparsers by dispatching to the underlying parser.
- """
- subp = getattr(p, 'subp', None)
- if subp: # subparsers
- p.parse_args(arglist) # argument checking
- return parser_call(subp[arglist[0]], arglist[1:])
- # regular parser
- if p.argspec.varkw:
- arglist, kwargs = _extract_kwargs(arglist)
- else:
- kwargs = {}
- argdict = vars(p.parse_args(arglist))
- args = [argdict[a] for a in p.argspec.args]
- varargs = argdict.get(p.argspec.varargs, [])
- collision = set(p.argspec.args) & set(kwargs)
- if collision:
- p.error('colliding keyword arguments: %s' % ' '.join(collision))
- return p.func(*(args + varargs), **kwargs)
-
-def call(obj, arglist=sys.argv[1:], **cfg):
- """
- If obj is a function or a bound method, parses the given arglist
- by using an argument parser inferred from the annotations of obj
- and then calls obj with the parsed arguments.
- If obj is an object with attribute .commands, builds a multiparser
- and dispatches to the associated subparsers.
- The user can provide a custom parse_annotation hook or replace
- the default one.
- """
- return parser_call(parser_from(obj, **cfg), arglist)
+if sys.version >= '2.5':
+ from plac_ext import Interpreter
diff --git a/plac/plac_core.py b/plac/plac_core.py
new file mode 100644
index 0000000..8df1d03
--- /dev/null
+++ b/plac/plac_core.py
@@ -0,0 +1,218 @@
+# this module should be kept Python 2.3 compatible
+import re, sys, inspect, argparse
+
+if sys.version >= '3':
+ from inspect import getfullargspec
+else:
+ class getfullargspec(object):
+ "A quick and dirty replacement for getfullargspec for Python 2.X"
+ def __init__(self, f):
+ self.args, self.varargs, self.varkw, self.defaults = \
+ inspect.getargspec(f)
+ self.annotations = getattr(f, '__annotations__', {})
+try:
+ set
+except NameError: # Python 2.3
+ from sets import Set as set
+
+def annotations(**ann):
+ """
+ Returns a decorator annotating a function with the given annotations.
+ This is a trick to support function annotations in Python 2.X.
+ """
+ def annotate(f):
+ fas = getfullargspec(f)
+ args = fas.args
+ if fas.varargs:
+ args.append(fas.varargs)
+ if fas.varkw:
+ args.append(fas.varkw)
+ for argname in ann:
+ if argname not in args:
+ raise NameError(
+ 'Annotating non-existing argument: %s' % argname)
+ f.__annotations__ = ann
+ return f
+ return annotate
+
+def is_annotation(obj):
+ """
+ An object is an annotation object if it has the attributes
+ help, kind, abbrev, type, choices, metavar.
+ """
+ return (hasattr(obj, 'help') and hasattr(obj, 'kind') and
+ hasattr(obj, 'abbrev') and hasattr(obj, 'type')
+ and hasattr(obj, 'choices') and hasattr(obj, 'metavar'))
+
+class Annotation(object):
+ def __init__(self, help="", kind="positional", abbrev=None, type=None,
+ choices=None, metavar=None):
+ assert kind in ('positional', 'option', 'flag'), kind
+ if kind == "positional":
+ assert abbrev is None, abbrev
+ self.help = help
+ self.kind = kind
+ self.abbrev = abbrev
+ self.type = type
+ self.choices = choices
+ self.metavar = metavar
+
+ def from_(cls, obj):
+ "Helper to convert an object into an annotation, if needed"
+ if is_annotation(obj):
+ return obj # do nothing
+ elif hasattr(obj, '__iter__') and not isinstance(obj, str):
+ return cls(*obj)
+ return cls(obj)
+ from_ = classmethod(from_)
+
+NONE = object() # sentinel use to signal the absence of a default
+
+PARSER_CFG = getfullargspec(argparse.ArgumentParser.__init__).args[1:]
+# the default arguments accepted by an ArgumentParser object
+
+def pconf(obj):
+ "Extracts the configuration of the underlying ArgumentParser from obj"
+ cfg = dict(description=obj.__doc__)
+ for name in dir(obj):
+ if name in PARSER_CFG: # argument of ArgumentParser
+ cfg[name] = getattr(obj, name)
+ return cfg
+
+def _parser_from(func, baseparser=None):
+ """
+ Extract the arguments from the attributes of the passed function
+ (or bound method) and return an ArgumentParser instance. As a side
+ effect, adds a .p attribute to func.
+ """
+ p = baseparser or argparse.ArgumentParser(**pconf(func))
+ p.func = func
+ p.argspec = f = getfullargspec(func)
+ p.parselist = _parser_call.__get__(p)
+ if inspect.ismethod(func):
+ del f.args[0] # remove self
+ try:
+ func.im_func.p = p # Python 2.X
+ except AttributeError:
+ func.__func__.p = p # Python 2.3
+ else:
+ func.p = p
+ defaults = f.defaults or ()
+ n_args = len(f.args)
+ n_defaults = len(defaults)
+ alldefaults = (NONE,) * (n_args - n_defaults) + defaults
+ prefix = p.prefix = getattr(func, 'prefix_chars', '-')[0]
+ for name, default in zip(f.args, alldefaults):
+ ann = f.annotations.get(name, ())
+ a = Annotation.from_(ann)
+ metavar = a.metavar
+ if default is NONE:
+ dflt = None
+ else:
+ dflt = default
+ if a.kind in ('option', 'flag'):
+ if a.abbrev:
+ shortlong = (prefix + a.abbrev, prefix*2 + name)
+ else:
+ shortlong = (prefix + name,)
+ elif default is NONE: # required argument
+ p.add_argument(name, help=a.help, type=a.type, choices=a.choices,
+ metavar=metavar)
+ else: # default argument
+ p.add_argument(name, nargs='?', help=a.help, default=dflt,
+ type=a.type, choices=a.choices, metavar=metavar)
+ if a.kind == 'option':
+ if default is not NONE:
+ metavar = metavar or str(default)
+ p.add_argument(help=a.help, default=dflt, type=a.type,
+ choices=a.choices, metavar=metavar, *shortlong)
+ elif a.kind == 'flag':
+ if default is not NONE and default is not False:
+ raise TypeError('Flag %r wants default False, got %r' %
+ (name, default))
+ p.add_argument(action='store_true', help=a.help, *shortlong)
+ if f.varargs:
+ a = Annotation.from_(f.annotations.get(f.varargs, ()))
+ p.add_argument(f.varargs, nargs='*', help=a.help, default=[],
+ type=a.type, metavar=a.metavar)
+ if f.varkw:
+ a = Annotation.from_(f.annotations.get(f.varkw, ()))
+ p.add_argument(f.varkw, nargs='*', help=a.help, default={},
+ type=a.type, metavar=a.metavar)
+ return p
+
+def parser_from(obj, baseparser=None):
+ """
+ obj can be a function, a bound method, or a generic object with a
+ .commands attribute. Returns an ArgumentParser with attributes
+ .func and .argspec, or a multi-parser with attribute .sub.
+ """
+ if hasattr(obj, 'p'): # the underlying parser has been generated already
+ return obj.p
+ elif hasattr(obj, 'commands'): # a command container
+ p = obj.p = baseparser or argparse.ArgumentParser(**pconf(obj))
+ subparsers = p.add_subparsers(
+ title='subcommands', help='-h to get additional help')
+ p.subp = {}
+ for cmd in obj.commands:
+ method = getattr(obj, cmd)
+ baseparser = subparsers.add_parser(cmd, **pconf(method))
+ p.subp[cmd] = _parser_from(method, baseparser)
+ p.parselist = _parser_call.__get__(p)
+ return p
+ elif inspect.isfunction(obj) or inspect.ismethod(obj): # error if not func
+ return _parser_from(obj, baseparser)
+ else:
+ raise TypeError('%r could not be converted into a parser' % obj)
+
+def _extract_kwargs(args):
+ "Returns two lists: regular args and name=value args"
+ arglist = []
+ kwargs = {}
+ for arg in args:
+ match = re.match(r'([a-zA-Z_]\w*)=', arg)
+ if match:
+ name = match.group(1)
+ kwargs[name] = arg[len(name)+1:]
+ else:
+ arglist.append(arg)
+ return arglist, kwargs
+
+def _parser_call(p, arglist):
+ """
+ Given a parser, calls its underlying callable with the arglist.
+ Works also for multiparsers by dispatching to the underlying parser.
+ """
+ subp = getattr(p, 'subp', None)
+ if subp: # subparsers
+ p.parse_args(arglist) # argument checking
+ return _parser_call(subp[arglist[0]], arglist[1:])
+ # regular parser
+ if p.argspec.varkw:
+ arglist, kwargs = _extract_kwargs(arglist)
+ else:
+ kwargs = {}
+ argdict = vars(p.parse_args(arglist))
+ args = [argdict[a] for a in p.argspec.args]
+ varargs = argdict.get(p.argspec.varargs, [])
+ collision = set(p.argspec.args) & set(kwargs)
+ if collision:
+ p.error('colliding keyword arguments: %s' % ' '.join(collision))
+ result = p.func(*(args + varargs), **kwargs)
+ if result is None:
+ return []
+ elif hasattr(result, '__iter__') and not isinstance(result, str):
+ return [str(x) for x in result]
+ else:
+ return [str(result)]
+
+def call(obj, arglist=sys.argv[1:]):
+ """
+ If obj is a function or a bound method, parses the given arglist
+ by using an argument parser inferred from the annotations of obj
+ and then calls obj with the parsed arguments.
+ If obj is an object with attribute .commands, builds a multiparser
+ and dispatches to the associated subparsers.
+ Return a list of strings.
+ """
+ return parser_from(obj).parselist(arglist)
diff --git a/plac/plac_ext.py b/plac/plac_ext.py
new file mode 100644
index 0000000..6c904f2
--- /dev/null
+++ b/plac/plac_ext.py
@@ -0,0 +1,156 @@
+import os, sys, cmd, shlex
+import plac
+
+def cmd_interface(obj):
+ "Returns a cmd.Cmd wrapper over the command container"
+ dic = {}
+ for command in obj.commands:
+ method = getattr(obj, command)
+ def do_func(self, line, command=command):
+ args = [command] + shlex.split(line)
+ try:
+ for output in plac.call(obj, args):
+ print(output)
+ except SystemExit:
+ print(e)
+ except Exception:
+ print('%s: %s' % (e.__class__.__name__, e))
+ do_func.__doc__ = method.__doc__
+ if sys.version >= '2.4':
+ do_func.__name__ = method.__name__
+ dic['do_' + command] = do_func
+ clsname = '_%s_' % obj.__class__.__name__
+ cls = type(clsname, (cmd.Cmd, object), dic)
+ return cls()
+
+# requires Python 2.5+
+class Interpreter(object):
+ """
+ The safety_net is a function taking a parsing function and a list of
+ arguments and applying the first to the second by managing some class
+ of exceptions.
+ """
+ def __init__(self, obj, safety_net=lambda parse, arglist: parse(arglist),
+ commentchar='#'):
+ self.obj = obj
+ self.safety_net = safety_net
+ self.commentchar = commentchar
+ self.interpreter = None
+ self.p = plac.parser_from(obj)
+ self.p.error = lambda msg: sys.exit(msg) # patch the parser
+
+ def __enter__(self):
+ self.interpreter = self._make_interpreter()
+ self.interpreter.send(None)
+ return self
+
+ def send(self, line):
+ """
+ Send a line to the underlying interpreter.
+ Return a string or None for comment lines.
+ The line should end with a newline.
+ """
+ return self.interpreter.send(line)
+
+ def close(self):
+ self.interpreter.close()
+
+ def __exit__(self, *exc):
+ self.close()
+
+ def _make_interpreter(self):
+ enter = getattr(self.obj, '__enter__', lambda : None)
+ exit = getattr(self.obj, '__exit__', lambda a1, a2, a3: None)
+ enter()
+ result = None
+ prefix = self.p.short_prefix
+ try:
+ while True:
+ line = yield result
+ if not line:
+ break
+ elif line.startswith(self.commentchar):
+ yield; continue
+ arglist = shlex.split(line)
+ for i, long_opt in enumerate(arglist):
+ # avoid double prefix in long options
+ if len(long_opt) > 2 and long_opt[0] == prefix:
+ arglist[i] = prefix + long_opt
+ try:
+ output = self.safety_net(self.p.parselist, arglist)
+ except SystemExit, e:
+ output = [str(e)]
+ result = os.linesep.join(output)
+ except:
+ exit(*sys.exc_info())
+ raise
+ else:
+ exit(None, None, None)
+
+## from cmd.py
+# def complete(self, text, state):
+# """Return the next possible completion for 'text'.
+
+# If a command has not been entered, then complete against command list.
+# Otherwise try to call complete_<command> to get list of completions.
+# """
+# if state == 0:
+# import readline
+# origline = readline.get_line_buffer()
+# line = origline.lstrip()
+# stripped = len(origline) - len(line)
+# begidx = readline.get_begidx() - stripped
+# endidx = readline.get_endidx() - stripped
+# if begidx>0:
+# cmd, args, foo = self.parseline(line)
+# if cmd == '':
+# compfunc = self.completedefault
+# else:
+# try:
+# compfunc = getattr(self, 'complete_' + cmd)
+# except AttributeError:
+# compfunc = self.completedefault
+# else:
+# compfunc = self.completenames
+# self.completion_matches = compfunc(text, line, begidx, endidx)
+# try:
+# return self.completion_matches[state]
+# except IndexError:
+# return None
+
+# def readlines(completekey='tab'):
+# if readline:
+# old_completer = readline.get_completer()
+# readline.set_completer(self.complete)
+# readline.parse_and_bind(self.completekey + ": complete")
+# try:
+# while True:
+# try:
+# yield raw_input('cli> ')
+# except EOFError:
+# break
+# finally:
+# if readline:
+# readline.set_completer(old_completer)
+
+class Cmds(object):
+ commands = 'checkout', 'commit', 'status', 'help'
+ quit = False
+
+ @plac.annotations(
+ name=('a recognized command', 'positional', None, str, commands))
+ def help(self, name):
+ return self.p.subp[name].format_help()
+
+ def checkout(self, url):
+ return ('checkout', url)
+
+ def commit(self):
+ return ('commit')
+
+ @plac.annotations(quiet=('summary information', 'flag'))
+ def status(self, quiet):
+ return ('status', quiet)
+
+if __name__ == '__main__':
+ cmdloop(Cmds())
diff --git a/plac/setup.py b/plac/setup.py
index ca34c5b..0a7ee88 100644
--- a/plac/setup.py
+++ b/plac/setup.py
@@ -3,18 +3,23 @@ try:
except ImportError:
from distutils.core import setup
import os.path
-import plac
+
+def getversion(fname):
+ "Get the __version__ without importing plac"
+ for line in open(fname):
+ if line.startswith('__version__'):
+ return eval(line[13:])
if __name__ == '__main__':
- setup(name=plac.__name__,
- version=plac.__version__,
+ setup(name='plac',
+ version=getversion(os.path.join(os.path.dirname(__file__),'plac.py')),
description='The easiest command line arguments parser in the world',
long_description=open('README.txt').read(),
author='Michele Simionato',
author_email='michele.simionato@gmail.com',
url='http://pypi.python.org/pypi/plac',
license="BSD License",
- py_modules = ['plac'],
+ py_modules = ['plac_core', 'plac_ext', 'plac'],
install_requires=['argparse>=1.1'],
keywords="command line arguments parser",
platforms=["All"],
diff --git a/plac/test_plac.py b/plac/test_plac.py
index c20a275..3cfd936 100644
--- a/plac/test_plac.py
+++ b/plac/test_plac.py
@@ -25,12 +25,13 @@ def parser_from(f, **kw):
def check_help(name):
sys.argv[0] = name + '.py'
dic = {}
- try:
- execfile(os.path.join('doc', name + '.py'), dic)
- except NameError: # Python 3
+ if sys.version >= '3':
exec(open(os.path.join('doc', name + '.py')).read(), dic)
- except SyntaxError: # raised by some tests when using Python 2
- return
+ else: # Python 2.X
+ try:
+ execfile(os.path.join('doc', name + '.py'), dic)
+ except SyntaxError: # raised by some tests when using Python 2
+ return
p = plac.parser_from(dic['main'])
expected = open(os.path.join('doc', name + '.help')).read().strip()
got = p.format_help().strip()
@@ -105,16 +106,16 @@ def test_kwargs():
return args, kw
main.__annotations__ = dict(opt=('Option', 'option'))
argskw = plac.call(main, ['arg1', 'arg2', 'a=1', 'b=2'])
- assert argskw == (('arg2',), dict(a='1', b='2')), argskw
+ assert argskw == ["('arg2',)", "{'a': '1', 'b': '2'}"], argskw
- argskw = plac.call(main, ['arg1', 'arg2', 'a=1', '-o2'])
- assert argskw == (('arg2',), dict(a='1')), argskw
+ argskw = plac.call(main, ['arg1', 'arg2', 'a=1', '-o', '2'])
+ assert argskw == ["('arg2',)", "{'a': '1'}"], argskw
expect(SystemExit, plac.call, main, ['arg1', 'arg2', 'a=1', 'opt=2'])
def test_expected_help():
for fname in os.listdir('doc'):
- if fname.endswith('.help'):
+ if fname.endswith('.help') and fname != 'vcs.help':
yield check_help, fname[:-5]
class Cmds(object):
@@ -126,10 +127,16 @@ class Cmds(object):
def test_cmds():
cmds = Cmds()
- assert 'commit' == plac.call(cmds, ['commit'])
- assert 'help', 'foo' == plac.call(cmds, ['help', 'foo'])
+ assert ['commit'] == plac.call(cmds, ['commit'])
+ assert ['help', 'foo'] == plac.call(cmds, ['help', 'foo'])
expect(SystemExit, plac.call, cmds, [])
+def test_yield():
+ def main():
+ for i in (1, 2, 3):
+ yield i
+ assert plac.call(main, []) == ['1', '2', '3']
+
if __name__ == '__main__':
n = 0
for name, test in sorted(globals().items()):