summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-06-20 11:00:47 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-06-20 11:00:47 +0200
commit6c431c39155f514f18bc9690ae51634528c47691 (patch)
tree6dfc0e78b19c115ab0c3e7056a79707fe4513201
parent00b953f00515c154dc982cd572f96eedd57558bf (diff)
downloadmicheles-6c431c39155f514f18bc9690ae51634528c47691.tar.gz
Major commit before plac 0.5plac-0.5.0
-rw-r--r--plac/CHANGES.txt8
-rw-r--r--plac/MANIFEST.in2
-rw-r--r--plac/doc/cmd_ex.py6
-rw-r--r--plac/doc/example10.py10
-rw-r--r--plac/doc/ishelve.plac (renamed from plac/doc/ishelve.batch)0
-rw-r--r--plac/doc/ishelve.py5
-rw-r--r--plac/doc/ishelve2.plac4
-rw-r--r--plac/doc/ishelve2.placet9
-rw-r--r--plac/doc/plac.html302
-rw-r--r--plac/doc/plac.pdf3513
-rw-r--r--plac/doc/plac.txt223
-rw-r--r--plac/doc/plac_adv.html695
-rw-r--r--plac/doc/plac_adv.pdf3542
-rw-r--r--plac/doc/plac_adv.txt547
-rw-r--r--plac/doc/shelve_interpreter.py1
-rw-r--r--plac/doc/test_ishelve.py14
-rw-r--r--plac/doc/test_plac.py28
-rw-r--r--plac/plac.py20
-rw-r--r--plac/plac_core.py150
-rw-r--r--plac/plac_ext.py24
20 files changed, 5251 insertions, 3852 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt
index 255737d..fc42bf1 100644
--- a/plac/CHANGES.txt
+++ b/plac/CHANGES.txt
@@ -1,10 +1,10 @@
HISTORY
----------
-0.5.0 Gigantic release. Introduced smart options, removed the default
- formatter class and added an Interpreter class. Made the split
- plac_code and plac_ext and added a plac runner script, able to
- run scripts, batch files and doctests (2010-06-XX).
+0.5.0 Gigantic release. Introduced smart options, added an Interpreter class
+ and the command container concept. Made the split plac/plac_core/plac_ext
+ and added a plac runner, able to run scripts, batch files and doctests.
+ Removed the default formatter class (2010-06-20).
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
diff --git a/plac/MANIFEST.in b/plac/MANIFEST.in
index e4c6c2d..0f93421 100644
--- a/plac/MANIFEST.in
+++ b/plac/MANIFEST.in
@@ -1 +1 @@
-include test_plac.py *.txt doc/*.py doc/*.help doc/*.txt doc/*.html doc/*.pdf
+include *.txt doc/*.py doc/*.help doc/*.txt doc/*.html doc/*.pdf
diff --git a/plac/doc/cmd_ex.py b/plac/doc/cmd_ex.py
new file mode 100644
index 0000000..4af18c4
--- /dev/null
+++ b/plac/doc/cmd_ex.py
@@ -0,0 +1,6 @@
+# cmd_ext.py
+from plac_ext import cmd_interface
+import ishelve2
+
+if __name__ == '__main__':
+ cmd_interface(ishelve2.main()).cmdloop()
diff --git a/plac/doc/example10.py b/plac/doc/example10.py
index ac64db9..02bbdc6 100644
--- a/plac/doc/example10.py
+++ b/plac/doc/example10.py
@@ -6,11 +6,15 @@ operator=("The name of an operator", 'positional', None, str, ['add', 'mul']),
numbers=("A number", 'positional', None, float, None, "n"))
def main(operator, *numbers):
"A script to add and multiply numbers"
- op = getattr(float, '__%s__' % operator)
- result = dict(add=0.0, mul=1.0)[operator]
+ if operator == 'mul':
+ op = float.__mul__
+ result = 1.0
+ else: # operator == 'add'
+ op = float.__add__
+ result = 0.0
for n in numbers:
result = op(result, n)
return result
if __name__ == '__main__':
- print(plac.call(main)[0])
+ print(plac.call(main))
diff --git a/plac/doc/ishelve.batch b/plac/doc/ishelve.plac
index ef378b3..ef378b3 100644
--- a/plac/doc/ishelve.batch
+++ b/plac/doc/ishelve.plac
diff --git a/plac/doc/ishelve.py b/plac/doc/ishelve.py
index 812b7ce..343b4ed 100644
--- a/plac/doc/ishelve.py
+++ b/plac/doc/ishelve.py
@@ -1,6 +1,5 @@
# ishelve.py
-import os, shelve
-import plac
+import os, shelve, plac
DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
@@ -13,7 +12,7 @@ DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
params='names of the parameters in the shelve',
setters='setters param=value')
def main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
- *params, **setters):
+ *params, **setters):
"A simple interface to a shelve"
sh = shelve.open(filename)
try:
diff --git a/plac/doc/ishelve2.plac b/plac/doc/ishelve2.plac
new file mode 100644
index 0000000..7d6ea02
--- /dev/null
+++ b/plac/doc/ishelve2.plac
@@ -0,0 +1,4 @@
+#!ishelve2.py -c ~/conf.shelve
+set a 1
+del a
+del a # intentional error
diff --git a/plac/doc/ishelve2.placet b/plac/doc/ishelve2.placet
new file mode 100644
index 0000000..a41fc8d
--- /dev/null
+++ b/plac/doc/ishelve2.placet
@@ -0,0 +1,9 @@
+#!ishelve2.py -configfile=~/test.shelve
+i> del
+deleting everything
+i> set a 1
+setting a=1
+i> set b 2
+setting b=2
+i> show b
+b = 2
diff --git a/plac/doc/plac.html b/plac/doc/plac.html
index 87e8840..da6cbad 100644
--- a/plac/doc/plac.html
+++ b/plac/doc/plac.html
@@ -6,6 +6,7 @@
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title>Plac: Parsing the Command Line the Easy Way</title>
<meta name="author" content="Michele Simionato" />
+<meta name="date" content="June 2010" />
<style type="text/css">
.first {
@@ -422,12 +423,14 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<td>Michele Simionato</td></tr>
<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
</tr>
-<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.3+</td>
-</tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>June 2010</td></tr>
<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/plac">http://pypi.python.org/pypi/plac</a></td>
</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">Requires:</th><td class="field-body">Python 2.3+</td>
+</tr>
<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install <span class="pre">-U</span> plac</tt></td>
</tr>
<tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td>
@@ -446,10 +449,11 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<li><a class="reference internal" href="#more-features" id="id7">More features</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>
+<li><a class="reference internal" href="#final-example-a-shelve-interface" id="id10">Final example: a shelve interface</a></li>
+<li><a class="reference internal" href="#plac-vs-argparse" id="id11">plac vs argparse</a></li>
+<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id12">plac vs the rest of the world</a></li>
+<li><a class="reference internal" href="#the-future" id="id13">The future</a></li>
+<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id14">Trivia: the story behind the name</a></li>
</ul>
</div>
<div class="section" id="the-importance-of-scaling-down">
@@ -460,13 +464,13 @@ world. The standard library alone contains three different modules:
<a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> (from Python 2.3) and <a class="reference external" href="http://argparse.googlecode.com">argparse</a> (from Python 2.7). All of
them are quite powerful and especially <a class="reference external" href="http://argparse.googlecode.com">argparse</a> is an industrial
strength solution; unfortunately, all of them feature a non-zero learning
-curve and a certain verbosity. They do not scale down well enough, at
+curve and a certain verbosity. They do not scale down well, at
least in my opinion.</p>
<p>It should not be necessary to stress the importance <a class="reference external" href="http://www.welton.it/articles/scalable_systems">scaling down</a>;
-nevertheless most people are obsessed with features and concerned with
-the possibility of scaling up, whereas I think that we should be even
-more concerned with the issue of scaling down. This is an old meme in
-the computing world: programs should address the common cases simply,
+nevertheless, a lot of people are obsessed with features and concerned with
+the possibility of scaling up, forgetting the equally important
+issue of scaling down. This is an old meme in
+the computing world: programs should address the common cases simply and
simple things should be kept simple, while at the same keeping
difficult things possible. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> adhere as much as possible to this
philosophy and it is designed to handle well the simple cases, while
@@ -486,7 +490,7 @@ throw-away scripts for themselves, choosing the command line
interface because it is the quick and simple. Such users are not
interested in features, they are interested in a small learning curve:
they just want to be able to write a simple command line tool from a
-simple specification, not to build a command line parser by
+simple specification, not to build a command-line parser by
hand. Unfortunately, the modules in the standard library forces them
to go the hard way. They are designed to implement power user tools
and they have a non-trivial learning curve. On the contrary, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
@@ -497,7 +501,7 @@ below will show.</p>
<h1><a class="toc-backref" href="#id2">Scripts with required arguments</a></h1>
<p>Let me start with the simplest possible thing: a script that takes a
single argument and does something to it. It cannot get simpler
-than that, unless you consider a script without command line
+than that, unless you consider a script without command-line
arguments, where there is nothing to parse. Still, it is a use
case <em>extremely common</em>: I need to write scripts like that nearly
every day, I wrote hundreds of them in the last few years and I have
@@ -522,9 +526,9 @@ if __name__ == '__main__':
</pre>
<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (nine lines)
-is essentially boilerplate that should not exists. Actually I think
+is essentially boilerplate that should not exist. Actually I think
the language should recognize the main function and pass to it the
-command line arguments automatically; unfortunaly this is unlikely to
+command-line arguments automatically; unfortunaly this is unlikely to
happen. I have been writing boilerplate like this in hundreds of
scripts for years, and every time I <em>hate</em> it. The purpose of using a
scripting language is convenience and trivial things should be
@@ -584,11 +588,12 @@ 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>
+<p>Moreover <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> manages the case of missing arguments and of too many arguments.
+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>
<div class="section" id="scripts-with-default-arguments">
<h1><a class="toc-backref" href="#id3">Scripts with default arguments</a></h1>
-<p>The need to have suitable defaults for command line arguments is quite
+<p>The need to have suitable defaults for command-line scripts is quite
common. For instance I have encountered this use case at work hundreds
of times:</p>
<pre class="literal-block">
@@ -610,10 +615,10 @@ 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.
+most recent 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>)
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
+of the command-line 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>
<pre class="literal-block">
@@ -668,14 +673,14 @@ optional arguments:
</pre>
<p>The examples here should have made clear that <em>plac is able to figure out
-the command line arguments parser to use from the signature of the main
+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">main</tt>
-function.</p>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to an old Python Cookbook recipe (<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a>), 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>
@@ -713,13 +718,13 @@ I will show in the next paragraphs.</p>
</div>
<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
+<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
than a thousand of them). Still, this use case cannot be neglected.
The standard library modules (all of them) are quite verbose when it
comes to specifying the options and frankly I have never used them
-directly. Instead, I have always relied on an old recipe of mine, the
+directly. Instead, I have always relied on 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. In <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the parser is inferred by the
@@ -739,7 +744,7 @@ if __name__ == '__main__':
<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 tells <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 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>.
+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
@@ -892,7 +897,7 @@ think to migrate to Python 3. I am pretty much sure most Pythonistas
are in the same situation. Therefore <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides a way to work
with function annotations even in Python 2.X (including Python 2.3).
There is no magic involved; you just need to add the annotations
-by hand. For instance the annotate function declaration</p>
+by hand. For instance the annotated function declaration</p>
<pre class="literal-block">
def main(dsn: &quot;Database dsn&quot;, *scripts: &quot;SQL scripts&quot;):
...
@@ -918,7 +923,7 @@ def main(dsn, *scripts):
</pre>
<p>In the rest of this article I will assume that you are using Python 2.X with
X &gt;= 4 and I will use the <tt class="docutils literal">plac.annotations</tt> decorator. Notice however
-that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> runs even on Python 2.3.</p>
+that the core features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> run even on Python 2.3.</p>
</div>
<div class="section" id="more-features">
<h1><a class="toc-backref" href="#id7">More features</a></h1>
@@ -932,7 +937,8 @@ general an annotation is a 6-tuple of the form</p>
<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,
+one-character string or <tt class="docutils literal">None</tt>, <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
from the string type to any Python type; by default there is no
@@ -944,7 +950,7 @@ options; by default there is no restriction i.e. <tt class="docutils literal">ch
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>
-<p>Here is an example showing many of the features (taken from the
+<p>Here is an example showing many of the features (copied from the
<a class="reference external" href="http://argparse.googlecode.com">argparse</a> documentation):</p>
<pre class="literal-block">
# example10.py
@@ -955,23 +961,20 @@ operator=(&quot;The name of an operator&quot;, 'positional', None, str, ['add',
numbers=(&quot;A number&quot;, 'positional', None, float, None, &quot;n&quot;))
def main(operator, *numbers):
&quot;A script to add and multiply numbers&quot;
- op = getattr(float, '__%s__' % operator)
- result = dict(add=0.0, mul=1.0)[operator]
+ if operator == 'mul':
+ op = float.__mul__
+ result = 1.0
+ else: # operator == 'add'
+ op = float.__add__
+ result = 0.0
for n in numbers:
result = op(result, n)
return result
if __name__ == '__main__':
- print(plac.call(main)[0])
+ print(plac.call(main))
</pre>
-<p>Often the main function of a script works by side effects and returns
-<tt class="docutils literal">None</tt>; in this example instead I choose to return the number and to
-print it in the <tt class="docutils literal">__main__</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">None</tt>.</p>
<p>Here is the usage:</p>
<pre class="literal-block">
usage: example10.py [-h] {add,mul} [n [n ...]]
@@ -997,17 +1000,11 @@ $ 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>
-<p>If the main function returns a generic number of elements,
-the elements returned by <tt class="docutils literal">plac.call</tt> are stringified by invoking
-<tt class="docutils literal">str</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>
+<p><tt class="docutils literal">plac.call</tt> can also be used in doctests like this:</p>
<pre class="doctest-block">
-&gt;&gt;&gt; import example10
+&gt;&gt;&gt; import plac, example10
&gt;&gt;&gt; plac.call(example10.main, ['add', '1', '2'])
-['3.0']
+3.0
</pre>
<p><tt class="docutils literal">plac.call</tt> works for generators too:</p>
<pre class="doctest-block">
@@ -1015,14 +1012,27 @@ For instance a doctest may look like this:</p>
... for i in range(int(n)):
... yield i
&gt;&gt;&gt; plac.call(main, ['3'])
-['0', '1', '2']
+[0, 1, 2]
</pre>
-<p>However, you should notice that <tt class="docutils literal">plac.call</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">list(result)</tt> to the result of a <tt class="docutils literal">plac.call</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">print</tt> with <tt class="docutils literal">yield</tt> in
-the main function and you will get an easy to test interface.</p>
+<p>Internally <tt class="docutils literal">plac.call</tt> tries to convert the output of the main function
+into a list, if possible. If the output is not iterable or it is a
+string, it is left unchanged, but if it is iterable it is converted.
+In particular, generator objects are exhausted by <tt class="docutils literal">plac.call</tt>.</p>
+<p>This behavior avoids mistakes like forgetting of applying
+<tt class="docutils literal">list(result)</tt> to the result of <tt class="docutils literal">plac.call</tt>; moreover it makes
+errors visible early, and avoids mistakes in code like the following:</p>
+<pre class="literal-block">
+try:
+ result = plac.call(main, args)
+except:
+ # do something
+</pre>
+<p>Without the <tt class="docutils literal">listify</tt> functionality, a main function returning a
+generator object would not raise any exception until the generator
+is iterated over.</p>
+<p>Moreover you can rely on type checks like <tt class="docutils literal">isinstance(result, list)</tt>
+for the output of <tt class="docutils literal">plac.call</tt>, to check if the output is an iterable
+or not.</p>
</div>
<div class="section" id="a-realistic-example">
<h1><a class="toc-backref" href="#id8">A realistic example</a></h1>
@@ -1063,12 +1073,12 @@ if __name__ == '__main__':
</pre>
<p>You can see the <em>yield-is-print</em> pattern here: instead of using
-<tt class="docutils literal">print</tt> in the main function, we use <tt class="docutils literal">yield</tt>, and we perform the
+<tt class="docutils literal">print</tt> in the main function, I use <tt class="docutils literal">yield</tt>, and I perform the
print in the <tt class="docutils literal">__main__</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">sys.stdout</tt> to a
-<tt class="docutils literal">StringIO</tt> object and we know that redirecting <tt class="docutils literal">sys.stdout</tt> is
-always ugly, especially in multithreaded situations.</p>
+tests invoking <tt class="docutils literal">plac.call</tt> and checking the result become trivial:
+had I performed the printing in the main function, the test would have
+involved an ugly hack like redirecting <tt class="docutils literal">sys.stdout</tt> to a
+<tt class="docutils literal">StringIO</tt> object.</p>
<p>Here is the usage message:</p>
<pre class="literal-block">
usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
@@ -1086,8 +1096,7 @@ optional arguments:
-d |, --delimiter | Column separator
</pre>
-<p>I leave as an exercise for the reader to write doctests for this
-example.</p>
+<p>You can check for yourself that the script works.</p>
</div>
<div class="section" id="keyword-arguments">
<h1><a class="toc-backref" href="#id9">Keyword arguments</a></h1>
@@ -1149,8 +1158,130 @@ 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="final-example-a-shelve-interface">
+<h1><a class="toc-backref" href="#id10">Final example: a shelve interface</a></h1>
+<p>Here is a less trivial example for the keyword arguments feature.
+The use case is the following: suppose we have stored the
+configuration parameters of a given application into a Python shelve
+and we need a command-line tool to edit the shelve.
+A possible implementation using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> could be the following:</p>
+<pre class="literal-block">
+# ishelve.py
+import os, shelve, plac
+
+DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
+
+&#64;plac.annotations(
+ help=('show help', 'flag'),
+ showall=('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 main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
+ *params, **setters):
+ &quot;A simple interface to a shelve&quot;
+ sh = shelve.open(filename)
+ try:
+ if not any([help, showall, clear, delete, params, setters]):
+ yield 'no arguments passed, use .help to see the available commands'
+ elif help: # custom help
+ yield 'Commands: .help, .showall, .clear, .delete'
+ yield '&lt;param&gt; ...'
+ yield '&lt;param=value&gt; ...'
+ elif showall:
+ 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()
+
+main.add_help = False # there is a custom help, remove the default one
+main.prefix_chars = '.' # use dot-prefixed commands
+
+if __name__ == '__main__':
+ for output in plac.call(main):
+ print(output)
+
+</pre>
+<p>A few notes are in order:</p>
+<ol class="arabic simple">
+<li>I have disabled the ordinary help provided by <a class="reference external" href="http://argparse.googlecode.com">argparse</a> and I have
+implemented a custom help command.</li>
+<li>I have changed the prefix character used to recognize the options
+to a dot.</li>
+<li>Keyword arguments recognition (in the <tt class="docutils literal">**setters</tt>) is used to make it
+possible to store a value in the shelve with the syntax
+<tt class="docutils literal">param_name=param_value</tt>.</li>
+<li><tt class="docutils literal">*params</tt> are used to retrieve parameters from the shelve and some
+error checking is performed in the case of missing parameters</li>
+<li>A command to clear the shelve is implemented as a flag (<tt class="docutils literal">.clear</tt>).</li>
+<li>A command to delete a given parameter is implemented as an option
+(<tt class="docutils literal">.delete</tt>).</li>
+<li>There is an option with default (<tt class="docutils literal">.filename=conf.shelve</tt>) to store
+the filename of the shelve.</li>
+<li>All things considered, the code looks like a poor man object oriented
+interface implemented with a chain of elifs instead of methods. Of course,
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can do better than that, but let me start from a low-level approach
+first.</li>
+</ol>
+<p>If you run <tt class="docutils literal">ishelve.py</tt> without arguments you get the following
+message:</p>
+<pre class="literal-block">
+$ python ishelve.py
+no arguments passed, use .help to see the available commands
+</pre>
+<p>If you run <tt class="docutils literal">ishelve.py</tt> with the option <tt class="docutils literal">.h</tt> (or any abbreviation
+of <tt class="docutils literal">.help</tt>) you get:</p>
+<pre class="literal-block">
+$ python ishelve.py .h
+Commands: .help, .showall, .clear, .delete
+&lt;param&gt; ...
+&lt;param=value&gt; ...
+</pre>
+<p>You can check by hand that the tool work:</p>
+<pre class="literal-block">
+$ python ishelve.py .clear # start from an empty shelve
+cleared the shelve
+$ python ishelve.py a=1 b=2
+setting a=1
+setting b=2
+$ python ishelve.py .showall
+b=2
+a=1
+$ python ishelve.py .del b # abbreviation for .delete
+deleted b
+$ python ishelve.py a
+1
+$ python ishelve.py b
+b: not found
+$ python ishelve.py .cler # mispelled command
+usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
+ [.filename /home/micheles/conf.shelve]
+ [params [params ...]] [setters [setters ...]]
+ishelve.py: error: unrecognized arguments: .cler
+</pre>
+</div>
<div class="section" id="plac-vs-argparse">
-<h1><a class="toc-backref" href="#id10">plac vs argparse</a></h1>
+<h1><a class="toc-backref" href="#id11">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
@@ -1167,7 +1298,8 @@ option, or to use <a class="reference external" href="http://argparse.googlecode
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support &quot;required options&quot;. As the <a class="reference external" href="http://argparse.googlecode.com">argparse</a>
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>
+the use of required options whenever possible.</em> Notice that since
+<a class="reference external" href="http://argparse.googlecode.com">argparse</a> supports them, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can manage them too, but not directly.</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
@@ -1221,8 +1353,8 @@ main.prefix_chars='/-'
<p>The first prefix char (<tt class="docutils literal">/</tt>) is used
as the default for the recognition of options and flags;
the second prefix char (<tt class="docutils literal">-</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.
-An example will be given in the <a class="reference external" href="in-writing">advanced usage document</a> .</p>
+working: however you can disable it and reimplement it, if you like,
+as seen in the <tt class="docutils literal">ishelve</tt> example.</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">
@@ -1230,13 +1362,11 @@ invoking the <tt class="docutils literal">plac.parser_from</tt> utility function
&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)
+&gt;&gt;&gt; print(plac.parser_from(main)) #doctest: +ELLIPSIS
+ArgumentParser(prog=...)
</pre>
<p>Internally <tt class="docutils literal">plac.call</tt> uses <tt class="docutils literal">plac.parser_from</tt> and adds the parser
-as an attribute <tt class="docutils literal">.p</tt>. When <tt class="docutils literal">plac.call(func)</tt> is
+to the main function as an attribute. When <tt class="docutils literal">plac.call(func)</tt> is
invoked multiple time, the parser is re-used and not rebuilt from scratch again.</p>
<p>I use <tt class="docutils literal">plac.parser_from</tt> in the unit tests of the module, but regular
users should not need to use it, unless they want to access <em>all</em>
@@ -1249,7 +1379,7 @@ adding such options to the <tt class="docutils literal">main</tt> function for y
attribute.</p>
</div>
<div class="section" id="plac-vs-the-rest-of-the-world">
-<h1><a class="toc-backref" href="#id11">plac vs the rest of the world</a></h1>
+<h1><a class="toc-backref" href="#id12">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
@@ -1268,19 +1398,28 @@ 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="#id12">The future</a></h1>
+<h1><a class="toc-backref" href="#id13">The future</a></h1>
<p>Currently the core of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is around 200 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 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 in alpha status, especially for what concerns the
-features described in the <a class="reference external" href="in-writing">advanced usage document</a>, which are
-implemented in a separated module (<tt class="docutils literal">plac_ext.py</tt>).</p>
+considered in alpha status.</p>
+<p>Notice that even if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> has been designed to be simple to use for
+simple stuff, its power should not be underestimated; it is actually a
+quite advanced tool with a domain of applicability which far exceeds
+the realm of command-line arguments parsers.</p>
+<p>Version 0.5 of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> doubled the code base and the documentation: it is
+based on the idea of using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to implement command-line interpreters,
+i.e. something akin to the <tt class="docutils literal">cmd</tt> module in the standard library, only better.
+The new features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> are described in the <a class="reference external" href="in-writing">advanced usage document</a> .
+They are implemented in a separated module (<tt class="docutils literal">plac_ext.py</tt>), since
+they require Python 2.5 to work, whereas <tt class="docutils literal">plac_core.py</tt> only requires
+Python 2.3.</p>
</div>
<div class="section" id="trivia-the-story-behind-the-name">
-<h1><a class="toc-backref" href="#id13">Trivia: the story behind the name</a></h1>
+<h1><a class="toc-backref" href="#id14">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
@@ -1305,9 +1444,6 @@ a <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap
<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>Version 0.5 of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> doubled the code base and the documentation: it is
-based on the idea of using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to implement command-line interpreters,
-i.e. something like the <tt class="docutils literal">cmd</tt> module in the standard library, only better.</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>
diff --git a/plac/doc/plac.pdf b/plac/doc/plac.pdf
index 3fbb344..5e15047 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 50 0 R >>
+ /F5 52 0 R >>
endobj
% 'F1': class PDFType1Font
2 0 obj
@@ -96,15 +96,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 46 0 R
+ /Dest [ 48 0 R
/XYZ
62.69291
- 329.0236
+ 296.0236
0 ]
/Rect [ 62.69291
- 563.5936
+ 548.5936
215.5029
- 575.5936 ]
+ 560.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -114,15 +114,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 46 0 R
+ /Dest [ 48 0 R
/XYZ
62.69291
- 329.0236
+ 296.0236
0 ]
/Rect [ 527.0227
- 563.5936
+ 548.5936
532.5827
- 575.5936 ]
+ 560.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -132,15 +132,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 54 0 R
+ /Dest [ 56 0 R
/XYZ
62.69291
- 627.0236
+ 615.0236
0 ]
/Rect [ 62.69291
- 545.5936
+ 530.5936
216.0629
- 557.5936 ]
+ 542.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -150,15 +150,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 54 0 R
+ /Dest [ 56 0 R
/XYZ
62.69291
- 627.0236
+ 615.0236
0 ]
/Rect [ 527.0227
- 545.5936
+ 530.5936
532.5827
- 557.5936 ]
+ 542.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -168,15 +168,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 60 0 R
+ /Dest [ 63 0 R
/XYZ
62.69291
- 298.2236
+ 274.2236
0 ]
/Rect [ 62.69291
- 527.5936
+ 512.5936
208.8329
- 539.5936 ]
+ 524.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -186,15 +186,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 60 0 R
+ /Dest [ 63 0 R
/XYZ
62.69291
- 298.2236
+ 274.2236
0 ]
/Rect [ 527.0227
- 527.5936
+ 512.5936
532.5827
- 539.5936 ]
+ 524.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -204,15 +204,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 72 0 R
+ /Dest [ 75 0 R
/XYZ
62.69291
- 253.4236
+ 229.4236
0 ]
/Rect [ 62.69291
- 509.5936
+ 494.5936
254.3829
- 521.5936 ]
+ 506.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -222,15 +222,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 72 0 R
+ /Dest [ 75 0 R
/XYZ
62.69291
- 253.4236
+ 229.4236
0 ]
/Rect [ 527.0227
- 509.5936
+ 494.5936
532.5827
- 521.5936 ]
+ 506.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -240,15 +240,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 76 0 R
+ /Dest [ 79 0 R
/XYZ
62.69291
- 251.0236
+ 231.0236
0 ]
/Rect [ 62.69291
- 491.5936
+ 476.5936
145.4929
- 503.5936 ]
+ 488.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -258,15 +258,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 76 0 R
+ /Dest [ 79 0 R
/XYZ
62.69291
- 251.0236
+ 231.0236
0 ]
/Rect [ 527.0227
- 491.5936
+ 476.5936
532.5827
- 503.5936 ]
+ 488.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -276,15 +276,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 78 0 R
+ /Dest [ 81 0 R
/XYZ
62.69291
- 401.4236
+ 377.4236
0 ]
/Rect [ 62.69291
- 473.5936
+ 458.5936
182.7329
- 485.5936 ]
+ 470.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -294,15 +294,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 78 0 R
+ /Dest [ 81 0 R
/XYZ
62.69291
- 401.4236
+ 377.4236
0 ]
/Rect [ 527.0227
- 473.5936
+ 458.5936
532.5827
- 485.5936 ]
+ 470.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -312,15 +312,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 86 0 R
+ /Dest [ 89 0 R
/XYZ
62.69291
647.8236
0 ]
/Rect [ 62.69291
- 455.5936
+ 440.5936
128.2629
- 467.5936 ]
+ 452.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -330,15 +330,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 86 0 R
+ /Dest [ 89 0 R
/XYZ
62.69291
647.8236
0 ]
/Rect [ 527.0227
- 455.5936
+ 440.5936
532.5827
- 467.5936 ]
+ 452.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -348,15 +348,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 90 0 R
+ /Dest [ 94 0 R
/XYZ
62.69291
- 218.0849
+ 765.0236
0 ]
/Rect [ 62.69291
- 437.5936
+ 422.5936
153.2929
- 449.5936 ]
+ 434.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -366,15 +366,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 90 0 R
+ /Dest [ 94 0 R
/XYZ
62.69291
- 218.0849
+ 765.0236
0 ]
/Rect [ 521.4627
- 437.5936
+ 422.5936
532.5827
- 449.5936 ]
+ 434.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -384,15 +384,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 94 0 R
+ /Dest [ 97 0 R
/XYZ
62.69291
- 765.0236
+ 707.8236
0 ]
/Rect [ 62.69291
- 419.5936
+ 404.5936
158.8229
- 431.5936 ]
+ 416.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -402,15 +402,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 94 0 R
+ /Dest [ 97 0 R
/XYZ
62.69291
- 765.0236
+ 707.8236
0 ]
/Rect [ 521.4627
- 419.5936
+ 404.5936
532.5827
- 431.5936 ]
+ 416.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -420,15 +420,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 116 0 R
+ /Dest [ 99 0 R
/XYZ
62.69291
- 765.0236
+ 683.8236
0 ]
/Rect [ 62.69291
- 401.5936
- 141.6229
- 413.5936 ]
+ 386.5936
+ 218.8729
+ 398.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -438,15 +438,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 116 0 R
+ /Dest [ 99 0 R
/XYZ
62.69291
- 765.0236
+ 683.8236
0 ]
/Rect [ 521.4627
- 401.5936
+ 386.5936
532.5827
- 413.5936 ]
+ 398.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -456,15 +456,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 138 0 R
+ /Dest [ 125 0 R
/XYZ
62.69291
- 420.6236
+ 595.8236
0 ]
/Rect [ 62.69291
- 383.5936
- 194.9529
- 395.5936 ]
+ 368.5936
+ 141.6229
+ 380.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -474,15 +474,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 138 0 R
+ /Dest [ 125 0 R
/XYZ
62.69291
- 420.6236
+ 595.8236
0 ]
/Rect [ 521.4627
- 383.5936
+ 368.5936
532.5827
- 395.5936 ]
+ 380.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -492,15 +492,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 138 0 R
+ /Dest [ 140 0 R
/XYZ
62.69291
- 231.6236
+ 259.4236
0 ]
/Rect [ 62.69291
- 365.5936
- 111.5829
- 377.5936 ]
+ 350.5936
+ 194.9529
+ 362.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -510,15 +510,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 138 0 R
+ /Dest [ 140 0 R
/XYZ
62.69291
- 231.6236
+ 259.4236
0 ]
/Rect [ 521.4627
- 365.5936
+ 350.5936
532.5827
- 377.5936 ]
+ 362.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -528,15 +528,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 152 0 R
+ /Dest [ 162 0 R
/XYZ
62.69291
- 765.0236
+ 741.0236
0 ]
/Rect [ 62.69291
- 347.5936
- 219.9529
- 359.5936 ]
+ 332.5936
+ 111.5829
+ 344.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -546,20 +546,56 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 152 0 R
+ /Dest [ 162 0 R
/XYZ
62.69291
- 765.0236
+ 741.0236
0 ]
/Rect [ 521.4627
- 347.5936
+ 332.5936
532.5827
- 359.5936 ]
+ 344.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER30': class PDFDictionary
+% 'Annot.NUMBER30': class LinkAnnotation
35 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 162 0 R
+ /XYZ
+ 62.69291
+ 534.0236
+ 0 ]
+ /Rect [ 62.69291
+ 314.5936
+ 219.9529
+ 326.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER31': class LinkAnnotation
+36 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 162 0 R
+ /XYZ
+ 62.69291
+ 534.0236
+ 0 ]
+ /Rect [ 521.4627
+ 314.5936
+ 532.5827
+ 326.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER32': class PDFDictionary
+37 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/getopt.html) >>
@@ -567,14 +603,14 @@ endobj
0
0 ]
/Rect [ 214.8914
- 281.5936
+ 248.5936
246.5585
- 293.5936 ]
+ 260.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER31': class PDFDictionary
-36 0 obj
+% 'Annot.NUMBER33': class PDFDictionary
+38 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html) >>
@@ -582,14 +618,14 @@ endobj
0
0 ]
/Rect [ 346.507
- 281.5936
+ 248.5936
389.2842
- 293.5936 ]
+ 260.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER32': class PDFDictionary
-37 0 obj
+% 'Annot.NUMBER34': class PDFDictionary
+39 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -597,14 +633,14 @@ endobj
0
0 ]
/Rect [ 493.1227
- 281.5936
+ 248.5936
531.4956
- 293.5936 ]
+ 260.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER33': class PDFDictionary
-38 0 obj
+% 'Annot.NUMBER35': class PDFDictionary
+40 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -612,59 +648,59 @@ endobj
0
0 ]
/Rect [ 346.384
- 269.5936
+ 236.5936
388.8477
- 281.5936 ]
+ 248.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER34': class PDFDictionary
-39 0 obj
+% 'Annot.NUMBER36': class PDFDictionary
+41 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://www.welton.it/articles/scalable_systems) >>
/Border [ 0
0
0 ]
- /Rect [ 292.1608
- 227.5936
- 350.0128
- 239.5936 ]
+ /Rect [ 311.5097
+ 194.5936
+ 371.5115
+ 206.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER35': class PDFDictionary
-40 0 obj
+% 'Annot.NUMBER37': class PDFDictionary
+42 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 208.2364
- 179.5936
- 229.8923
- 191.5936 ]
+ /Rect [ 62.69291
+ 146.5936
+ 84.90623
+ 158.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER36': class PDFDictionary
-41 0 obj
+% 'Annot.NUMBER38': class PDFDictionary
+43 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 152.7329
- 155.5936
- 192.1929
- 167.5936 ]
+ /Rect [ 453.4229
+ 134.5936
+ 492.8829
+ 146.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER37': class PDFDictionary
-42 0 obj
+% 'Annot.NUMBER39': class PDFDictionary
+44 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -672,14 +708,14 @@ endobj
0
0 ]
/Rect [ 116.9711
- 137.5936
+ 116.5936
139.5794
- 149.5936 ]
+ 128.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER38': class PDFDictionary
-43 0 obj
+% 'Annot.NUMBER40': class PDFDictionary
+45 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -687,14 +723,14 @@ endobj
0
0 ]
/Rect [ 277.9887
- 137.5936
+ 116.5936
321.7169
- 149.5936 ]
+ 128.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER39': class PDFDictionary
-44 0 obj
+% 'Annot.NUMBER41': class PDFDictionary
+46 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -702,14 +738,14 @@ endobj
0
0 ]
/Rect [ 504.0394
- 125.5936
+ 104.5936
525.3627
- 137.5936 ]
+ 116.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER40': class PDFDictionary
-45 0 obj
+% 'Annot.NUMBER42': class PDFDictionary
+47 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -717,14 +753,14 @@ endobj
0
0 ]
/Rect [ 351.0408
- 113.5936
+ 92.59362
390.5008
- 125.5936 ]
+ 104.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page1': class PDFPage
-46 0 obj
+48 0 obj
% Page dictionary
<< /Annots [ 5 0 R
6 0 R
@@ -765,13 +801,15 @@ endobj
42 0 R
43 0 R
44 0 R
- 45 0 R ]
- /Contents 170 0 R
+ 45 0 R
+ 46 0 R
+ 47 0 R ]
+ /Contents 181 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -782,8 +820,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER41': class PDFDictionary
-47 0 obj
+% 'Annot.NUMBER43': class PDFDictionary
+49 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -791,14 +829,14 @@ endobj
0
0 ]
/Rect [ 247.8817
- 756.5936
+ 744.5936
266.2217
- 768.5936 ]
+ 756.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER42': class PDFDictionary
-48 0 obj
+% 'Annot.NUMBER44': class PDFDictionary
+50 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -806,14 +844,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 726.5936
+ 714.5936
85.3538
- 738.5936 ]
+ 726.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER43': class PDFDictionary
-49 0 obj
+% 'Annot.NUMBER45': class PDFDictionary
+51 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -821,14 +859,14 @@ endobj
0
0 ]
/Rect [ 124.2211
- 654.5936
+ 642.5936
146.9252
- 666.5936 ]
+ 654.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'F5': class PDFType1Font
-50 0 obj
+52 0 obj
% Font Helvetica-Oblique
<< /BaseFont /Helvetica-Oblique
/Encoding /WinAnsiEncoding
@@ -836,8 +874,8 @@ endobj
/Subtype /Type1
/Type /Font >>
endobj
-% 'Annot.NUMBER44': class PDFDictionary
-51 0 obj
+% 'Annot.NUMBER46': class PDFDictionary
+53 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/getopt.html) >>
@@ -845,14 +883,14 @@ endobj
0
0 ]
/Rect [ 325.341
- 262.3936
+ 250.3936
356.6198
- 274.3936 ]
+ 262.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER45': class PDFDictionary
-52 0 obj
+% 'Annot.NUMBER47': class PDFDictionary
+54 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html) >>
@@ -860,14 +898,14 @@ endobj
0
0 ]
/Rect [ 376.7786
- 262.3936
+ 250.3936
419.1674
- 274.3936 ]
+ 262.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER46': class PDFDictionary
-53 0 obj
+% 'Annot.NUMBER48': class PDFDictionary
+55 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -875,27 +913,27 @@ endobj
0
0 ]
/Rect [ 365.694
- 250.3936
+ 238.3936
408.8281
- 262.3936 ]
+ 250.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page2': class PDFPage
-54 0 obj
+56 0 obj
% Page dictionary
-<< /Annots [ 47 0 R
- 48 0 R
- 49 0 R
+<< /Annots [ 49 0 R
+ 50 0 R
51 0 R
- 52 0 R
- 53 0 R ]
- /Contents 171 0 R
+ 53 0 R
+ 54 0 R
+ 55 0 R ]
+ /Contents 182 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -906,8 +944,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER47': class PDFDictionary
-55 0 obj
+% 'Annot.NUMBER49': class PDFDictionary
+57 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -915,14 +953,14 @@ endobj
0
0 ]
/Rect [ 83.82606
- 657.3936
+ 645.3936
106.0692
- 669.3936 ]
+ 657.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER48': class PDFDictionary
-56 0 obj
+% 'Annot.NUMBER50': class PDFDictionary
+58 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -930,14 +968,14 @@ endobj
0
0 ]
/Rect [ 243.8829
- 645.3936
+ 633.3936
265.0029
- 657.3936 ]
+ 645.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER49': class PDFDictionary
-57 0 obj
+% 'Annot.NUMBER51': class PDFDictionary
+59 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -945,14 +983,14 @@ endobj
0
0 ]
/Rect [ 83.6329
- 508.1936
+ 496.1936
105.6829
- 520.1936 ]
+ 508.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER50': class PDFDictionary
-58 0 obj
+% 'Annot.NUMBER52': class PDFDictionary
+60 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -960,41 +998,57 @@ endobj
0
0 ]
/Rect [ 421.9727
- 508.1936
+ 496.1936
465.1427
- 520.1936 ]
+ 508.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER51': class PDFDictionary
-59 0 obj
+% 'Annot.NUMBER53': class PDFDictionary
+61 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 107.8707
+ 301.7936
+ 129.1584
+ 313.7936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER54': class PDFDictionary
+62 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 211.6529
- 313.7936
- 232.7729
- 325.7936 ]
+ /Rect [ 117.7229
+ 289.7936
+ 138.8429
+ 301.7936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page3': class PDFPage
-60 0 obj
+63 0 obj
% Page dictionary
-<< /Annots [ 55 0 R
- 56 0 R
- 57 0 R
+<< /Annots [ 57 0 R
58 0 R
- 59 0 R ]
- /Contents 172 0 R
+ 59 0 R
+ 60 0 R
+ 61 0 R
+ 62 0 R ]
+ /Contents 183 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1005,8 +1059,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER52': class PDFDictionary
-61 0 obj
+% 'Annot.NUMBER55': class PDFDictionary
+64 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1014,14 +1068,14 @@ endobj
0
0 ]
/Rect [ 321.4303
- 651.3936
+ 627.3936
363.754
- 663.3936 ]
+ 639.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER53': class PDFDictionary
-62 0 obj
+% 'Annot.NUMBER56': class PDFDictionary
+65 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1029,14 +1083,14 @@ endobj
0
0 ]
/Rect [ 126.0429
- 639.3936
+ 615.3936
147.1629
- 651.3936 ]
+ 627.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER54': class PDFDictionary
-63 0 obj
+% 'Annot.NUMBER57': class PDFDictionary
+66 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1044,24 +1098,24 @@ endobj
0
0 ]
/Rect [ 62.69291
- 400.9936
+ 376.9936
84.20915
- 412.9936 ]
+ 388.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page4': class PDFPage
-64 0 obj
+67 0 obj
% Page dictionary
-<< /Annots [ 61 0 R
- 62 0 R
- 63 0 R ]
- /Contents 173 0 R
+<< /Annots [ 64 0 R
+ 65 0 R
+ 66 0 R ]
+ /Contents 184 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1072,8 +1126,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER55': class PDFDictionary
-65 0 obj
+% 'Annot.NUMBER58': class PDFDictionary
+68 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1081,14 +1135,14 @@ endobj
0
0 ]
/Rect [ 446.1627
- 699.3936
+ 675.3936
464.5027
- 711.3936 ]
+ 687.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER56': class PDFDictionary
-66 0 obj
+% 'Annot.NUMBER59': class PDFDictionary
+69 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1096,29 +1150,29 @@ endobj
0
0 ]
/Rect [ 62.69291
- 669.3936
- 84.28901
- 681.3936 ]
+ 645.3936
+ 86.84915
+ 657.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER57': class PDFDictionary
-67 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 [ 161.7834
- 669.3936
- 217.2895
- 681.3936 ]
+ /Rect [ 315.119
+ 645.3936
+ 367.369
+ 657.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER58': class PDFDictionary
-68 0 obj
+% 'Annot.NUMBER61': class PDFDictionary
+71 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1126,73 +1180,73 @@ endobj
0
0 ]
/Rect [ 62.69291
- 268.9936
+ 244.9936
83.81291
- 280.9936 ]
+ 256.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER59': class PDFDictionary
-69 0 obj
+% 'Annot.NUMBER62': class PDFDictionary
+72 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
/Border [ 0
0
0 ]
- /Rect [ 357.8702
- 169.9936
- 416.0058
- 181.9936 ]
+ /Rect [ 240.1228
+ 145.9936
+ 297.7099
+ 157.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER60': class PDFDictionary
-70 0 obj
+% 'Annot.NUMBER63': class PDFDictionary
+73 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 ]
+ /Rect [ 62.69291
+ 133.9936
+ 114.9429
+ 145.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER61': class PDFDictionary
-71 0 obj
+% 'Annot.NUMBER64': class PDFDictionary
+74 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 150.5229
- 145.9936
- 171.6429
- 157.9936 ]
+ /Rect [ 496.4721
+ 133.9936
+ 518.6827
+ 145.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page5': class PDFPage
-72 0 obj
+75 0 obj
% Page dictionary
-<< /Annots [ 65 0 R
- 66 0 R
- 67 0 R
- 68 0 R
+<< /Annots [ 68 0 R
69 0 R
70 0 R
- 71 0 R ]
- /Contents 174 0 R
+ 71 0 R
+ 72 0 R
+ 73 0 R
+ 74 0 R ]
+ /Contents 185 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1203,8 +1257,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER62': class PDFDictionary
-73 0 obj
+% 'Annot.NUMBER65': class PDFDictionary
+76 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1212,22 +1266,22 @@ endobj
0
0 ]
/Rect [ 494.1558
- 651.3936
+ 627.3936
515.9027
- 663.3936 ]
+ 639.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page6': class PDFPage
-74 0 obj
+77 0 obj
% Page dictionary
-<< /Annots [ 73 0 R ]
- /Contents 175 0 R
+<< /Annots [ 76 0 R ]
+ /Contents 186 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1238,8 +1292,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER63': class PDFDictionary
-75 0 obj
+% 'Annot.NUMBER66': class PDFDictionary
+78 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1247,22 +1301,22 @@ endobj
0
0 ]
/Rect [ 62.69291
- 215.5936
+ 195.5936
84.62846
- 227.5936 ]
+ 207.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page7': class PDFPage
-76 0 obj
+79 0 obj
% Page dictionary
-<< /Annots [ 75 0 R ]
- /Contents 176 0 R
+<< /Annots [ 78 0 R ]
+ /Contents 187 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1273,8 +1327,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER64': class PDFDictionary
-77 0 obj
+% 'Annot.NUMBER67': class PDFDictionary
+80 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1282,22 +1336,22 @@ endobj
0
0 ]
/Rect [ 110.2829
- 341.9936
+ 317.9936
132.8629
- 353.9936 ]
+ 329.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page8': class PDFPage
-78 0 obj
+81 0 obj
% Page dictionary
-<< /Annots [ 77 0 R ]
- /Contents 177 0 R
+<< /Annots [ 80 0 R ]
+ /Contents 188 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1308,23 +1362,23 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER65': class PDFDictionary
-79 0 obj
+% 'Annot.NUMBER68': class PDFDictionary
+82 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 303.6429
+ /Rect [ 392.5829
663.3936
- 324.7629
+ 413.7029
675.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER66': class PDFDictionary
-80 0 obj
+% 'Annot.NUMBER69': class PDFDictionary
+83 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1338,8 +1392,8 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER67': class PDFDictionary
-81 0 obj
+% 'Annot.NUMBER70': class PDFDictionary
+84 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1353,8 +1407,8 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER68': class PDFDictionary
-82 0 obj
+% 'Annot.NUMBER71': class PDFDictionary
+85 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1368,8 +1422,8 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER69': class PDFDictionary
-83 0 obj
+% 'Annot.NUMBER72': class PDFDictionary
+86 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1383,8 +1437,8 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER70': class PDFDictionary
-84 0 obj
+% 'Annot.NUMBER73': class PDFDictionary
+87 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1398,37 +1452,37 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER71': class PDFDictionary
-85 0 obj
+% 'Annot.NUMBER74': class PDFDictionary
+88 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 355.0429
+ /Rect [ 360.0429
396.3936
- 397.2829
+ 402.2829
408.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page9': class PDFPage
-86 0 obj
+89 0 obj
% Page dictionary
-<< /Annots [ 79 0 R
- 80 0 R
- 81 0 R
- 82 0 R
+<< /Annots [ 82 0 R
83 0 R
84 0 R
- 85 0 R ]
- /Contents 178 0 R
+ 85 0 R
+ 86 0 R
+ 87 0 R
+ 88 0 R ]
+ /Contents 189 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1439,8 +1493,27 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER72': class PDFDictionary
-87 0 obj
+% 'Page10': class PDFPage
+90 0 obj
+% Page dictionary
+<< /Contents 190 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 180 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER75': class PDFDictionary
+91 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1448,14 +1521,14 @@ endobj
0
0 ]
/Rect [ 338.1568
- 182.6549
+ 729.5936
360.5113
- 194.6549 ]
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER73': class PDFDictionary
-88 0 obj
+% 'Annot.NUMBER76': class PDFDictionary
+92 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://www.sqlalchemy.org/) >>
@@ -1463,14 +1536,14 @@ endobj
0
0 ]
/Rect [ 110.6843
- 170.6549
+ 717.5936
169.0343
- 182.6549 ]
+ 729.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER74': class PDFDictionary
-89 0 obj
+% 'Annot.NUMBER77': class PDFDictionary
+93 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html) >>
@@ -1478,24 +1551,24 @@ endobj
0
0 ]
/Rect [ 168.3029
- 158.6549
+ 705.5936
208.8829
- 170.6549 ]
+ 717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page10': class PDFPage
-90 0 obj
+% 'Page11': class PDFPage
+94 0 obj
% Page dictionary
-<< /Annots [ 87 0 R
- 88 0 R
- 89 0 R ]
- /Contents 179 0 R
+<< /Annots [ 91 0 R
+ 92 0 R
+ 93 0 R ]
+ /Contents 191 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1506,15 +1579,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page11': class PDFPage
-91 0 obj
+% 'Annot.NUMBER78': class PDFDictionary
+95 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 185.0709
+ 672.3936
+ 208.0228
+ 684.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER79': class PDFDictionary
+96 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 220.5998
+ 660.3936
+ 243.819
+ 672.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page12': class PDFPage
+97 0 obj
% Page dictionary
-<< /Contents 180 0 R
+<< /Annots [ 95 0 R
+ 96 0 R ]
+ /Contents 192 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1525,47 +1630,82 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER75': class PDFDictionary
-92 0 obj
+% 'Annot.NUMBER80': class PDFDictionary
+98 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 185.0709
- 729.5936
- 208.0228
- 741.5936 ]
+ /Rect [ 374.4929
+ 624.3936
+ 395.6129
+ 636.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER76': class PDFDictionary
-93 0 obj
+% 'Page13': class PDFPage
+99 0 obj
+% Page dictionary
+<< /Annots [ 98 0 R ]
+ /Contents 193 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 180 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER81': class PDFDictionary
+100 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 304.0655
+ 570.3936
+ 348.3808
+ 582.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER82': class PDFDictionary
+101 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 220.5998
- 717.5936
- 243.819
- 729.5936 ]
+ /Rect [ 293.7749
+ 396.3936
+ 316.2402
+ 408.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page12': class PDFPage
-94 0 obj
+% 'Page14': class PDFPage
+102 0 obj
% Page dictionary
-<< /Annots [ 92 0 R
- 93 0 R ]
- /Contents 181 0 R
+<< /Annots [ 100 0 R
+ 101 0 R ]
+ /Contents 194 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1576,8 +1716,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER77': class PDFDictionary
-95 0 obj
+% 'Annot.NUMBER83': class PDFDictionary
+103 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1585,14 +1725,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 729.5936
+ 560.3936
84.8789
- 741.5936 ]
+ 572.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER78': class PDFDictionary
-96 0 obj
+% 'Annot.NUMBER84': class PDFDictionary
+104 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1600,14 +1740,14 @@ endobj
0
0 ]
/Rect [ 466.5307
- 729.5936
+ 560.3936
509.8367
- 741.5936 ]
+ 572.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER79': class PDFDictionary
-97 0 obj
+% 'Annot.NUMBER85': class PDFDictionary
+105 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1615,14 +1755,14 @@ endobj
0
0 ]
/Rect [ 124.3929
- 705.5936
+ 536.3936
163.8529
- 717.5936 ]
+ 548.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER80': class PDFDictionary
-98 0 obj
+% 'Annot.NUMBER86': class PDFDictionary
+106 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1630,14 +1770,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 624.5936
+ 455.3936
127.9329
- 636.5936 ]
+ 467.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER81': class PDFDictionary
-99 0 obj
+% 'Annot.NUMBER87': class PDFDictionary
+107 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1645,14 +1785,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 606.5936
+ 437.3936
107.9337
- 618.5936 ]
+ 449.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER82': class PDFDictionary
-100 0 obj
+% 'Annot.NUMBER88': class PDFDictionary
+108 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1660,14 +1800,44 @@ endobj
0
0 ]
/Rect [ 308.5389
- 606.5936
+ 437.3936
351.8997
- 618.5936 ]
+ 449.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER83': class PDFDictionary
-101 0 obj
+% 'Annot.NUMBER89': class PDFDictionary
+109 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 380.6856
+ 413.3936
+ 423.7999
+ 425.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER90': class PDFDictionary
+110 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 494.4684
+ 413.3936
+ 516.4627
+ 425.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER91': class PDFDictionary
+111 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1675,14 +1845,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 564.5936
+ 383.3936
108.3529
- 576.5936 ]
+ 395.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER84': class PDFDictionary
-102 0 obj
+% 'Annot.NUMBER92': class PDFDictionary
+112 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1690,14 +1860,14 @@ endobj
0
0 ]
/Rect [ 277.2428
- 564.5936
+ 383.3936
321.0228
- 576.5936 ]
+ 395.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER85': class PDFDictionary
-103 0 obj
+% 'Annot.NUMBER93': class PDFDictionary
+113 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1705,14 +1875,14 @@ endobj
0
0 ]
/Rect [ 404.5839
- 552.5936
+ 371.3936
426.0657
- 564.5936 ]
+ 383.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER86': class PDFDictionary
-104 0 obj
+% 'Annot.NUMBER94': class PDFDictionary
+114 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1720,14 +1890,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 498.5936
+ 317.3936
108.61
- 510.5936 ]
+ 329.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER87': class PDFDictionary
-105 0 obj
+% 'Annot.NUMBER95': class PDFDictionary
+115 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1735,14 +1905,14 @@ endobj
0
0 ]
/Rect [ 459.2622
- 486.5936
+ 305.3936
481.289
- 498.5936 ]
+ 317.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER88': class PDFDictionary
-106 0 obj
+% 'Annot.NUMBER96': class PDFDictionary
+116 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1750,14 +1920,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 456.5936
+ 275.3936
108.9242
- 468.5936 ]
+ 287.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER89': class PDFDictionary
-107 0 obj
+% 'Annot.NUMBER97': class PDFDictionary
+117 0 obj
<< /A << /S /URI
/Type /Action
/URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
@@ -1765,14 +1935,14 @@ endobj
0
0 ]
/Rect [ 340.9248
- 456.5936
+ 275.3936
470.1087
- 468.5936 ]
+ 287.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER90': class PDFDictionary
-108 0 obj
+% 'Annot.NUMBER98': class PDFDictionary
+118 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1780,14 +1950,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 426.5936
+ 245.3936
107.9247
- 438.5936 ]
+ 257.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER91': class PDFDictionary
-109 0 obj
+% 'Annot.NUMBER99': class PDFDictionary
+119 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1795,14 +1965,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 414.5936
+ 233.3936
104.0329
- 426.5936 ]
+ 245.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER92': class PDFDictionary
-110 0 obj
+% 'Annot.NUMBER100': class PDFDictionary
+120 0 obj
<< /A << /S /URI
/Type /Action
/URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
@@ -1810,14 +1980,14 @@ endobj
0
0 ]
/Rect [ 489.2227
- 414.5936
+ 233.3936
532.176
- 426.5936 ]
+ 245.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER93': class PDFDictionary
-111 0 obj
+% 'Annot.NUMBER101': class PDFDictionary
+121 0 obj
<< /A << /S /URI
/Type /Action
/URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
@@ -1825,14 +1995,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 402.5936
+ 221.3936
159.6229
- 414.5936 ]
+ 233.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER94': class PDFDictionary
-112 0 obj
+% 'Annot.NUMBER102': class PDFDictionary
+122 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1840,14 +2010,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 381.5936
+ 200.3936
83.81291
- 393.5936 ]
+ 212.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER95': class PDFDictionary
-113 0 obj
+% 'Annot.NUMBER103': class PDFDictionary
+123 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1855,14 +2025,14 @@ endobj
0
0 ]
/Rect [ 219.4229
- 381.5936
+ 200.3936
261.6629
- 393.5936 ]
+ 212.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER96': class PDFDictionary
-114 0 obj
+% 'Annot.NUMBER104': class PDFDictionary
+124 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
@@ -1870,39 +2040,16 @@ endobj
0
0 ]
/Rect [ 455.2227
- 351.5936
+ 170.3936
534.3667
- 363.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER97': class PDFDictionary
-115 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 325.7268
- 184.3936
- 347.4138
- 196.3936 ]
+ 182.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page13': class PDFPage
-116 0 obj
+% 'Page15': class PDFPage
+125 0 obj
% Page dictionary
-<< /Annots [ 95 0 R
- 96 0 R
- 97 0 R
- 98 0 R
- 99 0 R
- 100 0 R
- 101 0 R
- 102 0 R
- 103 0 R
+<< /Annots [ 103 0 R
104 0 R
105 0 R
106 0 R
@@ -1914,13 +2061,22 @@ endobj
112 0 R
113 0 R
114 0 R
- 115 0 R ]
- /Contents 182 0 R
+ 115 0 R
+ 116 0 R
+ 117 0 R
+ 118 0 R
+ 119 0 R
+ 120 0 R
+ 121 0 R
+ 122 0 R
+ 123 0 R
+ 124 0 R ]
+ /Contents 195 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1931,23 +2087,23 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER98': class PDFDictionary
-117 0 obj
+% 'Annot.NUMBER105': class PDFDictionary
+126 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 242.2129
- 699.3936
- 365.0629
- 711.3936 ]
+ /Rect [ 325.7268
+ 675.3936
+ 347.4138
+ 687.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER99': class PDFDictionary
-118 0 obj
+% 'Annot.NUMBER106': class PDFDictionary
+127 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -1955,14 +2111,14 @@ endobj
0
0 ]
/Rect [ 327.2261
- 681.3936
+ 508.1936
410.5152
- 693.3936 ]
+ 520.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER100': class PDFDictionary
-119 0 obj
+% 'Annot.NUMBER107': class PDFDictionary
+128 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1970,14 +2126,14 @@ endobj
0
0 ]
/Rect [ 275.5829
- 490.1936
+ 328.9936
317.8229
- 502.1936 ]
+ 340.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER101': class PDFDictionary
-120 0 obj
+% 'Annot.NUMBER108': class PDFDictionary
+129 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1985,14 +2141,14 @@ endobj
0
0 ]
/Rect [ 307.9178
- 472.1936
+ 310.9936
351.5999
- 484.1936 ]
+ 322.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER102': class PDFDictionary
-121 0 obj
+% 'Annot.NUMBER109': class PDFDictionary
+130 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2000,14 +2156,14 @@ endobj
0
0 ]
/Rect [ 329.8034
- 448.1936
+ 286.9936
352.1804
- 460.1936 ]
+ 298.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER103': class PDFDictionary
-122 0 obj
+% 'Annot.NUMBER110': class PDFDictionary
+131 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2015,14 +2171,14 @@ endobj
0
0 ]
/Rect [ 109.0098
- 385.1936
+ 223.9936
131.9967
- 397.1936 ]
+ 235.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER104': class PDFDictionary
-123 0 obj
+% 'Annot.NUMBER111': class PDFDictionary
+132 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2030,14 +2186,14 @@ endobj
0
0 ]
/Rect [ 397.2929
- 361.1936
+ 199.9936
415.6329
- 373.1936 ]
+ 211.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER105': class PDFDictionary
-124 0 obj
+% 'Annot.NUMBER112': class PDFDictionary
+133 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/opterator) >>
@@ -2045,14 +2201,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 340.1936
+ 178.9936
128.4929
- 352.1936 ]
+ 190.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER106': class PDFDictionary
-125 0 obj
+% 'Annot.NUMBER113': class PDFDictionary
+134 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/CLIArgs) >>
@@ -2060,14 +2216,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 322.1936
+ 160.9936
124.5929
- 334.1936 ]
+ 172.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER107': class PDFDictionary
-126 0 obj
+% 'Annot.NUMBER114': class PDFDictionary
+135 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2075,14 +2231,14 @@ endobj
0
0 ]
/Rect [ 464.3898
- 301.1936
+ 139.9936
503.8498
- 313.1936 ]
+ 151.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER108': class PDFDictionary
-127 0 obj
+% 'Annot.NUMBER115': class PDFDictionary
+136 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2090,14 +2246,14 @@ endobj
0
0 ]
/Rect [ 305.0429
- 289.1936
+ 127.9936
323.3829
- 301.1936 ]
+ 139.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER109': class PDFDictionary
-128 0 obj
+% 'Annot.NUMBER116': class PDFDictionary
+137 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2105,14 +2261,14 @@ endobj
0
0 ]
/Rect [ 455.0104
- 271.1936
+ 109.9936
479.9015
- 283.1936 ]
+ 121.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER110': class PDFDictionary
-129 0 obj
+% 'Annot.NUMBER117': class PDFDictionary
+138 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2120,14 +2276,14 @@ endobj
0
0 ]
/Rect [ 303.707
- 259.1936
+ 97.99362
322.047
- 271.1936 ]
+ 109.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER111': class PDFDictionary
-130 0 obj
+% 'Annot.NUMBER118': class PDFDictionary
+139 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2135,14 +2291,47 @@ endobj
0
0 ]
/Rect [ 328.8186
- 259.1936
+ 97.99362
353.3701
- 271.1936 ]
+ 109.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER112': class PDFDictionary
-131 0 obj
+% 'Page16': class PDFPage
+140 0 obj
+% Page dictionary
+<< /Annots [ 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 ]
+ /Contents 196 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 180 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER119': class PDFDictionary
+141 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2150,14 +2339,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 247.1936
+ 756.5936
81.03291
- 259.1936 ]
+ 768.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER113': class PDFDictionary
-132 0 obj
+% 'Annot.NUMBER120': class PDFDictionary
+142 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2165,14 +2354,14 @@ endobj
0
0 ]
/Rect [ 156.6051
- 196.1936
+ 705.5936
177.8606
- 208.1936 ]
+ 717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER114': class PDFDictionary
-133 0 obj
+% 'Annot.NUMBER121': class PDFDictionary
+143 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2180,14 +2369,14 @@ endobj
0
0 ]
/Rect [ 186.6535
- 172.1936
+ 681.5936
226.1135
- 184.1936 ]
+ 693.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER115': class PDFDictionary
-134 0 obj
+% 'Annot.NUMBER122': class PDFDictionary
+144 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2195,14 +2384,14 @@ endobj
0
0 ]
/Rect [ 493.1227
- 172.1936
+ 681.5936
532.4646
- 184.1936 ]
+ 693.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER116': class PDFDictionary
-135 0 obj
+% 'Annot.NUMBER123': class PDFDictionary
+145 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2210,84 +2399,89 @@ endobj
0
0 ]
/Rect [ 72.7804
- 160.1936
+ 669.5936
96.20788
- 172.1936 ]
+ 681.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER117': class PDFDictionary
-136 0 obj
+% 'Annot.NUMBER124': class PDFDictionary
+146 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 457.957
- 148.1936
- 531.337
- 160.1936 ]
+ /Rect [ 149.2229
+ 639.5936
+ 171.2704
+ 651.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER118': class PDFDictionary
-137 0 obj
+% 'Annot.NUMBER125': class PDFDictionary
+147 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 136.1936
- 106.6029
- 148.1936 ]
+ /Rect [ 128.0309
+ 597.5936
+ 149.4369
+ 609.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page14': class PDFPage
-138 0 obj
-% Page dictionary
-<< /Annots [ 117 0 R
- 118 0 R
- 119 0 R
- 120 0 R
- 121 0 R
- 122 0 R
- 123 0 R
- 124 0 R
- 125 0 R
- 126 0 R
- 127 0 R
- 128 0 R
- 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 ]
- /Contents 183 0 R
- /MediaBox [ 0
+% 'Annot.NUMBER126': class PDFDictionary
+148 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
0
- 595.2756
- 841.8898 ]
- /Parent 169 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
+ 0 ]
+ /Rect [ 502.8367
+ 597.5936
+ 524.2427
+ 609.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
endobj
-% 'Annot.NUMBER119': class PDFDictionary
-139 0 obj
+% 'Annot.NUMBER127': class PDFDictionary
+149 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 187.4797
+ 573.5936
+ 209.0991
+ 585.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER128': class PDFDictionary
+150 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 301.6965
+ 573.5936
+ 426.0446
+ 585.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER129': class PDFDictionary
+151 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2295,14 +2489,14 @@ endobj
0
0 ]
/Rect [ 83.64556
- 729.5936
+ 498.5936
105.7082
- 741.5936 ]
+ 510.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER120': class PDFDictionary
-140 0 obj
+% 'Annot.NUMBER130': class PDFDictionary
+152 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
@@ -2310,14 +2504,14 @@ endobj
0
0 ]
/Rect [ 446.6
- 729.5936
+ 498.5936
502.5727
- 741.5936 ]
+ 510.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER121': class PDFDictionary
-141 0 obj
+% 'Annot.NUMBER131': class PDFDictionary
+153 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2325,14 +2519,14 @@ endobj
0
0 ]
/Rect [ 275.6828
- 717.5936
+ 486.5936
297.3688
- 729.5936 ]
+ 498.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER122': class PDFDictionary
-142 0 obj
+% 'Annot.NUMBER132': class PDFDictionary
+154 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser) >>
@@ -2340,14 +2534,14 @@ endobj
0
0 ]
/Rect [ 77.19665
- 705.5936
+ 474.5936
139.4904
- 717.5936 ]
+ 486.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER123': class PDFDictionary
-143 0 obj
+% 'Annot.NUMBER133': class PDFDictionary
+155 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2355,14 +2549,14 @@ endobj
0
0 ]
/Rect [ 96.54131
- 693.5936
+ 462.5936
139.0255
- 705.5936 ]
+ 474.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER124': class PDFDictionary
-144 0 obj
+% 'Annot.NUMBER134': class PDFDictionary
+156 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2370,14 +2564,14 @@ endobj
0
0 ]
/Rect [ 203.5016
- 660.5936
+ 429.5936
245.8453
- 672.5936 ]
+ 441.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER125': class PDFDictionary
-145 0 obj
+% 'Annot.NUMBER135': class PDFDictionary
+157 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -2385,14 +2579,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 585.5936
+ 354.5936
138.7898
- 597.5936 ]
+ 366.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER126': class PDFDictionary
-146 0 obj
+% 'Annot.NUMBER136': class PDFDictionary
+158 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2400,14 +2594,14 @@ endobj
0
0 ]
/Rect [ 114.6649
- 573.5936
+ 342.5936
154.1249
- 585.5936 ]
+ 354.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER127': class PDFDictionary
-147 0 obj
+% 'Annot.NUMBER137': class PDFDictionary
+159 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2415,14 +2609,14 @@ endobj
0
0 ]
/Rect [ 191.6329
- 561.5936
+ 330.5936
233.8729
- 573.5936 ]
+ 342.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER128': class PDFDictionary
-148 0 obj
+% 'Annot.NUMBER138': class PDFDictionary
+160 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2430,44 +2624,14 @@ endobj
0
0 ]
/Rect [ 263.3429
- 531.5936
+ 300.5936
286.6829
- 543.5936 ]
+ 312.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER129': class PDFDictionary
-149 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 128.0309
- 483.5936
- 149.4369
- 495.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER130': class PDFDictionary
-150 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 502.8367
- 483.5936
- 524.2427
- 495.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER131': class PDFDictionary
-151 0 obj
+% 'Annot.NUMBER139': class PDFDictionary
+161 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2475,18 +2639,16 @@ endobj
0
0 ]
/Rect [ 258.5629
- 441.5936
+ 252.5936
276.9029
- 453.5936 ]
+ 264.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page15': class PDFPage
-152 0 obj
+% 'Page17': class PDFPage
+162 0 obj
% Page dictionary
-<< /Annots [ 139 0 R
- 140 0 R
- 141 0 R
+<< /Annots [ 141 0 R
142 0 R
143 0 R
144 0 R
@@ -2496,13 +2658,23 @@ endobj
148 0 R
149 0 R
150 0 R
- 151 0 R ]
- /Contents 184 0 R
+ 151 0 R
+ 152 0 R
+ 153 0 R
+ 154 0 R
+ 155 0 R
+ 156 0 R
+ 157 0 R
+ 158 0 R
+ 159 0 R
+ 160 0 R
+ 161 0 R ]
+ /Contents 197 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 169 0 R
+ /Parent 180 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -2513,210 +2685,224 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R153': class PDFCatalog
-153 0 obj
+% 'R163': class PDFCatalog
+163 0 obj
% Document Root
-<< /Outlines 155 0 R
- /PageLabels 185 0 R
+<< /Outlines 165 0 R
+ /PageLabels 198 0 R
/PageMode /UseNone
- /Pages 169 0 R
+ /Pages 180 0 R
/Type /Catalog >>
endobj
-% 'R154': class PDFInfo
-154 0 obj
+% 'R164': class PDFInfo
+164 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100615054641-01'00')
+ /CreationDate (D:20100620102407-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Plac: Parsing the Command Line the Easy Way) >>
endobj
-% 'R155': class PDFOutlines
-155 0 obj
-<< /Count 13
- /First 156 0 R
- /Last 168 0 R
+% 'R165': class PDFOutlines
+165 0 obj
+<< /Count 14
+ /First 166 0 R
+ /Last 179 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-156 0 obj
-<< /Dest [ 46 0 R
+166 0 obj
+<< /Dest [ 48 0 R
/XYZ
62.69291
- 329.0236
+ 296.0236
0 ]
- /Next 157 0 R
- /Parent 155 0 R
+ /Next 167 0 R
+ /Parent 165 0 R
/Title (The importance of scaling down) >>
endobj
% 'Outline.1': class OutlineEntryObject
-157 0 obj
-<< /Dest [ 54 0 R
+167 0 obj
+<< /Dest [ 56 0 R
/XYZ
62.69291
- 627.0236
+ 615.0236
0 ]
- /Next 158 0 R
- /Parent 155 0 R
- /Prev 156 0 R
+ /Next 168 0 R
+ /Parent 165 0 R
+ /Prev 166 0 R
/Title (Scripts with required arguments) >>
endobj
% 'Outline.2': class OutlineEntryObject
-158 0 obj
-<< /Dest [ 60 0 R
+168 0 obj
+<< /Dest [ 63 0 R
/XYZ
62.69291
- 298.2236
+ 274.2236
0 ]
- /Next 159 0 R
- /Parent 155 0 R
- /Prev 157 0 R
+ /Next 169 0 R
+ /Parent 165 0 R
+ /Prev 167 0 R
/Title (Scripts with default arguments) >>
endobj
% 'Outline.3': class OutlineEntryObject
-159 0 obj
-<< /Dest [ 72 0 R
+169 0 obj
+<< /Dest [ 75 0 R
/XYZ
62.69291
- 253.4236
+ 229.4236
0 ]
- /Next 160 0 R
- /Parent 155 0 R
- /Prev 158 0 R
+ /Next 170 0 R
+ /Parent 165 0 R
+ /Prev 168 0 R
/Title (Scripts with options \(and smart options\)) >>
endobj
% 'Outline.4': class OutlineEntryObject
-160 0 obj
-<< /Dest [ 76 0 R
+170 0 obj
+<< /Dest [ 79 0 R
/XYZ
62.69291
- 251.0236
+ 231.0236
0 ]
- /Next 161 0 R
- /Parent 155 0 R
- /Prev 159 0 R
+ /Next 171 0 R
+ /Parent 165 0 R
+ /Prev 169 0 R
/Title (Scripts with flags) >>
endobj
% 'Outline.5': class OutlineEntryObject
-161 0 obj
-<< /Dest [ 78 0 R
+171 0 obj
+<< /Dest [ 81 0 R
/XYZ
62.69291
- 401.4236
+ 377.4236
0 ]
- /Next 162 0 R
- /Parent 155 0 R
- /Prev 160 0 R
+ /Next 172 0 R
+ /Parent 165 0 R
+ /Prev 170 0 R
/Title (plac for Python 2.X users) >>
endobj
% 'Outline.6': class OutlineEntryObject
-162 0 obj
-<< /Dest [ 86 0 R
+172 0 obj
+<< /Dest [ 89 0 R
/XYZ
62.69291
647.8236
0 ]
- /Next 163 0 R
- /Parent 155 0 R
- /Prev 161 0 R
+ /Next 173 0 R
+ /Parent 165 0 R
+ /Prev 171 0 R
/Title (More features) >>
endobj
% 'Outline.7': class OutlineEntryObject
-163 0 obj
-<< /Dest [ 90 0 R
+173 0 obj
+<< /Dest [ 94 0 R
/XYZ
62.69291
- 218.0849
+ 765.0236
0 ]
- /Next 164 0 R
- /Parent 155 0 R
- /Prev 162 0 R
+ /Next 174 0 R
+ /Parent 165 0 R
+ /Prev 172 0 R
/Title (A realistic example) >>
endobj
% 'Outline.8': class OutlineEntryObject
-164 0 obj
-<< /Dest [ 94 0 R
+174 0 obj
+<< /Dest [ 97 0 R
/XYZ
62.69291
- 765.0236
+ 707.8236
0 ]
- /Next 165 0 R
- /Parent 155 0 R
- /Prev 163 0 R
+ /Next 175 0 R
+ /Parent 165 0 R
+ /Prev 173 0 R
/Title (Keyword arguments) >>
endobj
% 'Outline.9': class OutlineEntryObject
-165 0 obj
-<< /Dest [ 116 0 R
+175 0 obj
+<< /Dest [ 99 0 R
/XYZ
62.69291
- 765.0236
+ 683.8236
0 ]
- /Next 166 0 R
- /Parent 155 0 R
- /Prev 164 0 R
- /Title (plac vs argparse) >>
+ /Next 176 0 R
+ /Parent 165 0 R
+ /Prev 174 0 R
+ /Title (Final example: a shelve interface) >>
endobj
% 'Outline.10': class OutlineEntryObject
-166 0 obj
-<< /Dest [ 138 0 R
+176 0 obj
+<< /Dest [ 125 0 R
/XYZ
62.69291
- 420.6236
+ 595.8236
0 ]
- /Next 167 0 R
- /Parent 155 0 R
- /Prev 165 0 R
- /Title (plac vs the rest of the world) >>
+ /Next 177 0 R
+ /Parent 165 0 R
+ /Prev 175 0 R
+ /Title (plac vs argparse) >>
endobj
% 'Outline.11': class OutlineEntryObject
-167 0 obj
-<< /Dest [ 138 0 R
+177 0 obj
+<< /Dest [ 140 0 R
/XYZ
62.69291
- 231.6236
+ 259.4236
0 ]
- /Next 168 0 R
- /Parent 155 0 R
- /Prev 166 0 R
- /Title (The future) >>
+ /Next 178 0 R
+ /Parent 165 0 R
+ /Prev 176 0 R
+ /Title (plac vs the rest of the world) >>
endobj
% 'Outline.12': class OutlineEntryObject
-168 0 obj
-<< /Dest [ 152 0 R
+178 0 obj
+<< /Dest [ 162 0 R
/XYZ
62.69291
- 765.0236
+ 741.0236
0 ]
- /Parent 155 0 R
- /Prev 167 0 R
+ /Next 179 0 R
+ /Parent 165 0 R
+ /Prev 177 0 R
+ /Title (The future) >>
+endobj
+% 'Outline.13': class OutlineEntryObject
+179 0 obj
+<< /Dest [ 162 0 R
+ /XYZ
+ 62.69291
+ 534.0236
+ 0 ]
+ /Parent 165 0 R
+ /Prev 178 0 R
/Title (Trivia: the story behind the name) >>
endobj
-% 'R169': class PDFPages
-169 0 obj
+% 'R180': class PDFPages
+180 0 obj
% page tree
-<< /Count 15
- /Kids [ 46 0 R
- 54 0 R
- 60 0 R
- 64 0 R
- 72 0 R
- 74 0 R
- 76 0 R
- 78 0 R
- 86 0 R
+<< /Count 17
+ /Kids [ 48 0 R
+ 56 0 R
+ 63 0 R
+ 67 0 R
+ 75 0 R
+ 77 0 R
+ 79 0 R
+ 81 0 R
+ 89 0 R
90 0 R
- 91 0 R
94 0 R
- 116 0 R
- 138 0 R
- 152 0 R ]
+ 97 0 R
+ 99 0 R
+ 102 0 R
+ 125 0 R
+ 140 0 R
+ 162 0 R ]
/Type /Pages >>
endobj
-% 'R170': class PDFStream
-170 0 obj
+% 'R181': class PDFStream
+181 0 obj
% page stream
-<< /Length 8958 >>
+<< /Length 9341 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -2776,14 +2962,14 @@ q
1 0 0 1 6 3 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 26.91937 0 Td (Requires:) Tj T* -26.91937 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 48.03937 0 Td (Date:) Tj T* -48.03937 0 Td ET
Q
Q
q
1 0 0 1 91.03937 3 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Python 2.3+) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (June 2010) Tj T* ET
Q
Q
q
@@ -2841,6 +3027,27 @@ q
1 0 0 1 6 3 cm
q
0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 26.91937 0 Td (Requires:) Tj T* -26.91937 0 Td ET
+Q
+Q
+q
+1 0 0 1 91.03937 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Python 2.3+) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 614.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 16.91937 0 Td (Installation:) Tj T* -16.91937 0 Td ET
Q
Q
@@ -2855,7 +3062,7 @@ q
Q
Q
q
-1 0 0 1 62.69291 614.0236 cm
+1 0 0 1 62.69291 599.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -2876,23 +3083,23 @@ q
Q
Q
q
-1 0 0 1 62.69291 581.0236 cm
+1 0 0 1 62.69291 566.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Contents) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 341.0236 cm
+1 0 0 1 62.69291 308.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 0 219 cm
+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 (The importance of scaling down) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 219 cm
+1 0 0 1 397.8898 237 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2900,13 +3107,13 @@ 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
+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 required arguments) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 201 cm
+1 0 0 1 397.8898 219 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2914,13 +3121,13 @@ 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
+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 default arguments) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 183 cm
+1 0 0 1 397.8898 201 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2928,13 +3135,13 @@ 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
+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 options \(and smart options\)) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 165 cm
+1 0 0 1 397.8898 183 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2942,13 +3149,13 @@ 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
+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 (Scripts with flags) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 147 cm
+1 0 0 1 397.8898 165 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2956,13 +3163,13 @@ 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
+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 (plac for Python 2.X users) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 129 cm
+1 0 0 1 397.8898 147 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2970,13 +3177,13 @@ 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
+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 (More features) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 111 cm
+1 0 0 1 397.8898 129 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2984,23 +3191,37 @@ 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
+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
Q
Q
q
+1 0 0 1 397.8898 111 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 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 (Keyword arguments) Tj T* ET
+Q
+Q
+q
1 0 0 1 397.8898 93 cm
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 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 60.88 0 Td (12) 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 (Keyword arguments) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Final example: a shelve interface) Tj T* ET
Q
Q
q
@@ -3008,7 +3229,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 (13) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -3022,7 +3243,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 (15) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -3036,7 +3257,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 (14) 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 (16) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -3050,7 +3271,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 (14) 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 (17) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -3064,34 +3285,34 @@ 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 (15) 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 (17) Tj T* -60.88 0 Td ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 308.0236 cm
+1 0 0 1 62.69291 275.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 242.0236 cm
+1 0 0 1 62.69291 209.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
+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, at least in my opinion.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 152.0236 cm
+1 0 0 1 62.69291 131.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
+BT 1 0 0 1 0 64.82 Tm 2.20186 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, a lot of people are) Tj T* 0 Tw .968555 Tw (obsessed with features and concerned with the possibility of scaling up, forgetting the equally important) Tj T* 0 Tw .048221 Tw (issue of scaling down. This is an old meme in the computing world: programs should address the common) Tj T* 0 Tw .36311 Tw (cases simply and simple things should be kept simple, while at the same keeping difficult things possible.) Tj T* 0 Tw 1.09332 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (adhere as much as possible to this philosophy and it is designed to handle well the simple cases,) Tj T* 0 Tw (while retaining the ability to handle complex cases by relying on the underlying power of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 98.02362 cm
+1 0 0 1 62.69291 89.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
+BT 1 0 0 1 0 28.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 ET
Q
Q
q
@@ -3105,38 +3326,38 @@ Q
endstream
endobj
-% 'R171': class PDFStream
-171 0 obj
+% 'R182': class PDFStream
+182 0 obj
% page stream
-<< /Length 4830 >>
+<< /Length 4921 >>
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
+1 0 0 1 62.69291 729.0236 cm
q
-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
+BT 1 0 0 1 0 28.82 Tm 1.618876 Tw 12 TL /F1 10 Tf 0 0 0 rg (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
Q
Q
q
-1 0 0 1 62.69291 639.0236 cm
+1 0 0 1 62.69291 627.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
+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.127318 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 606.0236 cm
+1 0 0 1 62.69291 594.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 540.0236 cm
+1 0 0 1 62.69291 528.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
+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.053984 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 338.8236 cm
+1 0 0 1 62.69291 326.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3157,9 +3378,9 @@ Q
Q
Q
q
-1 0 0 1 62.69291 234.8236 cm
+1 0 0 1 62.69291 222.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
+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.972927 Tw (should not exist. Actually I think the language should recognize the main function and pass to it the) Tj T* 0 Tw 3.309147 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
Q
q
@@ -3173,11 +3394,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
+n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-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
+BT 1 0 0 1 0 101.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* ET
Q
Q
Q
@@ -3194,14 +3415,14 @@ Q
endstream
endobj
-% 'R172': class PDFStream
-172 0 obj
+% 'R183': class PDFStream
+183 0 obj
% page stream
-<< /Length 4076 >>
+<< /Length 4194 >>
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 715.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3211,31 +3432,31 @@ 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 ( arg = p.parse_args\(\)) Tj T* ( main\(arg.dsn\)) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ( p.add_argument\('dsn'\)) Tj T* ( arg = p.parse_args\(\)) Tj T* ( main\(arg.dsn\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 671.8236 cm
+1 0 0 1 62.69291 659.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 641.8236 cm
+1 0 0 1 62.69291 629.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 524.6236 cm
+1 0 0 1 62.69291 512.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3256,13 +3477,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 492.6236 cm
+1 0 0 1 62.69291 480.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 459.4236 cm
+1 0 0 1 62.69291 447.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3283,7 +3504,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 330.2236 cm
+1 0 0 1 62.69291 318.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3304,22 +3525,22 @@ Q
Q
Q
q
-1 0 0 1 62.69291 310.2236 cm
+1 0 0 1 62.69291 286.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
+BT 1 0 0 1 0 16.82 Tm .167765 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (manages the case of missing arguments and of too many arguments. This is only the tip of) Tj T* 0 Tw (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 277.2236 cm
+1 0 0 1 62.69291 253.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 247.2236 cm
+1 0 0 1 62.69291 223.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
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 2.609984 Tw (The need to have suitable defaults for command-line scripts 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
@@ -3333,10 +3554,10 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 144 re B*
+n -6 -6 468.6898 120 re B*
Q
q
-BT 1 0 0 1 0 125.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# example4.py) Tj T* (from datetime import datetime) Tj T* T* (def main\(dsn, table='product', today=datetime.today\(\)\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn, table, today\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( args = sys.argv[1:]) Tj T* ( if not args:) Tj T* ET
+BT 1 0 0 1 0 101.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# example4.py) Tj T* (from datetime import datetime) Tj T* T* (def main\(dsn, table='product', today=datetime.today\(\)\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn, table, today\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ET
Q
Q
Q
@@ -3353,14 +3574,14 @@ Q
endstream
endobj
-% 'R173': class PDFStream
-173 0 obj
+% 'R184': class PDFStream
+184 0 obj
% page stream
-<< /Length 3963 >>
+<< /Length 3999 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 703.8236 cm
+1 0 0 1 62.69291 679.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3370,23 +3591,23 @@ 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
-BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif len\(args\) ) Tj (>) Tj ( 2:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(argv[2:]\)\)) Tj T* ( main\(*args\)) Tj T* ET
+BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( args = sys.argv[1:]) Tj T* ( if not args:) Tj T* ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif len\(args\) ) Tj (>) Tj ( 2:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(argv[2:]\)\)) Tj T* ( main\(*args\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 635.8236 cm
+1 0 0 1 62.69291 611.8236 cm
q
-BT 1 0 0 1 0 52.82 Tm 1.138935 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here I want to perform a query on a database table, by extracting the today's data: it makes sense for) Tj T* 0 Tw .299988 Tw /F4 10 Tf (today ) Tj /F1 10 Tf (to be a default argument. If there is a most used table \(in this example a table called ) Tj /F4 10 Tf ('product') Tj /F1 10 Tf (\)) Tj T* 0 Tw 2.828735 Tw (it also makes sense to make it a default argument. Performing the parsing of the command lines) Tj T* 0 Tw .083735 Tw (arguments by hand takes 8 ugly lines of boilerplate \(using ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (would require about the same number) Tj T* 0 Tw (of lines\). With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the entire ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block reduces to the usual two lines:) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .038488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here I want to perform a query on a database table, by extracting the most recent data: it makes sense for) Tj T* 0 Tw .299988 Tw /F4 10 Tf (today ) Tj /F1 10 Tf (to be a default argument. If there is a most used table \(in this example a table called ) Tj /F4 10 Tf ('product') Tj /F1 10 Tf (\)) Tj T* 0 Tw 3.313984 Tw (it also makes sense to make it a default argument. Performing the parsing of the command-line) Tj T* 0 Tw .083735 Tw (arguments by hand takes 8 ugly lines of boilerplate \(using ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (would require about the same number) Tj T* 0 Tw (of lines\). With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the entire ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block reduces to the usual two lines:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 590.6236 cm
+1 0 0 1 62.69291 566.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3407,14 +3628,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 570.6236 cm
+1 0 0 1 62.69291 546.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (In other words, six lines of boilerplate have been removed, and we get the usage message for free:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 417.4236 cm
+1 0 0 1 62.69291 393.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3435,13 +3656,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 385.4236 cm
+1 0 0 1 62.69291 361.4236 cm
q
BT 1 0 0 1 0 16.82 Tm .396235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (manages transparently even the case when you want to pass a variable number of arguments. Here) Tj T* 0 Tw (is an example, a script running on a database a series of SQL scripts:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 232.2236 cm
+1 0 0 1 62.69291 208.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3462,7 +3683,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 212.2236 cm
+1 0 0 1 62.69291 188.2236 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
@@ -3479,11 +3700,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
+n -6 -6 468.6898 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (usage: example7.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (Run the given scripts on the database) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( scripts) Tj T* T* ET
+BT 1 0 0 1 0 65.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* ET
Q
Q
Q
@@ -3500,14 +3721,14 @@ Q
endstream
endobj
-% 'R174': class PDFStream
-174 0 obj
+% 'R185': class PDFStream
+185 0 obj
% page stream
-<< /Length 4987 >>
+<< /Length 4674 >>
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 703.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3517,36 +3738,36 @@ 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 (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ( 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 683.8236 cm
+1 0 0 1 62.69291 659.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
+BT 1 0 0 1 0 28.82 Tm .952485 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 641.8236 cm
+1 0 0 1 62.69291 617.8236 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
+BT 1 0 0 1 0 28.82 Tm 3.036235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is inspired to an old Python Cookbook recipe \() Tj 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (\), in the sense that it delivers the) Tj T* 0 Tw .847209 Tw (programmer from the burden of writing the parser, but is less of a hack: instead of extracting the parser) Tj T* 0 Tw (from the docstring of the module, it 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 611.8236 cm
+1 0 0 1 62.69291 587.8236 cm
q
BT 1 0 0 1 0 16.82 Tm .319987 Tw 12 TL /F1 10 Tf 0 0 0 rg (The idea comes from the ) Tj /F5 10 Tf (function annotations ) Tj /F1 10 Tf (concept, a new feature of Python 3. An example is worth a) Tj T* 0 Tw (thousand words, so here it is:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 458.6236 cm
+1 0 0 1 62.69291 434.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3567,13 +3788,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 426.6236 cm
+1 0 0 1 62.69291 402.6236 cm
q
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 285.4236 cm
+1 0 0 1 62.69291 261.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3594,42 +3815,21 @@ Q
Q
Q
q
-1 0 0 1 62.69291 265.4236 cm
+1 0 0 1 62.69291 241.4236 cm
q
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 232.4236 cm
+1 0 0 1 62.69291 208.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
-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
+1 0 0 1 62.69291 118.4236 cm
q
-0 0 0 rg
-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
+BT 1 0 0 1 0 76.82 Tm .016457 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 2.557126 Tw (Instead, I have always relied on the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe, which provides a convenient wrapper over) Tj T* 0 Tw 1.09061 Tw 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (. Alternatively, in the simplest cases, I have just performed the parsing by hand. In ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the) Tj T* 0 Tw (parser is inferred by the function annotations. Here is an example:) Tj T* ET
Q
Q
q
@@ -3643,14 +3843,14 @@ Q
endstream
endobj
-% 'R175': class PDFStream
-175 0 obj
+% 'R186': class PDFStream
+186 0 obj
% page stream
-<< /Length 4202 >>
+<< /Length 4134 >>
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 655.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3660,24 +3860,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 108 re B*
Q
q
0 0 0 rg
-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
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 623.8236 cm
+1 0 0 1 62.69291 599.8236 cm
q
-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
+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 494.6236 cm
+1 0 0 1 62.69291 470.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3698,14 +3898,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 474.6236 cm
+1 0 0 1 62.69291 450.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here are two examples of usage:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 393.4236 cm
+1 0 0 1 62.69291 369.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3726,13 +3926,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 337.4236 cm
+1 0 0 1 62.69291 313.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
+1 0 0 1 62.69291 220.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3753,7 +3953,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 127.0236 cm
+1 0 0 1 62.69291 103.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -3774,13 +3974,6 @@ Q
Q
Q
q
-1 0 0 1 62.69291 107.0236 cm
-q
-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 56.69291 56.69291 cm
q
0 0 0 rg
@@ -3791,14 +3984,21 @@ Q
endstream
endobj
-% 'R176': class PDFStream
-176 0 obj
+% 'R187': class PDFStream
+187 0 obj
% page stream
-<< /Length 4357 >>
+<< /Length 4491 >>
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 753.0236 cm
+q
+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 659.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3819,13 +4019,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 639.8236 cm
q
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 602.6236 cm
+1 0 0 1 62.69291 582.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3846,13 +4046,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 570.6236 cm
+1 0 0 1 62.69291 550.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
+1 0 0 1 62.69291 457.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3873,14 +4073,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 457.4236 cm
+1 0 0 1 62.69291 437.4236 cm
q
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 328.2236 cm
+1 0 0 1 62.69291 308.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3901,13 +4101,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 308.2236 cm
+1 0 0 1 62.69291 288.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
+1 0 0 1 62.69291 243.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -3928,19 +4128,19 @@ Q
Q
Q
q
-1 0 0 1 62.69291 230.0236 cm
+1 0 0 1 62.69291 210.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 200.0236 cm
+1 0 0 1 62.69291 180.0236 cm
q
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 98.27714 cm
+1 0 0 1 62.69291 101.4244 cm
q
q
.96447 0 0 .96447 0 0 cm
@@ -3950,11 +4150,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 486 96 re B*
+n -6 -6 486 72 re B*
Q
q
0 0 0 rg
-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
+BT 1 0 0 1 0 53.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* ET
Q
Q
Q
@@ -3971,14 +4171,14 @@ Q
endstream
endobj
-% 'R177': class PDFStream
-177 0 obj
+% 'R188': class PDFStream
+188 0 obj
% page stream
-<< /Length 5041 >>
+<< /Length 5065 >>
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 703.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3988,18 +4188,18 @@ 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 (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 ( # ...) 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 610.6236 cm
+1 0 0 1 62.69291 586.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4020,7 +4220,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 565.4236 cm
+1 0 0 1 62.69291 541.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4041,37 +4241,37 @@ Q
Q
Q
q
-1 0 0 1 62.69291 521.4236 cm
+1 0 0 1 62.69291 497.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 455.4236 cm
+1 0 0 1 62.69291 431.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 413.4236 cm
+1 0 0 1 62.69291 389.4236 cm
q
BT 1 0 0 1 0 28.82 Tm .897045 Tw 12 TL /F1 10 Tf 0 0 0 rg (I also suggests to specify a one-character abbreviation for flags: in this way you can use the GNU-style) Tj T* 0 Tw 2.034431 Tw (composition of flags \(i.e. ) Tj /F4 10 Tf (-zxvf ) Tj /F1 10 Tf (is an abbreviation of ) Tj /F4 10 Tf (-z -x -v -f) Tj /F1 10 Tf (\). I usually do not provide the) Tj T* 0 Tw (one-character abbreviation for options, since it does not make sense to compose them.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 380.4236 cm
+1 0 0 1 62.69291 356.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 314.4236 cm
+1 0 0 1 62.69291 290.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
+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 2.692339 Tw (2.3\). There is no magic involved; you just need to add the annotations by hand. For instance the) Tj T* 0 Tw (annotated function declaration) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 269.2236 cm
+1 0 0 1 62.69291 245.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4092,14 +4292,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 249.2236 cm
+1 0 0 1 62.69291 225.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 168.0236 cm
+1 0 0 1 62.69291 144.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -4120,7 +4320,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 124.0236 cm
+1 0 0 1 62.69291 100.0236 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
Q
@@ -4136,10 +4336,10 @@ Q
endstream
endobj
-% 'R178': class PDFStream
-178 0 obj
+% 'R189': class PDFStream
+189 0 obj
% page stream
-<< /Length 5962 >>
+<< /Length 5334 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -4166,7 +4366,7 @@ Q
q
1 0 0 1 62.69291 659.8236 cm
q
-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
+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 the core features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (run even on Python 2.3.) Tj T* ET
Q
Q
q
@@ -4205,7 +4405,7 @@ Q
q
1 0 0 1 62.69291 512.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
+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.579431 Tw /F4 10 Tf (abbrev ) Tj /F1 10 Tf (is a one-character string or ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (, ) 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) Tj T* 0 Tw (discrete sequence of values and ) Tj /F4 10 Tf (metavar ) Tj /F1 10 Tf (is a string.) Tj T* ET
Q
Q
q
@@ -4229,11 +4429,11 @@ Q
q
1 0 0 1 62.69291 392.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
+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 \(copied 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 184.4467 cm
+1 0 0 1 62.69291 137.5749 cm
q
q
.976496 0 0 .976496 0 0 cm
@@ -4243,30 +4443,18 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 480 204 re B*
+n -6 -6 480 252 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* ( return result) Tj T* T* (if __name__ == '__main__':) Tj T* ( print\(plac.call\(main\)[0]\)) Tj T* ET
+BT 1 0 0 1 0 233.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* ( if operator == 'mul':) Tj T* ( op = float.__mul__) Tj T* ( result = 1.0) Tj T* ( else: # operator == 'add') Tj T* ( op = float.__add__) Tj T* ( result = 0.0) 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\)\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 152.4467 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.767209 Tw 12 TL /F1 10 Tf 0 0 0 rg (Often 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
-q
-1 0 0 1 62.69291 122.4467 cm
-q
-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 104.4467 cm
+1 0 0 1 62.69291 117.5749 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
@@ -4283,10 +4471,10 @@ Q
endstream
endobj
-% 'R179': class PDFStream
-179 0 obj
+% 'R190': class PDFStream
+190 0 obj
% page stream
-<< /Length 5001 >>
+<< /Length 4567 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -4338,13 +4526,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 450.4849 cm
+1 0 0 1 62.69291 486.4849 cm
q
-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
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (plac.call ) Tj /F1 10 Tf (can also be used in doctests like this:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 393.2849 cm
+1 0 0 1 62.69291 429.2849 cm
q
q
1 0 0 1 0 0 cm
@@ -4357,20 +4545,20 @@ q
n -6 -6 468.6898 48 re B*
Q
q
-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
+BT 1 0 0 1 0 29.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac, 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 373.2849 cm
+1 0 0 1 62.69291 409.2849 cm
q
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 292.0849 cm
+1 0 0 1 62.69291 328.0849 cm
q
q
1 0 0 1 0 0 cm
@@ -4383,38 +4571,26 @@ q
n -6 -6 468.6898 72 re B*
Q
q
-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
+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 260.0849 cm
-q
-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
+1 0 0 1 62.69291 284.0849 cm
q
-1 0 0 1 62.69291 230.0849 cm
-q
-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 197.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 28.82 Tm .158409 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (tries to convert the output of the main function into a list, if possible. If the output is) Tj T* 0 Tw .725703 Tw (not iterable or it is a string, it is left unchanged, but if it is iterable it is converted. In particular, generator) Tj T* 0 Tw (objects are exhausted by ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 155.0849 cm
+1 0 0 1 62.69291 254.0849 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
+BT 1 0 0 1 0 16.82 Tm 1.450751 Tw 12 TL /F1 10 Tf 0 0 0 rg (This behavior avoids mistakes like forgetting of applying ) Tj /F4 10 Tf (list\(result\) ) Tj /F1 10 Tf (to the result of ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (;) Tj T* 0 Tw (moreover it makes errors visible early, and avoids mistakes in code like the following:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 97.8849 cm
+1 0 0 1 62.69291 184.8849 cm
q
q
1 0 0 1 0 0 cm
@@ -4424,15 +4600,27 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 60 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (# dbcli.py) Tj T* (import plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* ET
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (try:) Tj T* ( result = plac.call\(main, args\)) Tj T* (except:) Tj T* ( # do something) Tj T* ET
+Q
Q
Q
Q
Q
+q
+1 0 0 1 62.69291 152.8849 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.986412 Tw 12 TL /F1 10 Tf 0 0 0 rg (Without the ) Tj /F4 10 Tf (listify ) Tj /F1 10 Tf (functionality, a main function returning a generator object would not raise any) Tj T* 0 Tw (exception until the generator is iterated over.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 122.8849 cm
+q
+BT 1 0 0 1 0 16.82 Tm .094269 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover you can rely on type checks like ) Tj /F4 10 Tf (isinstance\(result, list\) ) Tj /F1 10 Tf (for the output of ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (,) Tj T* 0 Tw (to check if the output is an iterable or not.) Tj T* ET
+Q
Q
q
1 0 0 1 56.69291 56.69291 cm
@@ -4445,14 +4633,26 @@ Q
endstream
endobj
-% 'R180': class PDFStream
-180 0 obj
+% 'R191': class PDFStream
+191 0 obj
% page stream
-<< /Length 3386 >>
+<< /Length 3839 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 439.8236 cm
+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 (A realistic example) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 702.0236 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 332.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4462,31 +4662,31 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 324 re B*
+n -6 -6 468.6898 360 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 305.71 Tm /F4 10 Tf 12 TL 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* ( 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
+BT 1 0 0 1 0 341.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* ( 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 371.8236 cm
+1 0 0 1 62.69291 276.8236 cm
q
-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
+BT 1 0 0 1 0 40.82 Tm .049987 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, I use ) Tj /F4 10 Tf (yield) Tj /F1 10 Tf (, and) Tj T* 0 Tw 3.55061 Tw (I perform the print in the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block. The advantage of the pattern is that tests invoking) Tj T* 0 Tw .52936 Tw /F4 10 Tf (plac.call ) Tj /F1 10 Tf (and checking the result become trivial: had I performed the printing in the main function, the) Tj T* 0 Tw (test would have involved an ugly hack like redirecting ) Tj /F4 10 Tf (sys.stdout ) Tj /F1 10 Tf (to a ) Tj /F4 10 Tf (StringIO ) Tj /F1 10 Tf (object.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 353.8236 cm
+1 0 0 1 62.69291 258.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 176.6236 cm
+1 0 0 1 62.69291 93.62362 cm
q
q
1 0 0 1 0 0 cm
@@ -4496,24 +4696,17 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 168 re B*
+n -6 -6 468.6898 156 re B*
Q
q
0 0 0 rg
-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
+BT 1 0 0 1 0 137.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* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 156.6236 cm
-q
-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 56.69291 56.69291 cm
q
0 0 0 rg
@@ -4524,26 +4717,54 @@ Q
endstream
endobj
-% 'R181': class PDFStream
-181 0 obj
+% 'R192': class PDFStream
+192 0 obj
% page stream
-<< /Length 3859 >>
+<< /Length 3848 >>
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 739.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 24 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ( -d |, --delimiter | Column separator) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 719.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can check for yourself that the script works.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 686.8236 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 702.0236 cm
+1 0 0 1 62.69291 644.8236 cm
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
-1 0 0 1 62.69291 464.8236 cm
+1 0 0 1 62.69291 407.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4564,14 +4785,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 444.8236 cm
+1 0 0 1 62.69291 387.6236 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
Q
Q
q
-1 0 0 1 62.69291 315.6236 cm
+1 0 0 1 62.69291 258.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4592,14 +4813,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 295.6236 cm
+1 0 0 1 62.69291 238.4236 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 62.69291 226.4236 cm
+1 0 0 1 62.69291 169.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4620,13 +4841,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 194.4236 cm
+1 0 0 1 62.69291 137.2236 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
Q
Q
q
-1 0 0 1 62.69291 137.2236 cm
+1 0 0 1 62.69291 92.02362 cm
q
q
1 0 0 1 0 0 cm
@@ -4636,60 +4857,491 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 36 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 17.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* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 93.22362 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 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R193': class PDFStream
+193 0 obj
+% page stream
+<< /Length 3629 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 739.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 24 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (example12.py: error: colliding keyword arguments: opt) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 695.8236 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
Q
Q
q
+1 0 0 1 62.69291 662.8236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Final example: a shelve interface) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 620.8236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .603516 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a less trivial example for the keyword arguments feature. The use case is the following: suppose) Tj T* 0 Tw .82881 Tw (we have stored the configuration parameters of a given application into a Python shelve and we need a) Tj T* 0 Tw (command-line tool to edit the shelve. A possible implementation using ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (could be the following:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 120.0682 cm
+q
+q
+.952737 0 0 .952737 0 0 cm
+q
+1 0 0 1 6.6 6.927412 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 492 516 re B*
+Q
+q
+BT 1 0 0 1 0 497.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# ishelve.py) Tj T* (import os, shelve, plac) Tj T* T* (DEFAULT_SHELVE = os.path.expanduser\('~/conf.shelve'\)) Tj T* T* (@plac.annotations\() Tj T* ( help=\('show help', 'flag'\),) Tj T* ( showall=\('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 main\(help, showall, clear, delete, filename=DEFAULT_SHELVE,) Tj T* ( *params, **setters\):) Tj T* ( "A simple interface to a shelve") Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if not any\([help, showall, clear, delete, params, setters]\):) Tj T* ( yield 'no arguments passed, use .help to see the available commands') Tj T* ( elif help: # custom help) Tj T* ( yield 'Commands: .help, .showall, .clear, .delete') Tj T* ( yield ') Tj (<) Tj (param) Tj (>) Tj ( ...') Tj T* ( yield ') Tj (<) Tj (param=value) Tj (>) Tj ( ...') Tj T* ( elif showall:) 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* ( yield '%s: not found' % param ) 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 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R182': class PDFStream
-182 0 obj
+% 'R194': class PDFStream
+194 0 obj
% page stream
-<< /Length 8875 >>
+<< /Length 6972 >>
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 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
+BT 1 0 0 1 0 137.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( 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* (main.add_help = False # there is a custom help, remove the default one) Tj T* (main.prefix_chars = '.' # use dot-prefixed commands) 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 587.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (A few notes are in order:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 581.8236 cm
+Q
+q
+1 0 0 1 62.69291 581.8236 cm
+Q
+q
+1 0 0 1 62.69291 551.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (1.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.075318 Tw 12 TL /F1 10 Tf 0 0 0 rg (I have disabled the ordinary help provided by ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (and I have implemented a custom help) Tj T* 0 Tw (command.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 551.8236 cm
+Q
+q
+1 0 0 1 62.69291 551.8236 cm
+Q
+q
+1 0 0 1 62.69291 533.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (I have changed the prefix character used to recognize the options to a dot.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 533.8236 cm
+Q
+q
+1 0 0 1 62.69291 533.8236 cm
+Q
+q
+1 0 0 1 62.69291 503.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (3.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 16.82 Tm .864985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Keyword arguments recognition \(in the ) Tj /F4 10 Tf (**setters) Tj /F1 10 Tf (\) is used to make it possible to store a value in) Tj T* 0 Tw (the shelve with the syntax ) Tj /F4 10 Tf (param_name=param_value) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 503.8236 cm
+Q
+q
+1 0 0 1 62.69291 503.8236 cm
+Q
+q
+1 0 0 1 62.69291 473.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (4.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 16.82 Tm .649318 Tw 12 TL /F4 10 Tf 0 0 0 rg (*params ) Tj /F1 10 Tf (are used to retrieve parameters from the shelve and some error checking is performed in) Tj T* 0 Tw (the case of missing parameters) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 473.8236 cm
+Q
+q
+1 0 0 1 62.69291 473.8236 cm
+Q
+q
+1 0 0 1 62.69291 455.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (5.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (A command to clear the shelve is implemented as a flag \() Tj /F4 10 Tf (.clear) Tj /F1 10 Tf (\).) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 455.8236 cm
+Q
+q
+1 0 0 1 62.69291 455.8236 cm
+Q
+q
+1 0 0 1 62.69291 437.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (6.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (A command to delete a given parameter is implemented as an option \() Tj /F4 10 Tf (.delete) Tj /F1 10 Tf (\).) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 437.8236 cm
+Q
+q
+1 0 0 1 62.69291 437.8236 cm
+Q
+q
+1 0 0 1 62.69291 419.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (7.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (There is an option with default \() Tj /F4 10 Tf (.filename=conf.shelve) Tj /F1 10 Tf (\) to store the filename of the shelve.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 419.8236 cm
+Q
+q
+1 0 0 1 62.69291 419.8236 cm
+Q
+q
+1 0 0 1 62.69291 377.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 27 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (8.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.001984 Tw 12 TL /F1 10 Tf 0 0 0 rg (All things considered, the code looks like a poor man object oriented interface implemented with a) Tj T* 0 Tw 1.345251 Tw (chain of elifs instead of methods. Of course, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can do better than that, but let me start from a) Tj T* 0 Tw (low-level approach first.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 377.8236 cm
+Q
+q
+1 0 0 1 62.69291 377.8236 cm
+Q
+q
+1 0 0 1 62.69291 359.8236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F4 10 Tf (ishelve.py ) Tj /F1 10 Tf (without arguments you get the following message:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 314.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python ishelve.py) Tj T* (no arguments passed, use .help to see the available commands) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 294.6236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F4 10 Tf (ishelve.py ) Tj /F1 10 Tf (with the option ) Tj /F4 10 Tf (.h ) Tj /F1 10 Tf (\(or any abbreviation of ) Tj /F4 10 Tf (.help) Tj /F1 10 Tf (\) you get:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 225.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 60 re B*
+Q
+q
+BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python ishelve.py .h) Tj T* (Commands: .help, .showall, .clear, .delete) Tj T* (<) Tj (param) Tj (>) Tj ( ...) Tj T* (<) Tj (param=value) Tj (>) Tj ( ...) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 205.4236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can check by hand that the tool work:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 100.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 96 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL ($ python ishelve.py .clear # start from an empty shelve) Tj T* (cleared the shelve) Tj T* ($ python ishelve.py a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* ($ python ishelve.py .showall) Tj T* (b=2) 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 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R195': class PDFStream
+195 0 obj
+% page stream
+<< /Length 8351 >>
+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 /F4 10 Tf 12 TL (a=1) Tj T* ($ python ishelve.py .del b # abbreviation for .delete) Tj T* (deleted b) Tj T* ($ python ishelve.py a) Tj T* (1) Tj T* ($ python ishelve.py b) Tj T* (b: not found) Tj T* ($ python ishelve.py .cler # mispelled command) Tj T* (usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) Tj T* (ishelve.py: error: unrecognized arguments: .cler) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 574.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac vs argparse) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 702.0236 cm
+1 0 0 1 62.69291 532.8236 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
+1 0 0 1 62.69291 526.8236 cm
Q
q
-1 0 0 1 62.69291 696.0236 cm
+1 0 0 1 62.69291 526.8236 cm
Q
q
-1 0 0 1 62.69291 618.0236 cm
+1 0 0 1 62.69291 448.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4709,17 +5361,17 @@ q
Q
Q
q
-1 0 0 1 62.69291 618.0236 cm
+1 0 0 1 62.69291 448.8236 cm
Q
q
-1 0 0 1 62.69291 618.0236 cm
+1 0 0 1 62.69291 448.8236 cm
Q
q
-1 0 0 1 62.69291 576.0236 cm
+1 0 0 1 62.69291 394.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 6 27 cm
+1 0 0 1 6 39 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
@@ -4728,20 +5380,20 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.120751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support "required options". As the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation puts it: ) Tj /F5 10 Tf (Required options) Tj T* 0 Tw 1.075318 Tw (are generally considered bad form - normal users expect options to be optional. You should avoid) Tj T* 0 Tw (the use of required options whenever possible.) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm 1.120751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support "required options". As the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation puts it: ) Tj /F5 10 Tf (Required options) Tj T* 0 Tw 1.075318 Tw (are generally considered bad form - normal users expect options to be optional. You should avoid) Tj T* 0 Tw .874269 Tw (the use of required options whenever possible. ) Tj /F1 10 Tf (Notice that since ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (supports them, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can) Tj T* 0 Tw (manage them too, but not directly.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 576.0236 cm
+1 0 0 1 62.69291 394.8236 cm
Q
q
-1 0 0 1 62.69291 576.0236 cm
+1 0 0 1 62.69291 394.8236 cm
Q
q
-1 0 0 1 62.69291 510.0236 cm
+1 0 0 1 62.69291 328.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4761,13 +5413,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 510.0236 cm
+1 0 0 1 62.69291 328.8236 cm
Q
q
-1 0 0 1 62.69291 510.0236 cm
+1 0 0 1 62.69291 328.8236 cm
Q
q
-1 0 0 1 62.69291 468.0236 cm
+1 0 0 1 62.69291 286.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4787,13 +5439,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 468.0236 cm
+1 0 0 1 62.69291 286.8236 cm
Q
q
-1 0 0 1 62.69291 468.0236 cm
+1 0 0 1 62.69291 286.8236 cm
Q
q
-1 0 0 1 62.69291 438.0236 cm
+1 0 0 1 62.69291 256.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4813,13 +5465,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 438.0236 cm
+1 0 0 1 62.69291 256.8236 cm
Q
q
-1 0 0 1 62.69291 438.0236 cm
+1 0 0 1 62.69291 256.8236 cm
Q
q
-1 0 0 1 62.69291 396.0236 cm
+1 0 0 1 62.69291 214.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4839,31 +5491,48 @@ q
Q
Q
q
-1 0 0 1 62.69291 396.0236 cm
+1 0 0 1 62.69291 214.8236 cm
Q
q
-1 0 0 1 62.69291 396.0236 cm
+1 0 0 1 62.69291 214.8236 cm
Q
q
-1 0 0 1 62.69291 378.0236 cm
+1 0 0 1 62.69291 196.8236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can leverage directly on many ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 336.0236 cm
+1 0 0 1 62.69291 154.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 282.0236 cm
+1 0 0 1 62.69291 100.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 212.8236 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 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R196': class PDFStream
+196 0 obj
+% page stream
+<< /Length 7105 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 703.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4884,43 +5553,26 @@ Q
Q
Q
q
-1 0 0 1 62.69291 168.8236 cm
+1 0 0 1 62.69291 659.8236 cm
q
BT 1 0 0 1 0 28.82 Tm .239318 Tw 12 TL /F1 10 Tf 0 0 0 rg (disables the recognition of the help flag ) Tj /F4 10 Tf (-h, --help) Tj /F1 10 Tf (. This mechanism does not look particularly elegant,) Tj T* 0 Tw .566988 Tw (but it works well enough. I assume that the typical user of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (will be happy with the defaults and would) Tj T* 0 Tw (not want to change them; still it is possible if she wants to.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 138.8236 cm
+1 0 0 1 62.69291 629.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.391235 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, by setting the ) Tj /F4 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add a comment to the usage) Tj T* 0 Tw (message \(by default the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function is used as description\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 108.8236 cm
+1 0 0 1 62.69291 599.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .392619 Tw (It is also possible to change the option prefix; for instance if your script must run under Windows and you) Tj T* 0 Tw (want to use "/" as option prefix you can add the line:) 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 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R183': class PDFStream
-183 0 obj
-% page stream
-<< /Length 6976 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 739.8236 cm
+1 0 0 1 62.69291 566.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4941,19 +5593,19 @@ Q
Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
+1 0 0 1 62.69291 522.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm .924198 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first prefix char \() Tj /F4 10 Tf (/) Tj /F1 10 Tf (\) is used as the default for the recognition of options and flags; the second prefix) Tj T* 0 Tw .26832 Tw (char \() Tj /F4 10 Tf (-) Tj /F1 10 Tf (\) is kept to keep the ) Tj /F4 10 Tf (-h/--help ) Tj /F1 10 Tf (option working: however you can disable it and reimplement it, if) Tj T* 0 Tw (you like. An example will be given in the ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .924198 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first prefix char \() Tj /F4 10 Tf (/) Tj /F1 10 Tf (\) is used as the default for the recognition of options and flags; the second prefix) Tj T* 0 Tw .26832 Tw (char \() Tj /F4 10 Tf (-) Tj /F1 10 Tf (\) is kept to keep the ) Tj /F4 10 Tf (-h/--help ) Tj /F1 10 Tf (option working: however you can disable it and reimplement it, if) Tj T* 0 Tw (you like, as seen in the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (example.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 665.8236 cm
+1 0 0 1 62.69291 492.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
Q
Q
q
-1 0 0 1 62.69291 548.6236 cm
+1 0 0 1 62.69291 399.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4963,53 +5615,53 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
+n -6 -6 468.6898 84 re B*
Q
q
-BT 1 0 0 1 0 89.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print plac.parser_from\(main\)) Tj T* (ArgumentParser\(prog='', usage=None, description=None, version=None,) Tj T* (formatter_class=) Tj (<) Tj (class 'argparse.HelpFormatter') Tj (>) Tj (, conflict_handler='error',) Tj T* (add_help=True\)) Tj T* ET
+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\)\) #doctest: +ELLIPSIS) Tj T* (ArgumentParser\(prog=...\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 516.6236 cm
+1 0 0 1 62.69291 355.4236 cm
q
-BT 1 0 0 1 0 16.82 Tm 3.25748 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (uses ) Tj /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and adds the parser as an attribute ) Tj /F4 10 Tf (.p) Tj /F1 10 Tf (. When) Tj T* 0 Tw /F4 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (is invoked multiple time, the parser is re-used and not rebuilt from scratch again.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 2.646905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (uses ) Tj /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and adds the parser to the main function as an) Tj T* 0 Tw .982126 Tw (attribute. When ) Tj /F4 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (is invoked multiple time, the parser is re-used and not rebuilt from) Tj T* 0 Tw (scratch again.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 486.6236 cm
+1 0 0 1 62.69291 325.4236 cm
q
BT 1 0 0 1 0 16.82 Tm .982765 Tw 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 not need to use it,) Tj T* 0 Tw (unless they want to access ) Tj /F5 10 Tf (all ) Tj /F1 10 Tf (of the features of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (directly without calling the main function.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 432.6236 cm
+1 0 0 1 62.69291 271.4236 cm
q
BT 1 0 0 1 0 40.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 1.257045 Tw (such options 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 except the) Tj T* 0 Tw (addition of the ) Tj /F4 10 Tf (.p ) Tj /F1 10 Tf (attribute.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 399.6236 cm
+1 0 0 1 62.69291 238.4236 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 357.6236 cm
+1 0 0 1 62.69291 196.4236 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 351.6236 cm
+1 0 0 1 62.69291 190.4236 cm
Q
q
-1 0 0 1 62.69291 351.6236 cm
+1 0 0 1 62.69291 190.4236 cm
Q
q
-1 0 0 1 62.69291 333.6236 cm
+1 0 0 1 62.69291 172.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5029,13 +5681,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 333.6236 cm
+1 0 0 1 62.69291 172.4236 cm
Q
q
-1 0 0 1 62.69291 333.6236 cm
+1 0 0 1 62.69291 172.4236 cm
Q
q
-1 0 0 1 62.69291 315.6236 cm
+1 0 0 1 62.69291 154.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5055,72 +5707,90 @@ q
Q
Q
q
-1 0 0 1 62.69291 315.6236 cm
+1 0 0 1 62.69291 154.4236 cm
Q
q
-1 0 0 1 62.69291 315.6236 cm
+1 0 0 1 62.69291 154.4236 cm
Q
q
-1 0 0 1 62.69291 285.6236 cm
+1 0 0 1 62.69291 124.4236 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 243.6236 cm
+1 0 0 1 62.69291 94.42362 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
-Q
-q
-1 0 0 1 62.69291 210.6236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The future) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 132.6236 cm
-q
-BT 1 0 0 1 0 64.82 Tm .135542 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently the core of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is around 200 lines of code, not counting blanks, comments and docstrings. I do) Tj T* 0 Tw .968626 Tw (not plan to extend the core much in the future. The idea is to keep the module short: it is and it should) Tj T* 0 Tw .11811 Tw (remain a little 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 T* 0 Tw 2.307485 Tw 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (becomes successfull and gains a reasonable number of users. For the moment it should be) Tj T* 0 Tw 1.245697 Tw (considered in alpha status, especially for what concerns the features described in the ) Tj 0 0 .501961 rg (advanced usage) Tj T* 0 Tw (document) Tj 0 0 0 rg (, which are implemented in a separated module \() Tj /F4 10 Tf (plac_ext.py) Tj /F1 10 Tf (\).) Tj T* ET
+BT 1 0 0 1 0 16.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 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 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (16) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R184': class PDFStream
-184 0 obj
+% 'R197': class PDFStream
+197 0 obj
% page stream
-<< /Length 4265 >>
+<< /Length 6121 >>
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 753.0236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 720.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The future) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 654.0236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .135542 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently the core of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is around 200 lines of code, not counting blanks, comments and docstrings. I do) Tj T* 0 Tw .968626 Tw (not plan to extend the core much in the future. The idea is to keep the module short: it is and it should) Tj T* 0 Tw .11811 Tw (remain a little 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 T* 0 Tw 2.307485 Tw 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (becomes successfull and gains a reasonable number of users. For the moment it should be) Tj T* 0 Tw (considered in alpha status.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .927488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that even if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (has been designed to be simple to use for simple stuff, its power should not be) Tj T* 0 Tw 1.02186 Tw (underestimated; it is actually a quite advanced tool with a domain of applicability which far exceeds the) Tj T* 0 Tw (realm of command-line arguments parsers.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 546.0236 cm
+q
+BT 1 0 0 1 0 52.82 Tm .285988 Tw 12 TL /F1 10 Tf 0 0 0 rg (Version 0.5 of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (doubled the code base and the documentation: it is based on the idea of using ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to) Tj T* 0 Tw .408555 Tw (implement command-line interpreters, i.e. something akin to the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module in the standard library, only) Tj T* 0 Tw .49936 Tw (better. The new features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (are described in the ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (. They are implemented) Tj T* 0 Tw .313828 Tw (in a separated module \() Tj /F4 10 Tf (plac_ext.py) Tj /F1 10 Tf (\), since they require Python 2.5 to work, whereas ) Tj /F4 10 Tf (plac_core.py) Tj T* 0 Tw /F1 10 Tf (only requires Python 2.3.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 513.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Trivia: the story behind the name) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 678.0236 cm
+1 0 0 1 62.69291 447.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 672.0236 cm
+1 0 0 1 62.69291 441.0236 cm
Q
q
-1 0 0 1 62.69291 672.0236 cm
+1 0 0 1 62.69291 441.0236 cm
Q
q
-1 0 0 1 62.69291 642.0236 cm
+1 0 0 1 62.69291 411.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5140,13 +5810,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 642.0236 cm
+1 0 0 1 62.69291 411.0236 cm
Q
q
-1 0 0 1 62.69291 642.0236 cm
+1 0 0 1 62.69291 411.0236 cm
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 381.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5167,38 +5837,32 @@ q
Q
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 381.0236 cm
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 381.0236 cm
Q
q
-1 0 0 1 62.69291 558.0236 cm
+1 0 0 1 62.69291 327.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 528.0236 cm
+1 0 0 1 62.69291 297.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 ) 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 498.0236 cm
+1 0 0 1 62.69291 267.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 456.0236 cm
-q
-BT 1 0 0 1 0 28.82 Tm .285988 Tw 12 TL /F1 10 Tf 0 0 0 rg (Version 0.5 of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (doubled the code base and the documentation: it is based on the idea of using ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to) Tj T* 0 Tw 1.55229 Tw (implement command-line interpreters, i.e. something like the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module in the standard library, only) Tj T* 0 Tw (better.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 438.0236 cm
+1 0 0 1 62.69291 249.0236 cm
q
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
@@ -5207,139 +5871,155 @@ 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 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (17) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R185': class PDFPageLabels
-185 0 obj
+% 'R198': class PDFPageLabels
+198 0 obj
% Document Root
<< /Nums [ 0
- 186 0 R
+ 199 0 R
1
- 187 0 R
+ 200 0 R
2
- 188 0 R
+ 201 0 R
3
- 189 0 R
+ 202 0 R
4
- 190 0 R
+ 203 0 R
5
- 191 0 R
+ 204 0 R
6
- 192 0 R
+ 205 0 R
7
- 193 0 R
+ 206 0 R
8
- 194 0 R
+ 207 0 R
9
- 195 0 R
+ 208 0 R
10
- 196 0 R
+ 209 0 R
11
- 197 0 R
+ 210 0 R
12
- 198 0 R
+ 211 0 R
13
- 199 0 R
+ 212 0 R
14
- 200 0 R ] >>
+ 213 0 R
+ 15
+ 214 0 R
+ 16
+ 215 0 R ] >>
endobj
-% 'R186': class PDFPageLabel
-186 0 obj
+% 'R199': class PDFPageLabel
+199 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R187': class PDFPageLabel
-187 0 obj
+% 'R200': class PDFPageLabel
+200 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R188': class PDFPageLabel
-188 0 obj
+% 'R201': class PDFPageLabel
+201 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R189': class PDFPageLabel
-189 0 obj
+% 'R202': class PDFPageLabel
+202 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R190': class PDFPageLabel
-190 0 obj
+% 'R203': class PDFPageLabel
+203 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R191': class PDFPageLabel
-191 0 obj
+% 'R204': class PDFPageLabel
+204 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R192': class PDFPageLabel
-192 0 obj
+% 'R205': class PDFPageLabel
+205 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R193': class PDFPageLabel
-193 0 obj
+% 'R206': class PDFPageLabel
+206 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R194': class PDFPageLabel
-194 0 obj
+% 'R207': class PDFPageLabel
+207 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R195': class PDFPageLabel
-195 0 obj
+% 'R208': class PDFPageLabel
+208 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R196': class PDFPageLabel
-196 0 obj
+% 'R209': class PDFPageLabel
+209 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R197': class PDFPageLabel
-197 0 obj
+% 'R210': class PDFPageLabel
+210 0 obj
% None
<< /S /D
/St 12 >>
endobj
-% 'R198': class PDFPageLabel
-198 0 obj
+% 'R211': class PDFPageLabel
+211 0 obj
% None
<< /S /D
/St 13 >>
endobj
-% 'R199': class PDFPageLabel
-199 0 obj
+% 'R212': class PDFPageLabel
+212 0 obj
% None
<< /S /D
/St 14 >>
endobj
-% 'R200': class PDFPageLabel
-200 0 obj
+% 'R213': class PDFPageLabel
+213 0 obj
% None
<< /S /D
/St 15 >>
endobj
+% 'R214': class PDFPageLabel
+214 0 obj
+% None
+<< /S /D
+ /St 16 >>
+endobj
+% 'R215': class PDFPageLabel
+215 0 obj
+% None
+<< /S /D
+ /St 17 >>
+endobj
xref
-0 201
+0 216
0000000000 65535 f
0000000113 00000 n
0000000258 00000 n
@@ -5368,187 +6048,202 @@ 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
-0000008032 00000 n
-0000008294 00000 n
-0000008557 00000 n
-0000008807 00000 n
-0000009056 00000 n
-0000009322 00000 n
-0000009574 00000 n
-0000009824 00000 n
-0000010076 00000 n
-0000010326 00000 n
-0000010578 00000 n
-0000010813 00000 n
-0000011478 00000 n
-0000011730 00000 n
-0000011981 00000 n
-0000012220 00000 n
-0000012415 00000 n
-0000012676 00000 n
-0000012940 00000 n
-0000013174 00000 n
-0000013537 00000 n
-0000013789 00000 n
+0000006324 00000 n
+0000006567 00000 n
+0000006811 00000 n
+0000007055 00000 n
+0000007299 00000 n
+0000007543 00000 n
+0000007787 00000 n
+0000008031 00000 n
+0000008275 00000 n
+0000008518 00000 n
+0000008780 00000 n
+0000009043 00000 n
+0000009293 00000 n
+0000009542 00000 n
+0000009808 00000 n
+0000010060 00000 n
+0000010310 00000 n
+0000010562 00000 n
+0000010812 00000 n
+0000011064 00000 n
+0000011299 00000 n
+0000011982 00000 n
+0000012234 00000 n
+0000012485 00000 n
+0000012724 00000 n
+0000012919 00000 n
+0000013180 00000 n
+0000013444 00000 n
+0000013678 00000 n
0000014041 00000 n
-0000014292 00000 n
-0000014542 00000 n
-0000014779 00000 n
-0000015133 00000 n
-0000015382 00000 n
-0000015634 00000 n
-0000015871 00000 n
-0000016207 00000 n
-0000016459 00000 n
-0000016711 00000 n
-0000016999 00000 n
-0000017251 00000 n
-0000017539 00000 n
-0000017827 00000 n
-0000018064 00000 n
-0000018436 00000 n
-0000018673 00000 n
-0000018991 00000 n
-0000019228 00000 n
-0000019546 00000 n
-0000019783 00000 n
-0000020101 00000 n
-0000020353 00000 n
-0000020605 00000 n
-0000020855 00000 n
-0000021105 00000 n
-0000021355 00000 n
-0000021607 00000 n
-0000021842 00000 n
-0000022214 00000 n
-0000022466 00000 n
-0000022712 00000 n
-0000022975 00000 n
-0000023297 00000 n
-0000023594 00000 n
-0000023846 00000 n
-0000024083 00000 n
-0000024410 00000 n
-0000024661 00000 n
-0000024911 00000 n
-0000025161 00000 n
+0000014293 00000 n
+0000014545 00000 n
+0000014796 00000 n
+0000015046 00000 n
+0000015298 00000 n
+0000015535 00000 n
+0000015898 00000 n
+0000016147 00000 n
+0000016399 00000 n
+0000016636 00000 n
+0000016972 00000 n
+0000017224 00000 n
+0000017476 00000 n
+0000017762 00000 n
+0000018014 00000 n
+0000018302 00000 n
+0000018590 00000 n
+0000018827 00000 n
+0000019199 00000 n
+0000019436 00000 n
+0000019754 00000 n
+0000019991 00000 n
+0000020309 00000 n
+0000020546 00000 n
+0000020864 00000 n
+0000021116 00000 n
+0000021368 00000 n
+0000021618 00000 n
+0000021868 00000 n
+0000022118 00000 n
+0000022370 00000 n
+0000022605 00000 n
+0000022963 00000 n
+0000023260 00000 n
+0000023512 00000 n
+0000023758 00000 n
+0000024021 00000 n
+0000024357 00000 n
+0000024609 00000 n
+0000024846 00000 n
+0000025173 00000 n
0000025411 00000 n
-0000025663 00000 n
-0000025914 00000 n
-0000026167 00000 n
-0000026418 00000 n
-0000026671 00000 n
-0000026922 00000 n
-0000027174 00000 n
-0000027427 00000 n
-0000027705 00000 n
-0000027958 00000 n
-0000028211 00000 n
-0000028488 00000 n
-0000028766 00000 n
-0000029019 00000 n
-0000029270 00000 n
-0000029587 00000 n
-0000029826 00000 n
-0000030341 00000 n
-0000030619 00000 n
-0000030908 00000 n
-0000031160 00000 n
-0000031412 00000 n
-0000031666 00000 n
-0000031920 00000 n
-0000032174 00000 n
-0000032433 00000 n
-0000032690 00000 n
-0000032942 00000 n
-0000033196 00000 n
-0000033454 00000 n
-0000033706 00000 n
-0000033964 00000 n
-0000034218 00000 n
-0000034472 00000 n
-0000034724 00000 n
-0000034976 00000 n
-0000035229 00000 n
-0000035506 00000 n
-0000035770 00000 n
-0000036291 00000 n
-0000036545 00000 n
-0000036832 00000 n
-0000037086 00000 n
-0000037397 00000 n
-0000037649 00000 n
-0000037901 00000 n
-0000038190 00000 n
-0000038442 00000 n
-0000038694 00000 n
-0000038952 00000 n
-0000039206 00000 n
-0000039460 00000 n
-0000039699 00000 n
-0000040126 00000 n
-0000040290 00000 n
-0000040564 00000 n
-0000040693 00000 n
-0000040887 00000 n
-0000041098 00000 n
-0000041308 00000 n
-0000041530 00000 n
-0000041728 00000 n
-0000041933 00000 n
-0000042126 00000 n
-0000042325 00000 n
-0000042522 00000 n
-0000042720 00000 n
-0000042931 00000 n
-0000043123 00000 n
-0000043306 00000 n
-0000043546 00000 n
-0000052607 00000 n
-0000057540 00000 n
-0000061719 00000 n
-0000065785 00000 n
-0000070875 00000 n
-0000075180 00000 n
-0000079640 00000 n
-0000084784 00000 n
-0000090849 00000 n
-0000095953 00000 n
-0000099442 00000 n
-0000103404 00000 n
-0000112382 00000 n
-0000119461 00000 n
-0000123833 00000 n
-0000124130 00000 n
-0000124209 00000 n
-0000124288 00000 n
-0000124367 00000 n
-0000124446 00000 n
-0000124525 00000 n
-0000124604 00000 n
-0000124683 00000 n
-0000124762 00000 n
-0000124841 00000 n
-0000124921 00000 n
-0000125001 00000 n
-0000125081 00000 n
-0000125161 00000 n
-0000125241 00000 n
+0000025729 00000 n
+0000025980 00000 n
+0000026219 00000 n
+0000026549 00000 n
+0000026801 00000 n
+0000027052 00000 n
+0000027303 00000 n
+0000027554 00000 n
+0000027807 00000 n
+0000028058 00000 n
+0000028309 00000 n
+0000028562 00000 n
+0000028815 00000 n
+0000029066 00000 n
+0000029319 00000 n
+0000029570 00000 n
+0000029822 00000 n
+0000030075 00000 n
+0000030353 00000 n
+0000030606 00000 n
+0000030860 00000 n
+0000031138 00000 n
+0000031417 00000 n
+0000031671 00000 n
+0000031923 00000 n
+0000032226 00000 n
+0000032757 00000 n
+0000033011 00000 n
+0000033300 00000 n
+0000033552 00000 n
+0000033804 00000 n
+0000034058 00000 n
+0000034312 00000 n
+0000034566 00000 n
+0000034825 00000 n
+0000035082 00000 n
+0000035334 00000 n
+0000035588 00000 n
+0000035846 00000 n
+0000036098 00000 n
+0000036341 00000 n
+0000036792 00000 n
+0000037046 00000 n
+0000037300 00000 n
+0000037552 00000 n
+0000037804 00000 n
+0000038057 00000 n
+0000038311 00000 n
+0000038565 00000 n
+0000038819 00000 n
+0000039073 00000 n
+0000039352 00000 n
+0000039606 00000 n
+0000039893 00000 n
+0000040147 00000 n
+0000040458 00000 n
+0000040710 00000 n
+0000040962 00000 n
+0000041251 00000 n
+0000041503 00000 n
+0000041755 00000 n
+0000042013 00000 n
+0000042252 00000 n
+0000042759 00000 n
+0000042923 00000 n
+0000043197 00000 n
+0000043326 00000 n
+0000043520 00000 n
+0000043731 00000 n
+0000043941 00000 n
+0000044163 00000 n
+0000044361 00000 n
+0000044566 00000 n
+0000044759 00000 n
+0000044958 00000 n
+0000045155 00000 n
+0000045369 00000 n
+0000045567 00000 n
+0000045778 00000 n
+0000045970 00000 n
+0000046153 00000 n
+0000046412 00000 n
+0000055856 00000 n
+0000060880 00000 n
+0000065177 00000 n
+0000069279 00000 n
+0000074056 00000 n
+0000078293 00000 n
+0000082887 00000 n
+0000088055 00000 n
+0000093492 00000 n
+0000098162 00000 n
+0000102104 00000 n
+0000106055 00000 n
+0000109787 00000 n
+0000116862 00000 n
+0000125316 00000 n
+0000132524 00000 n
+0000138752 00000 n
+0000139079 00000 n
+0000139158 00000 n
+0000139237 00000 n
+0000139316 00000 n
+0000139395 00000 n
+0000139474 00000 n
+0000139553 00000 n
+0000139632 00000 n
+0000139711 00000 n
+0000139790 00000 n
+0000139870 00000 n
+0000139950 00000 n
+0000140030 00000 n
+0000140110 00000 n
+0000140190 00000 n
+0000140270 00000 n
+0000140350 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\312\016\325\031\026V\207X\350\364\303&\322-:\354) (\312\016\325\031\026V\207X\350\364\303&\322-:\354)]
+ [(\270G\023\(\355mFCp\200\3773\015M\207;) (\270G\023\(\355mFCp\200\3773\015M\207;)]
- /Info 154 0 R
- /Root 153 0 R
- /Size 201 >>
+ /Info 164 0 R
+ /Root 163 0 R
+ /Size 216 >>
startxref
-125290
+140399
%%EOF
diff --git a/plac/doc/plac.txt b/plac/doc/plac.txt
index c0e1252..ccbe8f7 100644
--- a/plac/doc/plac.txt
+++ b/plac/doc/plac.txt
@@ -3,9 +3,10 @@ Plac: Parsing the Command Line the Easy Way
:Author: Michele Simionato
:E-mail: michele.simionato@gmail.com
-:Requires: Python 2.3+
+:Date: June 2010
:Download page: http://pypi.python.org/pypi/plac
:Project page: http://micheles.googlecode.com/hg/plac/doc/plac.html
+:Requires: Python 2.3+
:Installation: ``easy_install -U plac``
:License: BSD license
@@ -20,14 +21,14 @@ getopt_ (from the stone age),
optparse_ (from Python 2.3) and argparse_ (from Python 2.7). All of
them are quite powerful and especially argparse_ is an industrial
strength solution; unfortunately, all of them feature a non-zero learning
-curve and a certain verbosity. They do not scale down well enough, at
+curve and a certain verbosity. They do not scale down well, at
least in my opinion.
It should not be necessary to stress the importance `scaling down`_;
-nevertheless most people are obsessed with features and concerned with
-the possibility of scaling up, whereas I think that we should be even
-more concerned with the issue of scaling down. This is an old meme in
-the computing world: programs should address the common cases simply,
+nevertheless, a lot of people are obsessed with features and concerned with
+the possibility of scaling up, forgetting the equally important
+issue of scaling down. This is an old meme in
+the computing world: programs should address the common cases simply and
simple things should be kept simple, while at the same keeping
difficult things possible. plac_ adhere as much as possible to this
philosophy and it is designed to handle well the simple cases, while
@@ -49,7 +50,7 @@ throw-away scripts for themselves, choosing the command line
interface because it is the quick and simple. Such users are not
interested in features, they are interested in a small learning curve:
they just want to be able to write a simple command line tool from a
-simple specification, not to build a command line parser by
+simple specification, not to build a command-line parser by
hand. Unfortunately, the modules in the standard library forces them
to go the hard way. They are designed to implement power user tools
and they have a non-trivial learning curve. On the contrary, plac_
@@ -61,7 +62,7 @@ Scripts with required arguments
Let me start with the simplest possible thing: a script that takes a
single argument and does something to it. It cannot get simpler
-than that, unless you consider a script without command line
+than that, unless you consider a script without command-line
arguments, where there is nothing to parse. Still, it is a use
case *extremely common*: I need to write scripts like that nearly
every day, I wrote hundreds of them in the last few years and I have
@@ -72,9 +73,9 @@ writing by hand for years:
:literal:
As you see the whole ``if __name__ == '__main__'`` block (nine lines)
-is essentially boilerplate that should not exists. Actually I think
+is essentially boilerplate that should not exist. Actually I think
the language should recognize the main function and pass to it the
-command line arguments automatically; unfortunaly this is unlikely to
+command-line arguments automatically; unfortunaly this is unlikely to
happen. I have been writing boilerplate like this in hundreds of
scripts for years, and every time I *hate* it. The purpose of using a
scripting language is convenience and trivial things should be
@@ -108,12 +109,13 @@ underlying argparse_ module) a nice usage message::
.. include:: example3.help
:literal:
+Moreover plac_ manages the case of missing arguments and of too many arguments.
This is only the tip of the iceberg: plac_ is able to do much more than that.
Scripts with default arguments
--------------------------------------------------
-The need to have suitable defaults for command line arguments is quite
+The need to have suitable defaults for command-line scripts is quite
common. For instance I have encountered this use case at work hundreds
of times:
@@ -121,10 +123,10 @@ of times:
:literal:
Here I want to perform a query on a database table, by extracting the
-today's data: it makes sense for ``today`` to be a default argument.
+most recent data: it makes sense for ``today`` to be a default argument.
If there is a most used table (in this example a table called ``'product'``)
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
+of the command-line arguments by hand takes 8 ugly lines of boilerplate
(using argparse_ would require about the same number of lines).
With plac_ the entire ``__main__`` block reduces to the usual two lines::
@@ -150,15 +152,15 @@ Here is the usage message:
:literal:
The examples here should have made clear that *plac is able to figure out
-the command line arguments parser to use from the signature of the main
+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.
-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.
+plac_ is inspired to an old Python Cookbook recipe (optionparse_), 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
@@ -180,13 +182,13 @@ 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
+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
than a thousand of them). Still, this use case cannot be neglected.
The standard library modules (all of them) are quite verbose when it
comes to specifying the options and frankly I have never used them
-directly. Instead, I have always relied on an old recipe of mine, the
+directly. Instead, I have always relied on the
optionparse_ recipe, which provides a convenient wrapper over
optionparse_. Alternatively, in the simplest cases, I have just
performed the parsing by hand. In plac_ the parser is inferred by the
@@ -199,7 +201,7 @@ 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 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=``.
+a short form of the option ``-c``, the long form being ``--command``.
The usage message is the following:
.. include:: example8.help
@@ -306,7 +308,7 @@ think to migrate to Python 3. I am pretty much sure most Pythonistas
are in the same situation. Therefore plac_ provides a way to work
with function annotations even in Python 2.X (including Python 2.3).
There is no magic involved; you just need to add the annotations
-by hand. For instance the annotate function declaration
+by hand. For instance the annotated function declaration
::
@@ -334,7 +336,7 @@ 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 plac_ runs even on Python 2.3.
+that the core features of plac_ run even on Python 2.3.
More features
--------------------------------------------------
@@ -350,7 +352,8 @@ general an annotation is a 6-tuple of the form
where ``help`` is the help message, ``kind`` is a string in the set {
``"flag"``, ``"option"``, ``"positional"``}, ``abbrev`` is a
-one-character string, ``type`` is a callable taking a string in input,
+one-character string or ``None``, ``type`` is a callable taking a
+string in input,
``choices`` is a discrete sequence of values and ``metavar`` is a string.
``type`` is used to automagically convert the command line arguments
@@ -366,22 +369,12 @@ 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.
-Here is an example showing many of the features (taken from the
+Here is an example showing many of the features (copied from the
argparse_ documentation):
.. include:: example10.py
:literal:
-
-Often 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
@@ -398,17 +391,11 @@ 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')
-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:
+``plac.call`` can also be used in doctests like this:
->>> import example10
+>>> import plac, example10
>>> plac.call(example10.main, ['add', '1', '2'])
-['3.0']
+3.0
``plac.call`` works for generators too:
@@ -416,15 +403,29 @@ For instance a doctest may look like this:
... for i in range(int(n)):
... yield i
>>> plac.call(main, ['3'])
-['0', '1', '2']
+[0, 1, 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``.
+Internally ``plac.call`` tries to convert the output of the main function
+into a list, if possible. If the output is not iterable or it is a
+string, it is left unchanged, but if it is iterable it is converted.
+In particular, generator objects are exhausted by ``plac.call``.
-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.
+This behavior avoids mistakes like forgetting of applying
+``list(result)`` to the result of ``plac.call``; moreover it makes
+errors visible early, and avoids mistakes in code like the following::
+
+ try:
+ result = plac.call(main, args)
+ except:
+ # do something
+
+Without the ``listify`` functionality, a main function returning a
+generator object would not raise any exception until the generator
+is iterated over.
+
+Moreover you can rely on type checks like ``isinstance(result, list)``
+for the output of ``plac.call``, to check if the output is an iterable
+or not.
A realistic example
---------------------------------------
@@ -438,20 +439,19 @@ string into a SqlSoup_ object:
: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 function, I use ``yield``, and I 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.
+tests invoking ``plac.call`` and checking the result become trivial:
+had I performed the printing in the main function, the test would have
+involved an ugly hack like redirecting ``sys.stdout`` to a
+``StringIO`` object.
Here is the usage message:
.. include:: dbcli.help
:literal:
-I leave as an exercise for the reader to write doctests for this
-example.
+You can check for yourself that the script works.
Keyword arguments
---------------------------------------
@@ -489,6 +489,75 @@ 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.
+Final example: a shelve interface
+----------------------------------------------------------
+
+Here is a less trivial example for the keyword arguments feature.
+The use case is the following: suppose we have stored the
+configuration parameters of a given application into a Python shelve
+and we need a command-line tool to edit the shelve.
+A possible implementation using plac_ could be the following:
+
+.. include:: ishelve.py
+ :literal:
+
+A few notes are in order:
+
+1. I have disabled the ordinary help provided by argparse_ and I have
+ implemented a custom help command.
+2. I have changed the prefix character used to recognize the options
+ to a dot.
+3. Keyword arguments recognition (in the ``**setters``) is used to make it
+ possible to store a value in the shelve with the syntax
+ ``param_name=param_value``.
+4. ``*params`` are used to retrieve parameters from the shelve and some
+ error checking is performed in the case of missing parameters
+5. A command to clear the shelve is implemented as a flag (``.clear``).
+6. A command to delete a given parameter is implemented as an option
+ (``.delete``).
+7. There is an option with default (``.filename=conf.shelve``) to store
+ the filename of the shelve.
+8. All things considered, the code looks like a poor man object oriented
+ interface implemented with a chain of elifs instead of methods. Of course,
+ plac_ can do better than that, but let me start from a low-level approach
+ first.
+
+If you run ``ishelve.py`` without arguments you get the following
+message::
+
+ $ python ishelve.py
+ no arguments passed, use .help to see the available commands
+
+If you run ``ishelve.py`` with the option ``.h`` (or any abbreviation
+of ``.help``) you get::
+
+ $ python ishelve.py .h
+ Commands: .help, .showall, .clear, .delete
+ <param> ...
+ <param=value> ...
+
+You can check by hand that the tool work::
+
+ $ python ishelve.py .clear # start from an empty shelve
+ cleared the shelve
+ $ python ishelve.py a=1 b=2
+ setting a=1
+ setting b=2
+ $ python ishelve.py .showall
+ b=2
+ a=1
+ $ python ishelve.py .del b # abbreviation for .delete
+ deleted b
+ $ python ishelve.py a
+ 1
+ $ python ishelve.py b
+ b: not found
+ $ python ishelve.py .cler # mispelled command
+ usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
+ [.filename /home/micheles/conf.shelve]
+ [params [params ...]] [setters [setters ...]]
+ ishelve.py: error: unrecognized arguments: .cler
+
plac vs argparse
---------------------------------------------
@@ -509,7 +578,8 @@ following assumes knowledge of argparse_):
- plac_ does not support "required options". As the argparse_
documentation puts it: *Required options are generally considered bad
form - normal users expect options to be optional. You should avoid
- the use of required options whenever possible.*
+ the use of required options whenever possible.* Notice that since
+ argparse_ supports them, plac_ can manage them too, but not directly.
- plac_ supports only regular boolean flags. argparse_ has the ability to
define generalized two-value flags with values different from ``True``
@@ -573,8 +643,8 @@ as option prefix you can add the line::
The first prefix char (``/``) is used
as the default for the recognition of options and flags;
the second prefix char (``-``) is kept to keep the ``-h/--help`` option
-working: however you can disable it and reimplement it, if you like.
-An example will be given in the `advanced usage document`_ .
+working: however you can disable it and reimplement it, if you like,
+as seen in the ``ishelve`` example.
It is possible to access directly the underlying ArgumentParser_ object, by
invoking the ``plac.parser_from`` utility function:
@@ -583,13 +653,11 @@ invoking the ``plac.parser_from`` utility function:
>>> 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)
+>>> print(plac.parser_from(main)) #doctest: +ELLIPSIS
+ArgumentParser(prog=...)
Internally ``plac.call`` uses ``plac.parser_from`` and adds the parser
-as an attribute ``.p``. When ``plac.call(func)`` is
+to the main function as an attribute. When ``plac.call(func)`` is
invoked multiple time, the parser is re-used and not rebuilt from scratch again.
I use ``plac.parser_from`` in the unit tests of the module, but regular
@@ -633,9 +701,20 @@ 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 core back to argparse_ if plac_ becomes successfull
and gains a reasonable number of users. For the moment it should be
-considered in alpha status, especially for what concerns the
-features described in the `advanced usage document`_, which are
-implemented in a separated module (``plac_ext.py``).
+considered in alpha status.
+
+Notice that even if plac_ has been designed to be simple to use for
+simple stuff, its power should not be underestimated; it is actually a
+quite advanced tool with a domain of applicability which far exceeds
+the realm of command-line arguments parsers.
+
+Version 0.5 of plac_ doubled the code base and the documentation: it is
+based on the idea of using plac_ to implement command-line interpreters,
+i.e. something akin to the ``cmd`` module in the standard library, only better.
+The new features of plac_ are described in the `advanced usage document`_ .
+They are implemented in a separated module (``plac_ext.py``), since
+they require Python 2.5 to work, whereas ``plac_core.py`` only requires
+Python 2.3.
Trivia: the story behind the name
-----------------------------------------
@@ -667,10 +746,6 @@ 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!
-Version 0.5 of plac_ doubled the code base and the documentation: it is
-based on the idea of using plac_ to implement command-line interpreters,
-i.e. something like the ``cmd`` module in the standard library, only better.
-
That's all, I hope you will enjoy working with plac_!
.. _argparse: http://argparse.googlecode.com
diff --git a/plac/doc/plac_adv.html b/plac/doc/plac_adv.html
index d9f73b9..908445a 100644
--- a/plac/doc/plac_adv.html
+++ b/plac/doc/plac_adv.html
@@ -5,6 +5,8 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title>Testing and scripting your applications with plac</title>
+<meta name="author" content="Michele Simionato" />
+<meta name="date" content="June 2010" />
<style type="text/css">
.first {
@@ -413,45 +415,60 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<body>
<div class="document" id="testing-and-scripting-your-applications-with-plac">
<h1 class="title">Testing and scripting your applications with plac</h1>
-
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr><th class="docinfo-name">Author:</th>
+<td>Michele Simionato</td></tr>
+<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
+</tr>
+<tr><th class="docinfo-name">Date:</th>
+<td>June 2010</td></tr>
+<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/plac">http://pypi.python.org/pypi/plac</a></td>
+</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 <span class="pre">-U</span> plac</tt></td>
+</tr>
+<tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td>
+</tr>
+<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.5+</td>
+</tr>
+</tbody>
+</table>
+<p><em>The present document discusses a few of the advanced use
+cases for plac. It assumes you have already read an understood the
+basic documentation.</em></p>
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#introduction" id="id1">Introduction</a></li>
-<li><a class="reference internal" href="#a-simple-example-a-shelve-interface" id="id2">A simple example: a shelve interface</a></li>
-<li><a class="reference internal" href="#turning-a-script-into-an-interactive-application" id="id3">Turning a script into an interactive application</a></li>
-<li><a class="reference internal" href="#testing-a-plac-application" id="id4">Testing a plac application</a></li>
-<li><a class="reference internal" href="#plac-easytests" id="id5">Plac easytests</a></li>
-<li><a class="reference internal" href="#plac-batch-scripts" id="id6">Plac batch scripts</a></li>
-<li><a class="reference internal" href="#command-containers" id="id7">Command containers</a></li>
-<li><a class="reference internal" href="#a-non-class-based-example" id="id8">A non class-based example</a></li>
-<li><a class="reference internal" href="#writing-your-own-plac-runner" id="id9">Writing your own plac runner</a></li>
-<li><a class="reference internal" href="#summary" id="id10">Summary</a></li>
-<li><a class="reference internal" href="#appendix-custom-annotation-objects" id="id11">Appendix: custom annotation objects</a></li>
+<li><a class="reference internal" href="#from-scripts-to-interactive-applications" id="id2">From scripts to interactive applications</a></li>
+<li><a class="reference internal" href="#testing-a-plac-application" id="id3">Testing a plac application</a></li>
+<li><a class="reference internal" href="#plac-easy-tests" id="id4">Plac easy tests</a></li>
+<li><a class="reference internal" href="#plac-batch-scripts" id="id5">Plac batch scripts</a></li>
+<li><a class="reference internal" href="#containers-of-commands" id="id6">Containers of commands</a></li>
+<li><a class="reference internal" href="#for-cmd-lovers" id="id7">For <tt class="docutils literal">cmd</tt> lovers</a></li>
+<li><a class="reference internal" href="#the-plac-runner" id="id8">The plac runner</a></li>
+<li><a class="reference internal" href="#a-non-class-based-example" id="id9">A non class-based example</a></li>
+<li><a class="reference internal" href="#writing-your-own-plac-runner" id="id10">Writing your own plac runner</a></li>
+<li><a class="reference internal" href="#summary" id="id11">Summary</a></li>
+<li><a class="reference internal" href="#appendix-custom-annotation-objects" id="id12">Appendix: custom annotation objects</a></li>
</ul>
</div>
<div class="section" id="introduction">
<h1><a class="toc-backref" href="#id1">Introduction</a></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 its
-power should not be underestimated; it is actually a quite advanced
-tool with a domain of applicability which far exceeds the realm of
-command-line arguments parsers.</p>
-<p>In this document I will discuss a few of the advanced use cases for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.
-I assume you have already read an understood the <a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">basic documentation</a>.</p>
-<p>One of the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is to make it dead easy to write
-a scriptable and testable interface for an application, even if originally
-the application is not a command-line application.</p>
-<p>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, and this includes a <em>huge</em> domain
-of applications. A string-oriented interface
-is also a scriptable interface. That means that you can define
-a command language for your application and that it is possible
-to write scripts which are interpreted by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and can be run
-as batch scripts to execute any kind of operations.</p>
+<p>One of the design goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is to make it dead easy to write a
+scriptable and testable interface for an 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, and that includes a <em>huge</em> domain of applications.</p>
+<p>A string-oriented interface is a scriptable interface by
+construction. That means that you can define a command language for
+your application and that it is possible to write scripts which are
+interpretable by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and can be run as batch scripts.</p>
<p>Actually, at the most general level, you can see <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> as a generic tool to
-write domain specific languages (DSL). <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> makes it dead easy
-to write interpreters for command-oriented languages, both
-interactive interpreters and batch interpreters. With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you
+write domain specific languages (DSL). With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you
can test your application interactively as well as with batch
scripts, and even with the analogous of Python doctests for your
defined language.</p>
@@ -460,149 +477,22 @@ you could easily write an application like <a class="reference external" href="h
could use it to script your building procedure. Or any other thing,
your imagination is the only limit!</p>
</div>
-<div class="section" id="a-simple-example-a-shelve-interface">
-<h1><a class="toc-backref" href="#id2">A simple example: a shelve interface</a></h1>
-<p>Since I like to be concrete and to show examples, let me start by
-considering the following use case: you want to store some
-configuration parameters into a Python shelve and you need a
-command-line tool to edit your configuration, i.e. you want a shelve
-interface.
-A possible implementation using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> could be the following:</p>
-<pre class="literal-block">
-# ishelve.py
-import os, shelve
-import plac
-
-DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
-
-&#64;plac.annotations(
- help=('show help', 'flag'),
- showall=('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 main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
- *params, **setters):
- &quot;A simple interface to a shelve&quot;
- sh = shelve.open(filename)
- try:
- if not any([help, showall, clear, delete, params, setters]):
- yield 'no arguments passed, use .help to see the available commands'
- elif help: # custom help
- yield 'Commands: .help, .showall, .clear, .delete'
- yield '&lt;param&gt; ...'
- yield '&lt;param=value&gt; ...'
- elif showall:
- 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()
-
-main.add_help = False # there is a custom help, remove the default one
-main.prefix_chars = '.' # use dot-prefixed commands
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
-
-</pre>
-<p>A few notes are in order:</p>
-<ol class="arabic simple">
-<li>I have disabled the ordinary help provided by <a class="reference external" href="http://argparse.googlecode.com">argparse</a> and I have
-implemented a custom help command.</li>
-<li>I have changed the prefix character used to recognize the options
-to a dot: I like to change the prefix character when I disable the
-default help.</li>
-<li>A plac-specific feature, i.e. keyword arguments recognition is put
-to good use to make it possible to store a value in the shelve with
-the syntax <tt class="docutils literal">param_name=param_value</tt>.</li>
-<li>varargs are used to retrieve parameters from the shelve and some
-error checking is performed in the case of missing parameters</li>
-<li>A command to clear the shelve is implemented as a flag (<tt class="docutils literal">.clear</tt>).</li>
-<li>A command to delete a given parameter is implemented as an option
-(<tt class="docutils literal">.delete</tt>).</li>
-<li>There is an option with default (<tt class="docutils literal">.filename=conf.shelve</tt>) to store
-the filename of the shelve.</li>
-<li>All things considered, the code looks like a poor man object oriented
-interface implemented with a chain of elifs instead of methods. Of course,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can do better than that, but I am starting from a low-level approach
-first.</li>
-</ol>
-<!-- If the script is invoked with no arguments, the default help is
-invoked: this is done by invoking the argparse_ low-level method
-``.format_help``. In other words, the recognition of the couple
-``-h/- -help`` is disabled, but the original help feature is still there. -->
-<p>If you run <tt class="docutils literal">ishelve.py</tt> without arguments you get the following
-message:</p>
-<pre class="literal-block">
-$ python ishelve.py
-no arguments passed, use .help to see the available commands
-</pre>
-<p>If you run <tt class="docutils literal">ishelve.py</tt> with the option <tt class="docutils literal">.h</tt> (or any abbreviation
-of <tt class="docutils literal">.help</tt>) you get:</p>
-<pre class="literal-block">
-$ python ishelve.py .h
-Commands: .help, .showall, .clear, .delete
-&lt;param&gt; ...
-&lt;param=value&gt; ...
-</pre>
-<p>You can check by hand that the tool work:</p>
-<pre class="literal-block">
-$ python ishelve.py .clear # start from an empty shelve
-cleared the shelve
-$ python ishelve.py a=1 b=2
-setting a=1
-setting b=2
-$ python ishelve.py .showall
-b=2
-a=1
-$ python ishelve.py .del b # abbreviation for .delete
-deleted b
-$ python ishelve.py a
-1
-$ python ishelve.py b
-b: not found
-$ python ishelve.py .cler # mispelled command
-usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
-ishelve.py: error: unrecognized arguments: .cler
-</pre>
+<div class="section" id="from-scripts-to-interactive-applications">
+<h1><a class="toc-backref" href="#id2">From scripts to interactive applications</a></h1>
<p>Command-line scripts have many advantages, but are no substitute
-for a real interactive application.</p>
-</div>
-<div class="section" id="turning-a-script-into-an-interactive-application">
-<h1><a class="toc-backref" href="#id3">Turning a script into an interactive application</a></h1>
-<p>If you have a script with a large startup time which must be run
+for interactive applications.
+In particular, if you have a script with a large startup time which must be run
multiple times, it is best to turn it into an interactive application,
so that the startup is performed only once. <tt class="docutils literal">plac</tt> provides an
<tt class="docutils literal">Interpreter</tt> class just for this purpose.</p>
<p>The <tt class="docutils literal">Interpreter</tt> class wraps the main function of a script and
provides an <tt class="docutils literal">.interact</tt> method to start an interactive interpreter
reading commands from the console.</p>
-<p>You could define an interactive interpreter on top of <tt class="docutils literal">ishelve</tt> as
+<p>For instance, you can define an interactive interpreter on top of the
+<tt class="docutils literal">ishelve</tt> script introduced in the <a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">basic documentation</a> as
follows:</p>
<pre class="literal-block">
+# shelve_interpreter.py
import plac, ishelve
&#64;plac.annotations(
@@ -624,10 +514,13 @@ if __name__ == '__main__':
</pre>
<p>A trick has been used here: the ishelve command-line interface has been
-hidden inside and external interface. They are distinct: for instance
+hidden inside an external interface. They are distinct: for instance
the external interface recognizes the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> flag whereas the
internal interface only recognizes the <tt class="docutils literal">.help</tt> command:</p>
<pre class="literal-block">
+$ python shelve_interpreter.py -h
+</pre>
+<pre class="literal-block">
usage: shelve_interpreter.py [-h] [-interactive]
[subcommands [subcommands ...]]
@@ -648,8 +541,8 @@ and non-interactively:</p>
$ python shelve_interpreter.py .clear # non-interactive use
cleared the shelve
</pre>
-<p>Here is an usage session, using <tt class="docutils literal">rlwrap</tt> to enable readline features
-(<tt class="docutils literal">rlwrap</tt> is available in Unix-like systems):</p>
+<p>Here is an usage session, using <a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a> to enable readline features
+(<a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a> is available in Unix-like systems):</p>
<pre class="literal-block">
$ rlwrap python shelve_interpreter.py -i # interactive use
usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]
@@ -675,53 +568,31 @@ i&gt; [CTRL-D]
<p>The <tt class="docutils literal">.interact</tt> method
reads commands from the console and send them to the
underlying interpreter, until the user send a CTRL-D
-command (CTRL-Z in Windows). There are two default
-arguments <tt class="docutils literal"><span class="pre">prompt='i&gt;</span> '</tt>, and <tt class="docutils literal">intro=None</tt> which
-can be used to change the prompt and the message displayed
-at the beginning, which by default is the argparse-provided
-usage message.</p>
+command (CTRL-Z in Windows). There is a default
+argument <tt class="docutils literal"><span class="pre">prompt='i&gt;</span> '</tt> which
+can be used to change the prompt. The message displayed
+by default is the argparse-provided usage message, but can be
+customized by setting an <tt class="docutils literal">.intro</tt> attribute on the main function.</p>
<p>Notice that <tt class="docutils literal">plac.Interpreter</tt> is available only if you are using a recent
version of Python (&gt;= 2.5), because it is a context manager object
which uses extended generators internally.</p>
-<p>The distribution of <tt class="docutils literal">plac</tt> includes a runner script named <tt class="docutils literal">plac_runner.py</tt>
-which will be installed in a suitable directory in your system by distutils
-(say in <tt class="docutils literal">\usr\local\bin\plac_runner.py</tt> in a Unix-like operative system).
-The easiest way to turn a script into an interactive application is
-to use the runner. If you put this alias in your bashrc</p>
-<blockquote>
-<tt class="docutils literal">alias <span class="pre">plac=&quot;rlwrap</span> plac_runner.py&quot;</tt></blockquote>
-<p>(or you define a suitable <tt class="docutils literal">plac.bat</tt> script in Windows) you can
-run the original <tt class="docutils literal">ishelve.py</tt> script in interactive mode as
-follows:</p>
-<pre class="literal-block">
-$ plac -i ishelve.py
-usage: plac_runner.py ishelve.py [.help] [.showall] [.clear]
- [.delete DELETE] [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
-i&gt;
-</pre>
-<p>In other words, there is no need to write the interactive
-wrapper (<tt class="docutils literal">shelve_interpreter.py</tt>) by hand, the plac runner
-does the job for you.</p>
<p>You can conveniently test your application in interactive mode.
However manual testing is a poor substitute for automatic testing.</p>
</div>
<div class="section" id="testing-a-plac-application">
-<h1><a class="toc-backref" href="#id4">Testing a plac application</a></h1>
+<h1><a class="toc-backref" href="#id3">Testing a plac application</a></h1>
<p>In principle, one could write automatic tests for the
<tt class="docutils literal">ishelve</tt> application by using <tt class="docutils literal">plac.call</tt> directly:</p>
<pre class="literal-block">
# test_ishelve.py
-import plac
-from ishelve import ishelve
+import plac, 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']
+ assert plac.call(ishelve.main, ['.clear']) == ['cleared the shelve']
+ assert plac.call(ishelve.main, ['a=1']) == ['setting a=1']
+ assert plac.call(ishelve.main, ['a']) == ['1']
+ assert plac.call(ishelve.main, ['.delete=a']) == ['deleted a']
+ assert plac.call(ishelve.main, ['a']) == ['a: not found']
if __name__ == '__main__':
test()
@@ -755,9 +626,7 @@ and raises an <tt class="docutils literal">AssertionError</tt> if the output pro
interpreter is different from the expected output for the given input.</p>
<p><tt class="docutils literal">AssertionError</tt> is catched by tools like <tt class="docutils literal">py.test</tt> and
<tt class="docutils literal">nosetests</tt> and actually <tt class="docutils literal">plac</tt> tests are intended to be run with
-such tools. If you want to use the <tt class="docutils literal">unittest</tt> module in the
-standard library you can, but I am not going to support it directly
-(reminder: <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is opinionated and I dislike the unittest module).</p>
+such tools.</p>
<p>Interpreters offer a minor syntactic advantage with respect to calling
<tt class="docutils literal">plac.call</tt> directly, but they offer a <em>major</em> semantic advantage when things
go wrong (read exceptions): an <tt class="docutils literal">Interpreter</tt> object internally invokes
@@ -771,19 +640,18 @@ is guaranteed not to raise any exception except <tt class="docutils literal">Ass
initialization code and finalization code
(<tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt> functions) they will be run only
once at the beginning and at the end of the interpreter loop.
-<tt class="docutils literal">plac.call</tt> also executes the initialization/finalization code,
-but it runs it at each call, and that may be too expensive.</p>
+<tt class="docutils literal">plac.call</tt> instead ignores the initialization/finalization code.</p>
</div>
-<div class="section" id="plac-easytests">
-<h1><a class="toc-backref" href="#id5">Plac easytests</a></h1>
+<div class="section" id="plac-easy-tests">
+<h1><a class="toc-backref" href="#id4">Plac easy tests</a></h1>
<p>Writing your tests in terms of <tt class="docutils literal">Interpreter.check</tt> is certainly an
improvement over writing them in terms of <tt class="docutils literal">plac.call</tt>, but they
are still too low-level for my taste. The <tt class="docutils literal">Interpreter</tt> class provides
-support for doctest-style tests, a.k.a. <em>plac easytests</em>.</p>
+support for doctest-style tests, a.k.a. <em>plac easy tests</em>.</p>
<p>By using plac easy tests you can cut and paste your interactive session and
turn it into a runnable automatics test!
Consider for instance the following file <tt class="docutils literal">ishelve.placet</tt> (the <tt class="docutils literal">.placet</tt>
-extension is a mnemonic for plac easytests):</p>
+extension is a mnemonic for plac easy tests):</p>
<pre class="literal-block">
#!ishelve.py
i&gt; .clear # start from a clean state
@@ -802,45 +670,16 @@ SystemExit: unrecognized arguments: .cler
</pre>
<p>Notice the precence of the shebang line containing the name of the
<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> tool to test (a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> tool is just a Python module with a
-function called <tt class="docutils literal">main</tt>). You can doctest it by calling the
-<tt class="docutils literal">.doctest</tt> method of the interpreter</p>
+function called <tt class="docutils literal">main</tt>). The shebang is ignored by the interpreter
+(it looks like a comment to it) but it is there so that external
+tools (say a test runner) can infer the plac interpreter
+to use to test the file.</p>
+<p>You can test <tt class="docutils literal">ishelve.placet</tt> file by calling the
+<tt class="docutils literal">.doctest</tt> method of the interpreter, as in this example:</p>
<pre class="literal-block">
$ python -c&quot;import plac, ishelve
plac.Interpreter(ishelve.main).doctest(open('ishelve.placet'), verbose=True)&quot;
</pre>
-<p>and you will get the following output:</p>
-<pre class="literal-block">
-i&gt; .clear # start from a clean state
--&gt; cleared the shelve
-i&gt; a=1
--&gt; setting a=1
-i&gt; a
--&gt; 1
-i&gt; .del a
--&gt; deleted a
-i&gt; a
--&gt; a: not found
-i&gt; .cler # spelling error
--&gt; SystemExit: unrecognized arguments: .cler
-
-</pre>
-<p>You can also run placets following the shebang convention directly with
-the plac runner:</p>
-<pre class="literal-block">
-$ plac --easytest ishelve.placet
-run 1 plac easy test(s)
-</pre>
-<p>The runner ignore the extension, so you can actually use any extension
-your like, but <em>it relies on the first line of the file to correspond
-to an existing plac tool</em>, so you cannot skip it and you cannot write a
-wrong shebang.</p>
-<p>The plac runner does not provide any test discovery facility,
-but you can use standard Unix tools to help. For instance, you can
-run all the <tt class="docutils literal">.placet</tt> files into a directory and its subdirectories
-as follows:</p>
-<pre class="literal-block">
-$ find . -name \*.placet | xargs plac_runner.py -e
-</pre>
<p>Internally <tt class="docutils literal">Interpreter.doctests</tt> invokes <tt class="docutils literal">Interpreter.check</tt>
multiple times inside the same context and compare the output with the
expected output: if even a check fails, the whole test fail. The
@@ -861,29 +700,31 @@ failing command. This is especially useful if your tests are
stored in external files (plac easy tests does not need to be in
a file: you can just pass to the <tt class="docutils literal">.doctest</tt> method a list of
strings corresponding to the lines of the file).</p>
-<p>It is straighforward to integrate your .placet tests with standard
+<p>At the present plac easy tests do not use any code from the doctest
+module, but the situation may change in the future (it would be nice
+if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> could reuse doctests directives like ELLIPSIS).</p>
+<p>It is straighforward to integrate your <tt class="docutils literal">.placet</tt> tests with standard
testing tools. For instance, you can integrate your doctests with nose
or py.test as follow:</p>
<pre class="literal-block">
-import os, plac
+import os, shlex, plac
def test_doct():
- &quot;&quot;&quot;
- Find all the doctests in the current directory and run them with the
- corresponding plac tool.
- &quot;&quot;&quot;
- transcripts = [f for f in os.listdir('.') if f.endswith('.placet')]
- for transcript in transcripts:
- lines = list(open(transcript))
- assert lines[0].startswith('#!'), 'Missing or incorrect shebang line!'
- tool_path = lines[0][2:].strip() # get the path to the tool to test
- main = plac.import_main(tool_path)
- yield plac.Interpreter(main).doctest, lines[1:]
-
+ &quot;&quot;&quot;
+ Find all the doctests in the current directory and run them with the
+ corresponding plac interpreter (the shebang rules!)
+ &quot;&quot;&quot;
+ placets = [f for f in os.listdir('.') if f.endswith('.placet')]
+ for placet in placets:
+ lines = list(open(placet))
+ assert lines[0].startswith('#!'), 'Missing or incorrect shebang line!'
+ firstline = lines[0][2:] # strip the shebang
+ main = plac.import_main(*shlex.split(firstline))
+ yield plac.Interpreter(main).doctest, lines[1:]
</pre>
-<p>Here you should notice that usage of <tt class="docutils literal">plac.import_main(path)</tt>, an utility
-which is able to import the main function of the specified script.
-You can use both the full path name of the
+<p>Here you should notice that usage of <tt class="docutils literal">plac.import_main</tt>, an utility
+which is able to import the main function of the script specified in
+the shabng line. You can use both the full path name of the
tool, or a relative path name. In this case the runner look at the
environment variable <tt class="docutils literal">PLACPATH</tt> and it searches
the plac tool in the directories specified there (<tt class="docutils literal">PLACPATH</tt> is just
@@ -892,21 +733,18 @@ a string containing directory names separated by colons). If the variable
If the plac tool is not found, an <tt class="docutils literal">ImportError</tt> is raised.</p>
</div>
<div class="section" id="plac-batch-scripts">
-<h1><a class="toc-backref" href="#id6">Plac batch scripts</a></h1>
+<h1><a class="toc-backref" href="#id5">Plac batch scripts</a></h1>
<p>It is pretty easy to realize that an interactive interpreter can
also be used to run batch scripts: instead of reading the commands from
the console, it is enough to read the commands from a file.
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> interpreters provide an <tt class="docutils literal">.execute</tt> method to perform just that:</p>
-<pre class="literal-block">
-plac.Interpreter(main).execute(line_iterator)
-</pre>
-<p>There is just a subtle point to notice: whereas in an interactive
-loop one wants to manage all exceptions, in a batch script we want to
-make sure that the script does not continue in the background
-in case of unexpected errors. The implementation of <tt class="docutils literal">Interpreter.execute</tt>
-makes sure that any error raised by <tt class="docutils literal">plac.call</tt> internally is re-raised.
-In other words, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> interpreters <em>wrap the errors, but does not eat them</em>:
-the errors are always accessible and can be re-raised on demand.</p>
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> interpreters provide an <tt class="docutils literal">.execute</tt> method to perform just that.</p>
+<p>There is just a subtle point to notice: whereas in an interactive loop
+one wants to manage all exceptions, a batch script should not in the
+background in case of unexpected errors. The implementation of
+<tt class="docutils literal">Interpreter.execute</tt> makes sure that any error raised by
+<tt class="docutils literal">plac.call</tt> internally is re-raised. In other words, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
+interpreters <em>wrap the errors, but does not eat them</em>: the errors are
+always accessible and can be re-raised on demand.</p>
<p>In particular consider the following batch file, which contains a syntax
error (<tt class="docutils literal">.dl</tt> instead of <tt class="docutils literal">.del</tt>):</p>
<pre class="literal-block">
@@ -921,10 +759,10 @@ a=1 b=2
</pre>
<p>If you execute the batch file, the interpreter will raise a <tt class="docutils literal">SystemExit</tt>
with an appropriated error message at the <tt class="docutils literal">.dl</tt> line and the last command
-will <em>not</em> be executed. The easiest way to execute the batch file is
-to invoke the <tt class="docutils literal">plac</tt> runner:</p>
+will <em>not</em> be executed:</p>
<pre class="literal-block">
-$ plac --batch --verbose ishelve.batch
+$ python -c &quot;import plac, ishelve
+plac.Interpreter(ishelve.main).execute(open('ishelve.plac'), verbose=True)&quot;
i&gt; .clear
cleared the shelve
i&gt; a=1 b=2
@@ -938,17 +776,17 @@ deleted a
i&gt; .dl b
unrecognized arguments: .dl
</pre>
-<p>The <tt class="docutils literal"><span class="pre">--verbose</span></tt> flag is there to show the lines which are being interpreted
+<p>The <tt class="docutils literal">verbose</tt> flag is there to show the lines which are being interpreted
(prefixed by <tt class="docutils literal">i&gt;</tt>). This is done on purpose, so that you can cut and paste
the output of the batch script and turn it into a <tt class="docutils literal">.placet</tt> test
(cool, isn't it?).</p>
</div>
-<div class="section" id="command-containers">
-<h1><a class="toc-backref" href="#id7">Command containers</a></h1>
-<p>When I discussed the <tt class="docutils literal">ishelve</tt> implementation, I said that it looked
-like a poor man implementation of an object system as a chain of
-elifs; I also said that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was able to do better.
-Here I will substantiate my claim.</p>
+<div class="section" id="containers-of-commands">
+<h1><a class="toc-backref" href="#id6">Containers of commands</a></h1>
+<p>When I discussed the <tt class="docutils literal">ishelve</tt> implementation in the <a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">basic
+documentation</a>, I said that it looked like a poor man implementation
+of an object system as a chain of elifs; I also said that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was
+able to do better. Here I will substantiate my claim.</p>
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is actually able to infer a set of subparsers from a
generic container of commands. This is useful if you want to
implement <em>subcommands</em> (a familiar example of a command-line
@@ -961,15 +799,17 @@ and dispatch hooks (<tt class="docutils literal">__missing__</tt>, invoked for i
object-oriented way as follows:</p>
<pre class="literal-block">
# ishelve2.py
-import shelve, os, sys
-import plac
+import shelve, os, sys, 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', 'showall', 'delete'
- def __init__(self, fname):
- self.fname = fname
+ &#64;plac.annotations(
+ configfile=('path name of the shelve', 'option'))
+ def __init__(self, configfile='~/conf.shelve'):
+ self.fname = os.path.expanduser(configfile)
+ self.intro = 'Operating on %s. Available commands:\n%s' % (
+ self.fname, '\n'.join(c for c in self.commands))
def __enter__(self):
self.sh = shelve.open(self.fname)
return self
@@ -980,7 +820,7 @@ class ShelveInterface(object):
def show(self, *names):
&quot;show given parameters&quot;
for name in names:
- yield '%s = %s' % (name, self.sh[name])
+ yield '%s = %s' % (name, self.sh[name]) # no error checking
def showall(self):
&quot;show all parameters&quot;
for name in self.sh:
@@ -992,87 +832,239 @@ class ShelveInterface(object):
self.sh.clear()
else:
yield 'deleting %s' % name
- del self.sh[name]
+ del self.sh[name] # no error checking
def __exit__(self, etype, exc, tb):
self.sh.close()
-main = ShelveInterface(os.path.expanduser('~/conf.shelve'))
+main = ShelveInterface # the main 'function' can also be a class!
if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
+ i = plac.Interpreter(main())
+ i.interact()
</pre>
-<p>You should notice that <tt class="docutils literal">plac.call</tt> understands the context manager
-protocol: if you call an object with <tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt>
-methods, they are invoked in the right order (<tt class="docutils literal">__enter__</tt> before
-the call and <tt class="docutils literal">__exit__</tt> after the call, both in the regular
-and in the exceptional case). Since <tt class="docutils literal">plac.call</tt> does not use
-the <tt class="docutils literal">with</tt> statement internally, such feature works even in old versions
-of Python, before the introduction of the context manager
-protocol (in Python 2.5). In our example,
-the methods <tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt> make sure the the shelve
-is opened and closed correctly even in the case of exceptions. Notice
-that I have not implemented any error checking in the <tt class="docutils literal">show</tt> and
-<tt class="docutils literal">delete</tt> methods on purpose, to verify that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> works
-correctly in the presence of exceptions (in particular I want to show
-that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not &quot;eat&quot; the traceback).</p>
+<p><tt class="docutils literal">plac.Interpreter</tt> objects wrap context manager objects
+consistently. In other words, if you wrap an object with
+<tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt> methods, they are invoked in the right
+order (<tt class="docutils literal">__enter__</tt> before the interpreter loop starts and
+<tt class="docutils literal">__exit__</tt> after the interpreter loop ends, both in the regular and
+in the exceptional case). In our example, the methods <tt class="docutils literal">__enter__</tt>
+and <tt class="docutils literal">__exit__</tt> make sure the the shelve is opened and closed
+correctly even in the case of exceptions. Notice that I have not
+implemented any error checking in the <tt class="docutils literal">show</tt> and <tt class="docutils literal">delete</tt> methods
+on purpose, to verify that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> works correctly in the presence of
+exceptions.</p>
<p>Here is a session of usage on an Unix-like operating system:</p>
<pre class="literal-block">
-$ alias conf=&quot;python ishelve2.py&quot;
-$ conf set a pippo
+$ rlwrap python ishelve2.py
+Operating on /home/micheles/conf.shelve. Available commands:
+set
+show
+showall
+delete
+i&gt; delete
+deleting everything
+i&gt; set a pippo
setting a=pippo
-$ conf set b lippo
+i&gt; set b lippo
setting b=lippo
-$ conf showall
+i&gt; showall
b = lippo
a = pippo
-$ conf show a b
+i&gt; show a b
a = pippo
b = lippo
-$ conf del a # an abbreviation
+i&gt; del a
deleting a
-$ conf showall
+i&gt; showall
b = lippo
-$ conf delete a # notice the full traceback
-Traceback (most recent call last):
- ...
-_bsddb.DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
-</pre>
-<p>Notice that in script mode you get the full traceback, whereas in
-interactive mode the traceback is hidden:</p>
-<pre class="literal-block">
-$ plac -i ishelve2.py
-usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...
-i&gt; del a
+i&gt; delete a
DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
i&gt;
</pre>
-<p>You can see the traceback if you start the runner in verbose mode
-(<tt class="docutils literal">plac <span class="pre">-vi</span> ishelve2.py</tt>).</p>
+<p>Notice that in interactive mode the traceback is hidden, unless
+you pass the <tt class="docutils literal">verbose</tt> flag to the <tt class="docutils literal">Interpreter.interact</tt> method.</p>
<p>The interactive mode of <tt class="docutils literal">plac</tt> can be used as a replacement of the
<tt class="docutils literal">cmd</tt> module in the standard library. There are a few differences,
-however. For instance you miss tab completion, even if use <tt class="docutils literal">rlwrap</tt>
+however. For instance you miss tab completion, even if use <a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a>
(you get persistent command history for free, however). This is not
-a big issue, since <tt class="docutils literal">plac</tt> understands <em>command abbreviations</em>
-(in all modes, not only in interactive mode).</p>
-<p>If an abbreviation is ambiguous, plac warns you:</p>
+a big issue, since <tt class="docutils literal">plac</tt> understands command abbreviations.</p>
+<p>If an abbreviation is ambiguous, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> warns you:</p>
<pre class="literal-block">
-$ plac ishelve2.py -i
+$ rlwrap python ishelve2.py
usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...
i&gt; sh
NameError: Ambiguous command 'sh': matching ['showall', 'show']
</pre>
-<p>Giving the same abbreviation in script mode gives the same error
-but also shows the full traceback.</p>
+</div>
+<div class="section" id="for-cmd-lovers">
+<h1><a class="toc-backref" href="#id7">For <tt class="docutils literal">cmd</tt> lovers</a></h1>
+<p>I have been using the <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module of the standard library for years.
+I have also written a much enhanced <tt class="docutils literal">cmd2</tt> module which we are using
+internally at work and from which I have taken some ideas used in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.
+In many ways <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> makes the <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module obsolete,
+but I realize why many nostalgic souls would still use <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a>, especially
+until <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not grow real auto-completion features, instead of
+relying on <a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a>. But there must not be competition between <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
+and <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a>: actually the two can happily work togethere. For this
+reason I have put in the <tt class="docutils literal">plac_ext</tt> module a few lines of code
+for gluing together <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> and <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, the <tt class="docutils literal">cmd_interface</tt>.
+Using the <tt class="docutils literal">cmd_interface</tt> is quite trivial: give to it a plac
+command container and you will get in exchange a <tt class="docutils literal">cmd.Cmd</tt> object:</p>
+<pre class="literal-block">
+# cmd_ext.py
+from plac_ext import cmd_interface
+import ishelve2
+
+if __name__ == '__main__':
+ cmd_interface(ishelve2.main()).cmdloop()
+
+</pre>
+<p>Here is an example of interactive session:</p>
+<pre class="literal-block">
+$ python cmd_ex.py
+(Cmd) help
+
+Documented commands (type help &lt;topic&gt;):
+========================================
+delete set show showall
+
+Undocumented commands:
+======================
+EOF help
+
+(Cmd) set a 1
+setting a=1
+(Cmd) show a
+a = 1
+(Cmd) showall
+a = 1
+(Cmd) delete b
+KeyError: 'b'
+(Cmd) EOF [or CTRL-D]
+</pre>
+<p>Internally the <tt class="docutils literal">cmd_interface</tt> builds a <tt class="docutils literal">cmd.Cmd</tt> class and adds
+to it the <tt class="docutils literal">do_</tt> methods corresponding to the commands in the container,
+then it returns a <tt class="docutils literal">cmd.Cmd</tt> instance.</p>
+<p>The <tt class="docutils literal">cmd_interface</tt> is just a proof of concept: it is there so that you
+can study the source code and see an example of integration of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
+with a different framework. It may change and even go away in future
+releases of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
+</div>
+<div class="section" id="the-plac-runner">
+<h1><a class="toc-backref" href="#id8">The plac runner</a></h1>
+<p>The distribution of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> includes a runner script named <tt class="docutils literal">plac_runner.py</tt>,
+which will be installed in a suitable directory in your system by <a class="reference external" href="http://docs.python.org/distutils/">distutils</a>
+(say in <tt class="docutils literal">\usr\local\bin\plac_runner.py</tt> in a Unix-like operative system).
+The runner provides many facilities to run <tt class="docutils literal">.plac</tt> scripts and
+<tt class="docutils literal">.placet</tt> files, as well as Python modules containg a <tt class="docutils literal">main</tt>
+object, which can be a function, a command container object or
+even a command container class.</p>
+<p>For instance, suppose you want to execute a script containing commands
+defined in the <tt class="docutils literal">ishelve2</tt> module like the following one:</p>
+<pre class="literal-block">
+#!ishelve2.py -c ~/conf.shelve
+set a 1
+del a
+del a # intentional error
+
+</pre>
+<p>The first line of the <tt class="docutils literal">.plac</tt> script contains the name of the
+python module containing the plac interpreter and the arguments
+which must be passed to its main function in order to be able
+to instantiate an interpreter object. The other lines contains
+commands. Then you can run the script as follows:</p>
+<pre class="literal-block">
+$ plac_runner.py --batch ishelve2.plac
+setting a=1
+deleting a
+Traceback (most recent call last):
+ ...
+_bsddb.DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
+</pre>
+<p>The last command intentionally contained an error, to show that the
+plac runner does not eat the traceback.</p>
+<p>The runner can also be used to run Python modules in interactive
+mode and non-interactive mode. If you put this alias in your bashrc</p>
+<blockquote>
+<tt class="docutils literal">alias <span class="pre">plac=&quot;rlwrap</span> plac_runner.py&quot;</tt></blockquote>
+<p>(or you define a suitable <tt class="docutils literal">plac.bat</tt> script in Windows) you can
+run the <tt class="docutils literal">ishelve2.py</tt> script in interactive mode as
+follows:</p>
+<pre class="literal-block">
+$ plac -i ishelve2.py
+Operating on /home/micheles/conf.shelve. Available commands:
+set
+show
+showall
+delete
+i&gt; del
+deleting everything
+i&gt; set a 1
+setting a=1
+i&gt; set b 2
+setting b=2
+i&gt; show b
+b = 2
+</pre>
+<p>Now you can cut and paste the interactive session an turns into into
+a <tt class="docutils literal">.placet</tt> file like the following:</p>
+<pre class="literal-block">
+#!ishelve2.py -configfile=~/test.shelve
+i&gt; del
+deleting everything
+i&gt; set a 1
+setting a=1
+i&gt; set b 2
+setting b=2
+i&gt; show b
+b = 2
+
+</pre>
+<p>Notice that the first line specifies a test database
+<tt class="docutils literal">~/test.shelve</tt>, to avoid clobbering your default shelve. If you
+mispell the arguments in the first line plac will give you an
+<a class="reference external" href="http://argparse.googlecode.com">argparse</a> error message (just try).</p>
+<p>You can run placets following the shebang convention directly with
+the plac runner:</p>
+<pre class="literal-block">
+$ plac --test ishelve2.placet
+run 1 plac test(s)
+</pre>
+<p>If you want to see the output of the tests, pass the <tt class="docutils literal"><span class="pre">-v/--verbose</span></tt> flag.
+Notice that he runner ignore the extension, so you can actually use any
+extension your like, but <em>it relies on the first line of the file to invoke
+the corresponding plac tool with the given arguments</em>.</p>
+<p>The plac runner does not provide any test discovery facility,
+but you can use standard Unix tools to help. For instance, you can
+run all the <tt class="docutils literal">.placet</tt> files into a directory and its subdirectories
+as follows:</p>
+<pre class="literal-block">
+$ find . -name \*.placet | xargs plac_runner.py -t
+</pre>
+<p>The plac runner expects the main function of your script to
+return a plac tool, i.e. a function or an object with a <tt class="docutils literal">.commands</tt>
+attribute. It this is not the case the runner gracefully exits.</p>
+<p>It also works in non-interactive mode, if you call it as</p>
+<blockquote>
+<tt class="docutils literal">$ plac module.py args ...</tt></blockquote>
+<p>Here is an example:</p>
+<pre class="literal-block">
+$ plac ishelve.py a=1
+setting a=1
+$ plac ishelve.py .show
+a=1
+</pre>
+<p>Notice that it non-interactive mode the runner just invokes <tt class="docutils literal">plac.call</tt>
+on the <tt class="docutils literal">main</tt> object of the Python module.</p>
</div>
<div class="section" id="a-non-class-based-example">
-<h1><a class="toc-backref" href="#id8">A non class-based example</a></h1>
+<h1><a class="toc-backref" href="#id9">A non class-based example</a></h1>
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not force you to use classes to define command containers.
Even a simple function can be a valid command container, it is
enough to add to it a <tt class="docutils literal">.commands</tt> attribute and possibly
<tt class="docutils literal">__enter__</tt> and/or <tt class="docutils literal">__exit__</tt> attributes.</p>
-<p>A Python module is a perfect container of commands. As an
+<p>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">
@@ -1108,7 +1100,7 @@ def __exit__(etype, exc, tb):
main = __import__(__name__) # the module imports itself!
</pre>
-<p>Notice that I have defined both and <tt class="docutils literal">__exit__</tt> hook and a <tt class="docutils literal">__missing__</tt>
+<p>Notice that I have defined both an <tt class="docutils literal">__exit__</tt> hook and a <tt class="docutils literal">__missing__</tt>
hook, invoked for non-existing commands.
The real trick here is the line <tt class="docutils literal">main = __import__(__name__)</tt>, which
define <tt class="docutils literal">main</tt> to be an alias for the current module.</p>
@@ -1145,15 +1137,12 @@ usage message, as well as the documentation for the sub flag <tt class="docutils
<p>Here is an example of a non-interactive session:</p>
<pre class="literal-block">
$ plac vcs.py check url
-ok
checkout
url
$ plac vcs.py st -q
-ok
status
True
$ plac vcs.py co
-ok
commit
None
</pre>
@@ -1176,24 +1165,16 @@ i&gt; [CTRL-D]
ok
</pre>
<p>Notice the invocation of the <tt class="docutils literal">__missing__</tt> hook for non-existing commands.
-Notice also that the <tt class="docutils literal">__exit__</tt> hook gets called differently in interactive
-mode and non-interactive mode: in the first case it is called at the end of the
-interactive loop with a <tt class="docutils literal">GeneratorExit</tt> exception, whereas in
-the second case there is no exception.</p>
+Notice also that the <tt class="docutils literal">__exit__</tt> hook gets called only in interactive
+mode.</p>
<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.</p>
-<p>Technically a multi-parser is a parser object with an attribute <tt class="docutils literal">.subp</tt>
-which is a dictionary of subparsers; each of the methods listed in
-the attribute <tt class="docutils literal">.commands</tt> corresponds to a subparser inferred from
-the method signature. The original object gets a <tt class="docutils literal">.p</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>
</div>
<div class="section" id="writing-your-own-plac-runner">
-<h1><a class="toc-backref" href="#id9">Writing your own plac runner</a></h1>
+<h1><a class="toc-backref" href="#id10">Writing your own plac runner</a></h1>
<p>The runner included in the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> distribution is intentionally kept
-small (around 40 lines of code) so that you can study it and write
+small (around 50 lines of code) so that you can study it and write
your own runner if want to. If you need to go to such level
of detail, you should know that the most important method of
the <tt class="docutils literal">Interpreter</tt> class is the <tt class="docutils literal">.send</tt> method, which takes
@@ -1265,7 +1246,7 @@ loop in a separate process and send commands to it via the Queue
class provided by the <a class="reference external" href="http://docs.python.org/library/multiprocessing.html">multiprocessing</a> module.</p>
</div>
<div class="section" id="summary">
-<h1><a class="toc-backref" href="#id10">Summary</a></h1>
+<h1><a class="toc-backref" href="#id11">Summary</a></h1>
<p>Once <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> claimed to be the easiest command-line arguments parser
in the world. Having read this document you may think that it is not
so easy after all. But it is a false impression. Actually the
@@ -1285,7 +1266,7 @@ or use the <tt class="docutils literal">Interpreter.doctest</tt> feature;</li>
<p>Moreover, remember that <tt class="docutils literal">plac_runner.py</tt> is your friend.</p>
</div>
<div class="section" id="appendix-custom-annotation-objects">
-<h1><a class="toc-backref" href="#id11">Appendix: custom annotation objects</a></h1>
+<h1><a class="toc-backref" href="#id12">Appendix: 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>
@@ -1339,6 +1320,10 @@ 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>
+<!-- If the script is invoked with no arguments, the default help is
+invoked: this is done by invoking the argparse_ low-level method
+``.format_help``. In other words, the recognition of the couple
+``-h/- -help`` is disabled, but the original help feature is still there. -->
</div>
</div>
</body>
diff --git a/plac/doc/plac_adv.pdf b/plac/doc/plac_adv.pdf
index 1eb01a8..1d509d0 100644
--- a/plac/doc/plac_adv.pdf
+++ b/plac/doc/plac_adv.pdf
@@ -6,8 +6,9 @@
<< /F1 2 0 R
/F2 3 0 R
/F3 4 0 R
- /F4 32 0 R
- /F5 37 0 R >>
+ /F4 8 0 R
+ /F5 9 0 R
+ /F6 22 0 R >>
endobj
% 'F1': class PDFType1Font
2 0 obj
@@ -36,116 +37,143 @@ endobj
/Subtype /Type1
/Type /Font >>
endobj
-% 'Annot.NUMBER1': class LinkAnnotation
+% 'Annot.NUMBER1': class PDFDictionary
5 0 obj
-<< /Border [ 0
+<< /A << /S /URI
+ /Type /Action
+ /URI (mailto:michele.simionato@gmail.com) >>
+ /Border [ 0
0
0 ]
- /Contents ()
- /Dest [ 41 0 R
- /XYZ
- 62.69291
- 492.0236
- 0 ]
- /Rect [ 62.69291
- 690.5936
- 121.0229
- 702.5936 ]
+ /Rect [ 153.7323
+ 707.5936
+ 526.5827
+ 719.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER2': class LinkAnnotation
+% 'Annot.NUMBER2': class PDFDictionary
6 0 obj
-<< /Border [ 0
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
0
0 ]
- /Contents ()
- /Dest [ 41 0 R
- /XYZ
- 62.69291
- 492.0236
- 0 ]
- /Rect [ 527.0227
- 690.5936
- 532.5827
- 702.5936 ]
+ /Rect [ 153.7323
+ 677.5936
+ 526.5827
+ 689.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER3': class LinkAnnotation
+% 'Annot.NUMBER3': class PDFDictionary
7 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 153.7323
+ 650.5936
+ 526.5827
+ 662.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F4': class PDFType1Font
+8 0 obj
+% Font Courier
+<< /BaseFont /Courier
+ /Encoding /WinAnsiEncoding
+ /Name /F4
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'F5': class PDFType1Font
+9 0 obj
+% Font Helvetica-Oblique
+<< /BaseFont /Helvetica-Oblique
+ /Encoding /WinAnsiEncoding
+ /Name /F5
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER4': class LinkAnnotation
+10 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 41 0 R
+ /Dest [ 42 0 R
/XYZ
62.69291
- 207.0236
+ 302.0236
0 ]
/Rect [ 62.69291
- 672.5936
- 237.2129
- 684.5936 ]
+ 518.5936
+ 121.0229
+ 530.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER4': class LinkAnnotation
-8 0 obj
+% 'Annot.NUMBER5': class LinkAnnotation
+11 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 41 0 R
+ /Dest [ 42 0 R
/XYZ
62.69291
- 207.0236
+ 302.0236
0 ]
/Rect [ 527.0227
- 672.5936
+ 518.5936
532.5827
- 684.5936 ]
+ 530.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER5': class LinkAnnotation
-9 0 obj
+% 'Annot.NUMBER6': class LinkAnnotation
+12 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 46 0 R
+ /Dest [ 44 0 R
/XYZ
62.69291
765.0236
0 ]
/Rect [ 62.69291
- 654.5936
- 282.1929
- 666.5936 ]
+ 500.5936
+ 249.4129
+ 512.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER6': class LinkAnnotation
-10 0 obj
+% 'Annot.NUMBER7': class LinkAnnotation
+13 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 46 0 R
+ /Dest [ 44 0 R
/XYZ
62.69291
765.0236
0 ]
/Rect [ 527.0227
- 654.5936
+ 500.5936
532.5827
- 666.5936 ]
+ 512.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER7': class LinkAnnotation
-11 0 obj
+% 'Annot.NUMBER8': class LinkAnnotation
+14 0 obj
<< /Border [ 0
0
0 ]
@@ -153,17 +181,17 @@ endobj
/Dest [ 47 0 R
/XYZ
62.69291
- 168.6236
+ 306.6236
0 ]
/Rect [ 62.69291
- 636.5936
+ 482.5936
184.9529
- 648.5936 ]
+ 494.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER8': class LinkAnnotation
-12 0 obj
+% 'Annot.NUMBER9': class LinkAnnotation
+15 0 obj
<< /Border [ 0
0
0 ]
@@ -171,473 +199,459 @@ endobj
/Dest [ 47 0 R
/XYZ
62.69291
- 168.6236
+ 306.6236
0 ]
/Rect [ 527.0227
- 636.5936
+ 482.5936
532.5827
- 648.5936 ]
+ 494.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER9': class LinkAnnotation
-13 0 obj
+% 'Annot.NUMBER10': class LinkAnnotation
+16 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 52 0 R
+ /Dest [ 48 0 R
/XYZ
62.69291
- 741.0236
+ 297.8236
0 ]
/Rect [ 62.69291
- 618.5936
- 131.6229
- 630.5936 ]
+ 464.5936
+ 134.4029
+ 476.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER10': class LinkAnnotation
-14 0 obj
+% 'Annot.NUMBER11': class LinkAnnotation
+17 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 52 0 R
+ /Dest [ 48 0 R
/XYZ
62.69291
- 741.0236
+ 297.8236
0 ]
/Rect [ 527.0227
- 618.5936
+ 464.5936
532.5827
- 630.5936 ]
+ 476.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER11': class LinkAnnotation
-15 0 obj
+% 'Annot.NUMBER12': class LinkAnnotation
+18 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 60 0 R
+ /Dest [ 61 0 R
/XYZ
62.69291
- 170.8826
+ 681.0236
0 ]
/Rect [ 62.69291
- 600.5936
+ 446.5936
148.2829
- 612.5936 ]
+ 458.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER12': class LinkAnnotation
-16 0 obj
+% 'Annot.NUMBER13': class LinkAnnotation
+19 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 60 0 R
+ /Dest [ 61 0 R
/XYZ
62.69291
- 170.8826
+ 681.0236
0 ]
/Rect [ 527.0227
- 600.5936
+ 446.5936
532.5827
- 612.5936 ]
+ 458.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER13': class LinkAnnotation
-17 0 obj
+% 'Annot.NUMBER14': class LinkAnnotation
+20 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 64 0 R
+ /Dest [ 65 0 R
/XYZ
62.69291
- 271.4236
+ 765.0236
0 ]
/Rect [ 62.69291
- 582.5936
- 164.9329
- 594.5936 ]
+ 428.5936
+ 182.7129
+ 440.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER14': class LinkAnnotation
-18 0 obj
+% 'Annot.NUMBER15': class LinkAnnotation
+21 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 64 0 R
+ /Dest [ 65 0 R
/XYZ
62.69291
- 271.4236
+ 765.0236
0 ]
/Rect [ 527.0227
- 582.5936
+ 428.5936
532.5827
- 594.5936 ]
+ 440.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER15': class LinkAnnotation
-19 0 obj
+% 'F6': class PDFType1Font
+22 0 obj
+% Font Courier-Bold
+<< /BaseFont /Courier-Bold
+ /Encoding /WinAnsiEncoding
+ /Name /F6
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER16': class LinkAnnotation
+23 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 69 0 R
+ /Dest [ 85 0 R
/XYZ
62.69291
- 240.8235
+ 765.0236
0 ]
/Rect [ 62.69291
- 564.5936
- 193.8529
- 576.5936 ]
+ 410.5936
+ 135.0429
+ 422.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER16': class LinkAnnotation
-20 0 obj
+% 'Annot.NUMBER17': class LinkAnnotation
+24 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 69 0 R
+ /Dest [ 85 0 R
/XYZ
62.69291
- 240.8235
+ 765.0236
0 ]
- /Rect [ 521.4627
- 564.5936
+ /Rect [ 527.0227
+ 410.5936
532.5827
- 576.5936 ]
+ 422.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER17': class LinkAnnotation
-21 0 obj
+% 'Annot.NUMBER18': class LinkAnnotation
+25 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 73 0 R
+ /Dest [ 85 0 R
/XYZ
62.69291
- 765.0236
+ 181.6236
0 ]
/Rect [ 62.69291
- 546.5936
- 201.6029
- 558.5936 ]
+ 392.5936
+ 137.7129
+ 404.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER18': class LinkAnnotation
-22 0 obj
+% 'Annot.NUMBER19': class LinkAnnotation
+26 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 73 0 R
+ /Dest [ 85 0 R
/XYZ
62.69291
- 765.0236
+ 181.6236
0 ]
- /Rect [ 521.4627
- 546.5936
+ /Rect [ 527.0227
+ 392.5936
532.5827
- 558.5936 ]
+ 404.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER19': class LinkAnnotation
-23 0 obj
+% 'Annot.NUMBER20': class LinkAnnotation
+27 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 77 0 R
+ /Dest [ 89 0 R
/XYZ
62.69291
- 615.8236
+ 371.4236
0 ]
/Rect [ 62.69291
- 528.5936
- 108.2629
- 540.5936 ]
+ 374.5936
+ 193.8529
+ 386.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER20': class LinkAnnotation
-24 0 obj
+% 'Annot.NUMBER21': class LinkAnnotation
+28 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 77 0 R
+ /Dest [ 89 0 R
/XYZ
62.69291
- 615.8236
+ 371.4236
0 ]
/Rect [ 521.4627
- 528.5936
+ 374.5936
532.5827
- 540.5936 ]
+ 386.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER21': class LinkAnnotation
-25 0 obj
+% 'Annot.NUMBER22': class LinkAnnotation
+29 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 77 0 R
+ /Dest [ 92 0 R
/XYZ
62.69291
- 390.8236
+ 384.6236
0 ]
/Rect [ 62.69291
- 510.5936
- 241.6029
- 522.5936 ]
+ 356.5936
+ 201.6029
+ 368.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER22': class LinkAnnotation
-26 0 obj
+% 'Annot.NUMBER23': class LinkAnnotation
+30 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 77 0 R
+ /Dest [ 92 0 R
/XYZ
62.69291
- 390.8236
+ 384.6236
0 ]
/Rect [ 521.4627
- 510.5936
+ 356.5936
532.5827
- 522.5936 ]
+ 368.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER23': class PDFDictionary
-27 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+% 'Annot.NUMBER24': class LinkAnnotation
+31 0 obj
+<< /Border [ 0
0
0 ]
+ /Contents ()
+ /Dest [ 95 0 R
+ /XYZ
+ 62.69291
+ 241.4236
+ 0 ]
/Rect [ 62.69291
- 456.5936
- 83.9408
- 468.5936 ]
+ 338.5936
+ 108.2629
+ 350.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER24': class PDFDictionary
-28 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+% 'Annot.NUMBER25': class LinkAnnotation
+32 0 obj
+<< /Border [ 0
0
0 ]
- /Rect [ 364.4907
- 414.5936
- 382.8307
- 426.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER25': class PDFDictionary
-29 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
- /Border [ 0
- 0
+ /Contents ()
+ /Dest [ 95 0 R
+ /XYZ
+ 62.69291
+ 241.4236
0 ]
- /Rect [ 146.0829
- 402.5936
- 237.7929
- 414.5936 ]
+ /Rect [ 521.4627
+ 338.5936
+ 532.5827
+ 350.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER26': class PDFDictionary
-30 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+% 'Annot.NUMBER26': class LinkAnnotation
+33 0 obj
+<< /Border [ 0
0
0 ]
- /Rect [ 160.8629
- 384.5936
- 184.1608
- 396.5936 ]
+ /Contents ()
+ /Dest [ 97 0 R
+ /XYZ
+ 62.69291
+ 687.0236
+ 0 ]
+ /Rect [ 62.69291
+ 320.5936
+ 241.6029
+ 332.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER27': class PDFDictionary
-31 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+% 'Annot.NUMBER27': class LinkAnnotation
+34 0 obj
+<< /Border [ 0
0
0 ]
- /Rect [ 121.3434
- 354.5936
- 142.5569
- 366.5936 ]
+ /Contents ()
+ /Dest [ 97 0 R
+ /XYZ
+ 62.69291
+ 687.0236
+ 0 ]
+ /Rect [ 521.4627
+ 320.5936
+ 532.5827
+ 332.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'F4': class PDFType1Font
-32 0 obj
-% Font Helvetica-Oblique
-<< /BaseFont /Helvetica-Oblique
- /Encoding /WinAnsiEncoding
- /Name /F4
- /Subtype /Type1
- /Type /Font >>
-endobj
% 'Annot.NUMBER28': class PDFDictionary
-33 0 obj
+35 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 126.6129
- 318.5936
- 147.7329
- 330.5936 ]
+ /Rect [ 185.4471
+ 266.5936
+ 207.1062
+ 278.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER29': class PDFDictionary
-34 0 obj
+36 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 278.4678
- 300.5936
- 300.0328
- 312.5936 ]
+ /Rect [ 177.6784
+ 254.5936
+ 199.6123
+ 266.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER30': class PDFDictionary
-35 0 obj
+37 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 95.87905
- 288.5936
- 118.5152
- 300.5936 ]
+ /Rect [ 62.69291
+ 200.5936
+ 83.81291
+ 212.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER31': class PDFDictionary
-36 0 obj
+38 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 248.4095
- 276.5936
- 270.8808
- 288.5936 ]
+ /Rect [ 278.4678
+ 182.5936
+ 300.0328
+ 194.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'F5': class PDFType1Font
-37 0 obj
-% Font Courier
-<< /BaseFont /Courier
- /Encoding /WinAnsiEncoding
- /Name /F5
- /Subtype /Type1
- /Type /Font >>
-endobj
% 'Annot.NUMBER32': class PDFDictionary
-38 0 obj
+39 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://twill.idyll.org/) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 83.20457
- 234.5936
- 105.3762
- 246.5936 ]
+ /Rect [ 117.3573
+ 170.5936
+ 138.5845
+ 182.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER33': class PDFDictionary
-39 0 obj
+40 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://twill.idyll.org/) >>
/Border [ 0
0
0 ]
- /Rect [ 128.6679
- 234.5936
- 147.0079
- 246.5936 ]
+ /Rect [ 83.20457
+ 128.5936
+ 105.3762
+ 140.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER34': class PDFDictionary
-40 0 obj
+41 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 456.7333
- 147.5936
- 477.9365
- 159.5936 ]
+ /Rect [ 128.6679
+ 128.5936
+ 147.0079
+ 140.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page1': class PDFPage
-41 0 obj
+42 0 obj
% Page dictionary
<< /Annots [ 5 0 R
6 0 R
7 0 R
- 8 0 R
- 9 0 R
10 0 R
11 0 R
12 0 R
@@ -650,7 +664,6 @@ endobj
19 0 R
20 0 R
21 0 R
- 22 0 R
23 0 R
24 0 R
25 0 R
@@ -660,19 +673,22 @@ endobj
29 0 R
30 0 R
31 0 R
+ 32 0 R
33 0 R
34 0 R
35 0 R
36 0 R
+ 37 0 R
38 0 R
39 0 R
- 40 0 R ]
- /Contents 94 0 R
+ 40 0 R
+ 41 0 R ]
+ /Contents 115 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -683,15 +699,31 @@ endobj
/Trans << >>
/Type /Page >>
endobj
+% 'Annot.NUMBER35': class PDFDictionary
+43 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 633.5936
+ 157.1829
+ 645.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
% 'Page2': class PDFPage
-42 0 obj
+44 0 obj
% Page dictionary
-<< /Contents 95 0 R
+<< /Annots [ 43 0 R ]
+ /Contents 116 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -702,85 +734,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER35': class PDFDictionary
-43 0 obj
+% 'Annot.NUMBER36': class PDFDictionary
+45 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://freshmeat.net/projects/rlwrap/) >>
/Border [ 0
0
0 ]
- /Rect [ 304.0655
- 753.5936
- 348.3808
- 765.5936 ]
+ /Rect [ 223.0988
+ 711.3936
+ 255.7481
+ 723.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER36': class PDFDictionary
-44 0 obj
+% 'Annot.NUMBER37': class PDFDictionary
+46 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://freshmeat.net/projects/rlwrap/) >>
/Border [ 0
0
0 ]
- /Rect [ 291.4339
- 567.5936
- 313.6065
- 579.5936 ]
+ /Rect [ 390.8554
+ 711.3936
+ 423.5047
+ 723.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page3': class PDFPage
-45 0 obj
-% Page dictionary
-<< /Annots [ 43 0 R
- 44 0 R ]
- /Contents 96 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 93 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page4': class PDFPage
-46 0 obj
-% Page dictionary
-<< /Contents 97 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 93 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page5': class PDFPage
47 0 obj
% Page dictionary
-<< /Contents 98 0 R
+<< /Annots [ 45 0 R
+ 46 0 R ]
+ /Contents 117 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -791,31 +785,15 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER37': class PDFDictionary
+% 'Page4': class PDFPage
48 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 327.915
- 262.1936
- 351.1161
- 274.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page6': class PDFPage
-49 0 obj
% Page dictionary
-<< /Annots [ 48 0 R ]
- /Contents 99 0 R
+<< /Contents 118 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -827,7 +805,7 @@ endobj
/Type /Page >>
endobj
% 'Annot.NUMBER38': class PDFDictionary
-50 0 obj
+49 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -835,14 +813,14 @@ endobj
0
0 ]
/Rect [ 370.6785
- 442.3936
+ 651.3936
392.4956
- 454.3936 ]
+ 663.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER39': class PDFDictionary
-51 0 obj
+50 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -850,35 +828,14 @@ endobj
0
0 ]
/Rect [ 455.8742
- 442.3936
+ 651.3936
477.6913
- 454.3936 ]
+ 663.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page7': class PDFPage
-52 0 obj
-% Page dictionary
-<< /Annots [ 50 0 R
- 51 0 R ]
- /Contents 100 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 93 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
% 'Annot.NUMBER40': class PDFDictionary
-53 0 obj
+51 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -886,14 +843,14 @@ endobj
0
0 ]
/Rect [ 185.9351
- 583.3936
+ 454.6093
207.4695
- 595.3936 ]
+ 466.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER41': class PDFDictionary
-54 0 obj
+52 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -901,14 +858,14 @@ endobj
0
0 ]
/Rect [ 369.8905
- 583.3936
+ 454.6093
396.425
- 595.3936 ]
+ 466.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER42': class PDFDictionary
-55 0 obj
+53 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -916,14 +873,14 @@ endobj
0
0 ]
/Rect [ 408.8916
- 571.3936
+ 442.6093
427.2316
- 583.3936 ]
+ 454.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER43': class PDFDictionary
-56 0 obj
+54 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -931,14 +888,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 559.3936
+ 430.6093
86.03291
- 571.3936 ]
+ 442.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER44': class PDFDictionary
-57 0 obj
+55 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -946,14 +903,14 @@ endobj
0
0 ]
/Rect [ 91.65423
- 559.3936
+ 430.6093
112.8355
- 571.3936 ]
+ 442.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER45': class PDFDictionary
-58 0 obj
+56 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -961,43 +918,45 @@ endobj
0
0 ]
/Rect [ 223.1366
- 547.3936
+ 418.6093
250.7972
- 559.3936 ]
+ 430.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER46': class PDFDictionary
-59 0 obj
+57 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 460.388
- 123.4526
- 482.0127
- 135.4526 ]
+ /Rect [ 201.0929
+ 322.6093
+ 222.2129
+ 334.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page8': class PDFPage
-60 0 obj
+% 'Page5': class PDFPage
+58 0 obj
% Page dictionary
-<< /Annots [ 53 0 R
+<< /Annots [ 49 0 R
+ 50 0 R
+ 51 0 R
+ 52 0 R
+ 53 0 R
54 0 R
55 0 R
56 0 R
- 57 0 R
- 58 0 R
- 59 0 R ]
- /Contents 101 0 R
+ 57 0 R ]
+ /Contents 119 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1009,36 +968,72 @@ endobj
/Type /Page >>
endobj
% 'Annot.NUMBER47': class PDFDictionary
-61 0 obj
+59 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 302.1995
- 687.3936
- 325.1461
- 699.3936 ]
+ /Rect [ 460.388
+ 633.5936
+ 482.0127
+ 645.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER48': class PDFDictionary
-62 0 obj
+60 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 286.9966
- 223.9936
- 309.0542
- 235.9936 ]
+ /Rect [ 95.32996
+ 567.5936
+ 116.857
+ 579.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
+% 'Page6': class PDFPage
+61 0 obj
+% Page dictionary
+<< /Annots [ 59 0 R
+ 60 0 R ]
+ /Contents 120 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 114 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
% 'Annot.NUMBER49': class PDFDictionary
+62 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 316.3528
+ 729.5936
+ 409.2453
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER50': class PDFDictionary
63 0 obj
<< /A << /S /URI
/Type /Action
@@ -1046,25 +1041,40 @@ endobj
/Border [ 0
0
0 ]
+ /Rect [ 441.0467
+ 717.5936
+ 463.6507
+ 729.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER51': class PDFDictionary
+64 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
/Rect [ 62.69291
- 193.9936
+ 687.5936
84.70395
- 205.9936 ]
+ 699.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page9': class PDFPage
-64 0 obj
+% 'Page7': class PDFPage
+65 0 obj
% Page dictionary
-<< /Annots [ 61 0 R
- 62 0 R
- 63 0 R ]
- /Contents 102 0 R
+<< /Annots [ 62 0 R
+ 63 0 R
+ 64 0 R ]
+ /Contents 121 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1075,47 +1085,63 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER50': class PDFDictionary
-65 0 obj
+% 'Annot.NUMBER52': class PDFDictionary
+66 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 110.3329
- 159.3936
- 132.2079
- 171.3936 ]
+ /Rect [ 431.7904
+ 639.3936
+ 453.7245
+ 651.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER51': class PDFDictionary
-66 0 obj
+% 'Annot.NUMBER53': class PDFDictionary
+67 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://freshmeat.net/projects/rlwrap/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 465.1151
+ 226.1936
+ 495.7839
+ 238.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER54': class PDFDictionary
+68 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 489.0277
- 159.3936
- 510.9027
- 171.3936 ]
+ /Rect [ 206.6529
+ 184.1936
+ 227.7729
+ 196.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page10': class PDFPage
-67 0 obj
+% 'Page8': class PDFPage
+69 0 obj
% Page dictionary
-<< /Annots [ 65 0 R
- 66 0 R ]
- /Contents 103 0 R
+<< /Annots [ 66 0 R
+ 67 0 R
+ 68 0 R ]
+ /Contents 122 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1126,31 +1152,255 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER52': class PDFDictionary
-68 0 obj
+% 'Annot.NUMBER55': class PDFDictionary
+70 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/cmd.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 163.0507
+ 729.5936
+ 185.1123
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER56': class PDFDictionary
+71 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
+ /Rect [ 514.6827
+ 717.5936
+ 533.0227
+ 729.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER57': class PDFDictionary
+72 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 127.0179
+ 705.5936
+ 148.2762
+ 717.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER58': class PDFDictionary
+73 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/cmd.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 197.4628
+ 705.5936
+ 219.2712
+ 717.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER59': class PDFDictionary
+74 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/cmd.html) >>
+ /Border [ 0
+ 0
+ 0 ]
/Rect [ 62.69291
- 205.3935
- 84.72012
- 217.3935 ]
+ 693.5936
+ 81.58291
+ 705.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page11': class PDFPage
-69 0 obj
+% 'Annot.NUMBER60': class PDFDictionary
+75 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 158.1049
+ 693.5936
+ 180.2789
+ 705.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER61': class PDFDictionary
+76 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://freshmeat.net/projects/rlwrap/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 483.7387
+ 693.5936
+ 510.9587
+ 705.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER62': class PDFDictionary
+77 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 244.5366
+ 681.5936
+ 266.7822
+ 693.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER63': class PDFDictionary
+78 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/cmd.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 287.3678
+ 681.5936
+ 306.2578
+ 693.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER64': class PDFDictionary
+79 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/cmd.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 452.6357
+ 669.5936
+ 474.6447
+ 681.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER65': class PDFDictionary
+80 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 494.4437
+ 669.5936
+ 512.7837
+ 681.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER66': class PDFDictionary
+81 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 193.5645
+ 209.1936
+ 215.6228
+ 221.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER67': class PDFDictionary
+82 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 114.3829
+ 197.1936
+ 132.7229
+ 209.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER68': class PDFDictionary
+83 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 149.5469
+ 146.1936
+ 172.1982
+ 158.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER69': class PDFDictionary
+84 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/distutils/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 224.3178
+ 134.1936
+ 260.8853
+ 146.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page9': class PDFPage
+85 0 obj
% Page dictionary
-<< /Annots [ 68 0 R ]
- /Contents 104 0 R
+<< /Annots [ 70 0 R
+ 71 0 R
+ 72 0 R
+ 73 0 R
+ 74 0 R
+ 75 0 R
+ 76 0 R
+ 77 0 R
+ 78 0 R
+ 79 0 R
+ 80 0 R
+ 81 0 R
+ 82 0 R
+ 83 0 R
+ 84 0 R ]
+ /Contents 123 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1161,15 +1411,15 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page12': class PDFPage
-70 0 obj
+% 'Page10': class PDFPage
+86 0 obj
% Page dictionary
-<< /Contents 105 0 R
+<< /Contents 124 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1180,15 +1430,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page13': class PDFPage
-71 0 obj
+% 'Annot.NUMBER70': class PDFDictionary
+87 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 381.1529
+ 744.5936
+ 423.3929
+ 756.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER71': class PDFDictionary
+88 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 335.9936
+ 84.72012
+ 347.9936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page11': class PDFPage
+89 0 obj
% Page dictionary
-<< /Contents 106 0 R
+<< /Annots [ 87 0 R
+ 88 0 R ]
+ /Contents 125 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1199,8 +1481,27 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER53': class PDFDictionary
-72 0 obj
+% 'Page12': class PDFPage
+90 0 obj
+% Page dictionary
+<< /Contents 126 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 114 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER72': class PDFDictionary
+91 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1208,22 +1509,22 @@ endobj
0
0 ]
/Rect [ 182.479
- 729.5936
+ 349.1936
203.7662
- 741.5936 ]
+ 361.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page14': class PDFPage
-73 0 obj
+% 'Page13': class PDFPage
+92 0 obj
% Page dictionary
-<< /Annots [ 72 0 R ]
- /Contents 107 0 R
+<< /Annots [ 91 0 R ]
+ /Contents 127 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1234,8 +1535,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER54': class PDFDictionary
-74 0 obj
+% 'Annot.NUMBER73': class PDFDictionary
+93 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/multiprocessing.html) >>
@@ -1243,14 +1544,14 @@ endobj
0
0 ]
/Rect [ 295.0229
- 631.3936
+ 256.9936
367.2629
- 643.3936 ]
+ 268.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER55': class PDFDictionary
-75 0 obj
+% 'Annot.NUMBER74': class PDFDictionary
+94 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1258,14 +1559,35 @@ endobj
0
0 ]
/Rect [ 91.57623
- 580.3936
+ 205.9936
114.8995
- 592.3936 ]
+ 217.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER56': class PDFDictionary
-76 0 obj
+% 'Page14': class PDFPage
+95 0 obj
+% Page dictionary
+<< /Annots [ 93 0 R
+ 94 0 R ]
+ /Contents 128 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 114 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER75': class PDFDictionary
+96 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1273,24 +1595,22 @@ endobj
0
0 ]
/Rect [ 106.6216
- 355.3936
+ 651.5936
128.3202
- 367.3936 ]
+ 663.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page15': class PDFPage
-77 0 obj
+97 0 obj
% Page dictionary
-<< /Annots [ 74 0 R
- 75 0 R
- 76 0 R ]
- /Contents 108 0 R
+<< /Annots [ 96 0 R ]
+ /Contents 129 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1302,14 +1622,14 @@ endobj
/Type /Page >>
endobj
% 'Page16': class PDFPage
-78 0 obj
+98 0 obj
% Page dictionary
-<< /Contents 109 0 R
+<< /Contents 130 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 93 0 R
+ /Parent 114 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1320,187 +1640,199 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R79': class PDFCatalog
-79 0 obj
+% 'R99': class PDFCatalog
+99 0 obj
% Document Root
-<< /Outlines 81 0 R
- /PageLabels 110 0 R
+<< /Outlines 101 0 R
+ /PageLabels 131 0 R
/PageMode /UseNone
- /Pages 93 0 R
+ /Pages 114 0 R
/Type /Catalog >>
endobj
-% 'R80': class PDFInfo
-80 0 obj
-<< /Author ()
- /CreationDate (D:20100618073441-01'00')
+% 'R100': class PDFInfo
+100 0 obj
+<< /Author (Michele Simionato)
+ /CreationDate (D:20100620110025-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Testing and scripting your applications with plac) >>
endobj
-% 'R81': class PDFOutlines
-81 0 obj
-<< /Count 11
- /First 82 0 R
- /Last 92 0 R
+% 'R101': class PDFOutlines
+101 0 obj
+<< /Count 12
+ /First 102 0 R
+ /Last 113 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-82 0 obj
-<< /Dest [ 41 0 R
+102 0 obj
+<< /Dest [ 42 0 R
/XYZ
62.69291
- 492.0236
+ 302.0236
0 ]
- /Next 83 0 R
- /Parent 81 0 R
+ /Next 103 0 R
+ /Parent 101 0 R
/Title (Introduction) >>
endobj
% 'Outline.1': class OutlineEntryObject
-83 0 obj
-<< /Dest [ 41 0 R
+103 0 obj
+<< /Dest [ 44 0 R
/XYZ
62.69291
- 207.0236
+ 765.0236
0 ]
- /Next 84 0 R
- /Parent 81 0 R
- /Prev 82 0 R
- /Title (A simple example: a shelve interface) >>
+ /Next 104 0 R
+ /Parent 101 0 R
+ /Prev 102 0 R
+ /Title (From scripts to interactive applications) >>
endobj
% 'Outline.2': class OutlineEntryObject
-84 0 obj
-<< /Dest [ 46 0 R
+104 0 obj
+<< /Dest [ 47 0 R
/XYZ
62.69291
- 765.0236
+ 306.6236
0 ]
- /Next 85 0 R
- /Parent 81 0 R
- /Prev 83 0 R
- /Title (Turning a script into an interactive application) >>
+ /Next 105 0 R
+ /Parent 101 0 R
+ /Prev 103 0 R
+ /Title (Testing a plac application) >>
endobj
% 'Outline.3': class OutlineEntryObject
-85 0 obj
-<< /Dest [ 47 0 R
+105 0 obj
+<< /Dest [ 48 0 R
/XYZ
62.69291
- 168.6236
+ 297.8236
0 ]
- /Next 86 0 R
- /Parent 81 0 R
- /Prev 84 0 R
- /Title (Testing a plac application) >>
+ /Next 106 0 R
+ /Parent 101 0 R
+ /Prev 104 0 R
+ /Title (Plac easy tests) >>
endobj
% 'Outline.4': class OutlineEntryObject
-86 0 obj
-<< /Dest [ 52 0 R
+106 0 obj
+<< /Dest [ 61 0 R
/XYZ
62.69291
- 741.0236
+ 681.0236
0 ]
- /Next 87 0 R
- /Parent 81 0 R
- /Prev 85 0 R
- /Title (Plac easytests) >>
+ /Next 107 0 R
+ /Parent 101 0 R
+ /Prev 105 0 R
+ /Title (Plac batch scripts) >>
endobj
% 'Outline.5': class OutlineEntryObject
-87 0 obj
-<< /Dest [ 60 0 R
+107 0 obj
+<< /Dest [ 65 0 R
/XYZ
62.69291
- 170.8826
+ 765.0236
0 ]
- /Next 88 0 R
- /Parent 81 0 R
- /Prev 86 0 R
- /Title (Plac batch scripts) >>
+ /Next 108 0 R
+ /Parent 101 0 R
+ /Prev 106 0 R
+ /Title (Containers of commands) >>
endobj
% 'Outline.6': class OutlineEntryObject
-88 0 obj
-<< /Dest [ 64 0 R
+108 0 obj
+<< /Dest [ 85 0 R
/XYZ
62.69291
- 271.4236
+ 765.0236
0 ]
- /Next 89 0 R
- /Parent 81 0 R
- /Prev 87 0 R
- /Title (Command containers) >>
+ /Next 109 0 R
+ /Parent 101 0 R
+ /Prev 107 0 R
+ /Title (For cmd lovers) >>
endobj
% 'Outline.7': class OutlineEntryObject
-89 0 obj
-<< /Dest [ 69 0 R
+109 0 obj
+<< /Dest [ 85 0 R
/XYZ
62.69291
- 240.8235
+ 181.6236
0 ]
- /Next 90 0 R
- /Parent 81 0 R
- /Prev 88 0 R
- /Title (A non class-based example) >>
+ /Next 110 0 R
+ /Parent 101 0 R
+ /Prev 108 0 R
+ /Title (The plac runner) >>
endobj
% 'Outline.8': class OutlineEntryObject
-90 0 obj
-<< /Dest [ 73 0 R
+110 0 obj
+<< /Dest [ 89 0 R
/XYZ
62.69291
- 765.0236
+ 371.4236
0 ]
- /Next 91 0 R
- /Parent 81 0 R
- /Prev 89 0 R
- /Title (Writing your own plac runner) >>
+ /Next 111 0 R
+ /Parent 101 0 R
+ /Prev 109 0 R
+ /Title (A non class-based example) >>
endobj
% 'Outline.9': class OutlineEntryObject
-91 0 obj
-<< /Dest [ 77 0 R
+111 0 obj
+<< /Dest [ 92 0 R
/XYZ
62.69291
- 615.8236
+ 384.6236
0 ]
- /Next 92 0 R
- /Parent 81 0 R
- /Prev 90 0 R
- /Title (Summary) >>
+ /Next 112 0 R
+ /Parent 101 0 R
+ /Prev 110 0 R
+ /Title (Writing your own plac runner) >>
endobj
% 'Outline.10': class OutlineEntryObject
-92 0 obj
-<< /Dest [ 77 0 R
+112 0 obj
+<< /Dest [ 95 0 R
+ /XYZ
+ 62.69291
+ 241.4236
+ 0 ]
+ /Next 113 0 R
+ /Parent 101 0 R
+ /Prev 111 0 R
+ /Title (Summary) >>
+endobj
+% 'Outline.11': class OutlineEntryObject
+113 0 obj
+<< /Dest [ 97 0 R
/XYZ
62.69291
- 390.8236
+ 687.0236
0 ]
- /Parent 81 0 R
- /Prev 91 0 R
+ /Parent 101 0 R
+ /Prev 112 0 R
/Title (Appendix: custom annotation objects) >>
endobj
-% 'R93': class PDFPages
-93 0 obj
+% 'R114': class PDFPages
+114 0 obj
% page tree
<< /Count 16
- /Kids [ 41 0 R
- 42 0 R
- 45 0 R
- 46 0 R
+ /Kids [ 42 0 R
+ 44 0 R
47 0 R
- 49 0 R
- 52 0 R
- 60 0 R
- 64 0 R
- 67 0 R
+ 48 0 R
+ 58 0 R
+ 61 0 R
+ 65 0 R
69 0 R
- 70 0 R
- 71 0 R
- 73 0 R
- 77 0 R
- 78 0 R ]
+ 85 0 R
+ 86 0 R
+ 89 0 R
+ 90 0 R
+ 92 0 R
+ 95 0 R
+ 97 0 R
+ 98 0 R ]
/Type /Pages >>
endobj
-% 'R94': class PDFStream
-94 0 obj
+% 'R115': class PDFStream
+115 0 obj
% page stream
-<< /Length 7157 >>
+<< /Length 8624 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -1510,511 +1842,437 @@ BT 1 0 0 1 0 9.64 Tm 2.664882 0 Td 24 TL /F2 20 Tf 0 0 0 rg (Testing and scripti
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 (Contents) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 504.0236 cm
+1 0 0 1 62.69291 716.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 0 183 cm
+1 0 0 1 6 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Introduction) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 36.93937 0 Td (Author:) Tj T* -36.93937 0 Td ET
Q
Q
q
-1 0 0 1 397.8898 183 cm
+1 0 0 1 91.03937 3 cm
q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (1) Tj T* -66.44 0 Td ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Michele Simionato) Tj T* ET
Q
Q
q
-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 (A simple example: a shelve interface) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 165 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (1) Tj T* -66.44 0 Td ET
-Q
-Q
+1 0 0 1 62.69291 701.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 0 147 cm
+1 0 0 1 6 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Turning a script into an interactive application) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 39.69937 0 Td (E-mail:) Tj T* -39.69937 0 Td ET
Q
Q
q
-1 0 0 1 397.8898 147 cm
+1 0 0 1 91.03937 3 cm
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (4) Tj T* -66.44 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (michele.simionato@gmail.com) Tj T* 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 (Testing a plac application) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 129 cm
+1 0 0 1 62.69291 686.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
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
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 48.03937 0 Td (Date:) Tj T* -48.03937 0 Td ET
Q
Q
q
-1 0 0 1 0 111 cm
+1 0 0 1 91.03937 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac easytests) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (June 2010) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 111 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 0 93 cm
+1 0 0 1 62.69291 659.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac batch scripts) Tj T* ET
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F2 10 Tf 12 TL 25.25937 0 Td (Download) Tj T* 21.11 0 Td (page:) Tj T* -46.36937 0 Td ET
Q
Q
q
-1 0 0 1 397.8898 93 cm
+1 0 0 1 91.03937 15 cm
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /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 /F1 10 Tf 12 TL (http://pypi.python.org/pypi/plac) Tj T* 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 (Command containers) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 75 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
-Q
-Q
+1 0 0 1 62.69291 644.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 0 57 cm
+1 0 0 1 6 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A non class-based example) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 9.68937 0 Td (Project page:) Tj T* -9.68937 0 Td ET
Q
Q
q
-1 0 0 1 397.8898 57 cm
+1 0 0 1 91.03937 3 cm
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /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 /F1 10 Tf 12 TL (http://micheles.googlecode.com/hg/plac/doc/plac.html) Tj T* ET
Q
Q
q
-1 0 0 1 0 39 cm
-q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Writing your own plac runner) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 39 cm
+1 0 0 1 62.69291 629.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
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 (14) Tj T* -60.88 0 Td ET
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 16.91937 0 Td (Installation:) Tj T* -16.91937 0 Td ET
Q
Q
q
-1 0 0 1 0 21 cm
+1 0 0 1 91.03937 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Summary) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (easy_install -U plac) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 21 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (15) Tj T* -60.88 0 Td ET
Q
Q
q
-1 0 0 1 0 3 cm
+1 0 0 1 62.69291 614.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Appendix: custom annotation objects) Tj T* ET
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 32.46937 0 Td (License:) Tj T* -32.46937 0 Td ET
Q
Q
q
-1 0 0 1 397.8898 3 cm
+1 0 0 1 91.03937 3 cm
q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (15) Tj T* -60.88 0 Td ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (BSD license) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 471.0236 cm
+1 0 0 1 62.69291 599.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Introduction) Tj T* ET
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 26.91937 0 Td (Requires:) Tj T* -26.91937 0 Td ET
Q
Q
q
-1 0 0 1 62.69291 429.0236 cm
+1 0 0 1 91.03937 3 cm
q
-BT 1 0 0 1 0 28.82 Tm .127882 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 its power should not be underestimated; it) Tj T* 0 Tw 3.73186 Tw (is actually a quite advanced tool with a domain of applicability which far exceeds the realm of) Tj T* 0 Tw (command-line arguments parsers.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Python 2.5+) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 399.0236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .316988 Tw 12 TL /F1 10 Tf 0 0 0 rg (In this document I will discuss a few of the advanced use cases for ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. I assume you have already read) Tj T* 0 Tw (an understood the ) Tj 0 0 .501961 rg (basic documentation) Tj 0 0 0 rg (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 369.0236 cm
+1 0 0 1 62.69291 569.0236 cm
q
-BT 1 0 0 1 0 16.82 Tm 2.177988 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 make it dead easy to write a scriptable and testable interface for an) Tj T* 0 Tw (application, even if originally the application is not a command-line application.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F5 10 Tf 12 TL .372339 Tw (The present document discusses a few of the advanced use cases for plac. It assumes you have already) Tj T* 0 Tw (read an understood the basic documentation.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 315.0236 cm
+1 0 0 1 62.69291 536.0236 cm
q
-BT 1 0 0 1 0 40.82 Tm .093488 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can use ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (whenever you have an API with strings in input and strings in output, and this includes a) Tj T* 0 Tw .163318 Tw /F4 10 Tf (huge ) Tj /F1 10 Tf (domain of applications. A string-oriented interface is also a scriptable interface. That means that you) Tj T* 0 Tw 1.714104 Tw (can define a command language for your application and that it is possible to write scripts which are) Tj T* 0 Tw (interpreted by ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and can be run as batch scripts to execute any kind of operations.) Tj T* ET
+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 261.0236 cm
+1 0 0 1 62.69291 314.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 0 201 cm
q
-BT 1 0 0 1 0 40.82 Tm .444987 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually, at the most general level, you can see ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (as a generic tool to write domain specific languages) Tj T* 0 Tw 1.516136 Tw (\(DSL\). ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (makes it dead easy to write interpreters for command-oriented languages, both interactive) Tj T* 0 Tw 1.351318 Tw (interpreters and batch interpreters. With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (you can test your application interactively as well as with) Tj T* 0 Tw (batch scripts, and even with the analogous of Python doctests for your defined language.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Introduction) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 219.0236 cm
+1 0 0 1 397.8898 201 cm
q
-BT 1 0 0 1 0 28.82 Tm .694104 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can easily replace the ) Tj /F5 10 Tf (cmd ) Tj /F1 10 Tf (module of the standard library and you could easily write an application) Tj T* 0 Tw 2.731654 Tw (like ) Tj 0 0 .501961 rg (twill ) Tj 0 0 0 rg (with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Or you could use it to script your building procedure. Or any other thing, your) Tj T* 0 Tw (imagination is the only limit!) Tj T* ET
+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 62.69291 186.0236 cm
+1 0 0 1 0 183 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A simple example: a shelve interface) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (From scripts to interactive applications) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 132.0236 cm
+1 0 0 1 397.8898 183 cm
q
-BT 1 0 0 1 0 40.82 Tm .421567 Tw 12 TL /F1 10 Tf 0 0 0 rg (Since I like to be concrete and to show examples, let me start by considering the following use case: you) Tj T* 0 Tw .74311 Tw (want to store some configuration parameters into a Python shelve and you need a command-line tool to) Tj T* 0 Tw .08311 Tw (edit your configuration, i.e. you want a shelve interface. A possible implementation using ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (could be the) Tj T* 0 Tw (following:) Tj T* ET
+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 56.69291 56.69291 cm
+1 0 0 1 0 165 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
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Testing a plac application) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R95': class PDFStream
-95 0 obj
-% page stream
-<< /Length 2985 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 123.6412 cm
-q
-q
-.952737 0 0 .952737 0 0 cm
-q
-1 0 0 1 6.6 6.927412 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 492 672 re B*
-Q
+1 0 0 1 397.8898 165 cm
q
-BT 1 0 0 1 0 653.71 Tm 12 TL /F5 10 Tf 0 0 0 rg (# ishelve.py) Tj T* (import os, shelve) Tj T* (import plac) Tj T* T* (DEFAULT_SHELVE = os.path.expanduser\('~/conf.shelve'\)) Tj T* T* (@plac.annotations\() Tj T* ( help=\('show help', 'flag'\),) Tj T* ( showall=\('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 main\(help, showall, clear, delete, filename=DEFAULT_SHELVE,) Tj T* ( *params, **setters\):) Tj T* ( "A simple interface to a shelve") Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if not any\([help, showall, clear, delete, params, setters]\):) Tj T* ( yield 'no arguments passed, use .help to see the available commands') Tj T* ( elif help: # custom help) Tj T* ( yield 'Commands: .help, .showall, .clear, .delete') Tj T* ( yield ') Tj (<) Tj (param) Tj (>) Tj ( ...') Tj T* ( yield ') Tj (<) Tj (param=value) Tj (>) Tj ( ...') Tj T* ( elif showall:) 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* ( 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* (main.add_help = False # there is a custom help, remove the default one) Tj T* (main.prefix_chars = '.' # use dot-prefixed commands) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
-Q
-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
Q
Q
q
-1 0 0 1 62.69291 103.6412 cm
+1 0 0 1 0 147 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (A few notes are in order:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac easy tests) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 97.64124 cm
-Q
+1 0 0 1 397.8898 147 cm
q
-1 0 0 1 62.69291 97.64124 cm
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (4) Tj T* -66.44 0 Td ET
+Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 0 129 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
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac batch scripts) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R96': class PDFStream
-96 0 obj
-% page stream
-<< /Length 6841 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 735.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 6 15 cm
+1 0 0 1 397.8898 129 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (1.) Tj T* -5.66 0 Td ET
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (6) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 0 111 cm
q
-BT 1 0 0 1 0 16.82 Tm 2.075318 Tw 12 TL /F1 10 Tf 0 0 0 rg (I have disabled the ordinary help provided by ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (and I have implemented a custom help) Tj T* 0 Tw (command.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Containers of commands) Tj T* ET
Q
Q
q
-Q
-Q
+1 0 0 1 397.8898 111 cm
q
-1 0 0 1 62.69291 735.0236 cm
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
Q
-q
-1 0 0 1 62.69291 735.0236 cm
Q
q
-1 0 0 1 62.69291 705.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 15 cm
+1 0 0 1 0 93 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (For ) Tj /F6 10 Tf (cmd ) Tj /F2 10 Tf (lovers) Tj T* ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 397.8898 93 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .203145 Tw (I have changed the prefix character used to recognize the options to a dot: I like to change the prefix) Tj T* 0 Tw (character when I disable the default help.) Tj T* ET
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
Q
Q
q
-Q
-Q
+1 0 0 1 0 75 cm
q
-1 0 0 1 62.69291 705.0236 cm
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (The plac runner) Tj T* ET
Q
-q
-1 0 0 1 62.69291 705.0236 cm
Q
q
-1 0 0 1 62.69291 675.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 15 cm
+1 0 0 1 397.8898 75 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (3.) Tj T* -5.66 0 Td ET
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 0 57 cm
q
-BT 1 0 0 1 0 16.82 Tm .59186 Tw 12 TL /F1 10 Tf 0 0 0 rg (A plac-specific feature, i.e. keyword arguments recognition is put to good use to make it possible to) Tj T* 0 Tw (store a value in the shelve with the syntax ) Tj /F5 10 Tf (param_name=param_value) Tj /F1 10 Tf (.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A non class-based example) Tj T* ET
Q
Q
q
-Q
-Q
+1 0 0 1 397.8898 57 cm
q
-1 0 0 1 62.69291 675.0236 cm
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 0 Td ET
Q
-q
-1 0 0 1 62.69291 675.0236 cm
Q
q
-1 0 0 1 62.69291 645.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 15 cm
+1 0 0 1 0 39 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (4.) Tj T* -5.66 0 Td ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Writing your own plac runner) Tj T* ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 397.8898 39 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .107485 Tw (varargs are used to retrieve parameters from the shelve and some error checking is performed in the) Tj T* 0 Tw (case of missing parameters) Tj T* ET
+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
Q
Q
q
-Q
-Q
+1 0 0 1 0 21 cm
q
-1 0 0 1 62.69291 645.0236 cm
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Summary) Tj T* ET
Q
-q
-1 0 0 1 62.69291 645.0236 cm
Q
q
-1 0 0 1 62.69291 627.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
+1 0 0 1 397.8898 21 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (5.) Tj T* -5.66 0 Td ET
+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 (14) Tj T* -60.88 0 Td ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 0 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (A command to clear the shelve is implemented as a flag \() Tj /F5 10 Tf (.clear) Tj /F1 10 Tf (\).) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Appendix: custom annotation objects) Tj T* ET
Q
Q
q
-Q
-Q
+1 0 0 1 397.8898 3 cm
q
-1 0 0 1 62.69291 627.0236 cm
+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 (15) Tj T* -60.88 0 Td ET
Q
-q
-1 0 0 1 62.69291 627.0236 cm
Q
q
-1 0 0 1 62.69291 609.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (6.) Tj T* -5.66 0 Td ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 62.69291 281.0236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (A command to delete a given parameter is implemented as an option \() Tj /F5 10 Tf (.delete) Tj /F1 10 Tf (\).) Tj T* ET
+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
-Q
-Q
+1 0 0 1 62.69291 239.0236 cm
q
-1 0 0 1 62.69291 609.0236 cm
+BT 1 0 0 1 0 28.82 Tm .539036 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the design goals of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is to make it dead easy to write a scriptable and testable interface for an) Tj T* 0 Tw .813876 Tw (application. You can use ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (whenever you have an API with strings in input and strings in output, and) Tj T* 0 Tw (that includes a ) Tj /F5 10 Tf (huge ) Tj /F1 10 Tf (domain of applications.) Tj T* ET
Q
-q
-1 0 0 1 62.69291 609.0236 cm
Q
q
-1 0 0 1 62.69291 591.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
+1 0 0 1 62.69291 197.0236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (7.) Tj T* -5.66 0 Td ET
+BT 1 0 0 1 0 28.82 Tm 1.756651 Tw 12 TL /F1 10 Tf 0 0 0 rg (A string-oriented interface is a scriptable interface by construction. That means that you can define a) Tj T* 0 Tw .918735 Tw (command language for your application and that it is possible to write scripts which are interpretable by) Tj T* 0 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and can be run as batch scripts.) Tj T* ET
Q
Q
q
-1 0 0 1 23 3 cm
+1 0 0 1 62.69291 155.0236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (There is an option with default \() Tj /F5 10 Tf (.filename=conf.shelve) Tj /F1 10 Tf (\) to store the filename of the shelve.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .444987 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually, at the most general level, you can see ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (as a generic tool to write domain specific languages) Tj T* 0 Tw .107209 Tw (\(DSL\). With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (you can test your application interactively as well as with batch scripts, and even with the) Tj T* 0 Tw (analogous of Python doctests for your defined language.) Tj T* ET
Q
Q
q
-Q
-Q
+1 0 0 1 62.69291 113.0236 cm
q
-1 0 0 1 62.69291 591.0236 cm
+BT 1 0 0 1 0 28.82 Tm .694104 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can easily replace the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module of the standard library and you could easily write an application) Tj T* 0 Tw 2.731654 Tw (like ) Tj 0 0 .501961 rg (twill ) Tj 0 0 0 rg (with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Or you could use it to script your building procedure. Or any other thing, your) Tj T* 0 Tw (imagination is the only limit!) Tj T* ET
Q
-q
-1 0 0 1 62.69291 591.0236 cm
Q
q
-1 0 0 1 62.69291 549.0236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-q
-1 0 0 1 6 27 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 5.66 0 Td (8.) Tj T* -5.66 0 Td ET
+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
+% 'R116': class PDFStream
+116 0 obj
+% page stream
+<< /Length 4233 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 23 3 cm
+1 0 0 1 62.69291 744.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.001984 Tw 12 TL /F1 10 Tf 0 0 0 rg (All things considered, the code looks like a poor man object oriented interface implemented with a) Tj T* 0 Tw 1.052619 Tw (chain of elifs instead of methods. Of course, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can do better than that, but I am starting from a) Tj T* 0 Tw (low-level approach first.) Tj T* ET
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (From scripts to interactive applications) 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 3.215814 Tw 12 TL /F1 10 Tf 0 0 0 rg (Command-line scripts have many advantages, but are no substitute for interactive applications. In) Tj T* 0 Tw .088171 Tw (particular, if you have a script with a large startup time which must be run multiple times, it is best to turn it) Tj T* 0 Tw 4.582126 Tw (into an interactive application, so that the startup is performed only once. ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (provides an) Tj T* 0 Tw /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class just for this purpose.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 549.0236 cm
-Q
+1 0 0 1 62.69291 660.0236 cm
q
-1 0 0 1 62.69291 549.0236 cm
+BT 1 0 0 1 0 16.82 Tm 1.293984 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class wraps the main function of a script and provides an ) Tj /F4 10 Tf (.interact ) Tj /F1 10 Tf (method to) Tj T* 0 Tw (start an interactive interpreter reading commands from the console.) Tj T* ET
+Q
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 630.0236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F5 10 Tf (ishelve.py ) Tj /F1 10 Tf (without arguments you get the following message:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.49436 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can define an interactive interpreter on top of the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (script introduced in the) Tj T* 0 Tw 0 0 .501961 rg (basic documentation ) Tj 0 0 0 rg (as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 485.8236 cm
+1 0 0 1 62.69291 380.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2024,24 +2282,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 240 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F5 10 Tf 12 TL ($ python ishelve.py) Tj T* (no arguments passed, use .help to see the available commands) Tj T* ET
+BT 1 0 0 1 0 221.71 Tm /F4 10 Tf 12 TL (# shelve_interpreter.py) Tj T* (import plac, ishelve) Tj T* T* (@plac.annotations\() Tj T* ( interactive=\('start interactive interface', 'flag'\),) Tj T* ( subcommands='the commands of the underlying ishelve interpreter'\)) Tj T* (def main\(interactive, *subcommands\):) Tj T* ( """) Tj T* ( This script works both interactively and non-interactively.) Tj T* ( Use .help to see the internal commands.) Tj T* ( """) Tj T* ( if interactive:) Tj T* ( plac.Interpreter\(ishelve.main\).interact\(\)) Tj T* ( else:) Tj T* ( for out in plac.call\(ishelve.main, subcommands\):) Tj T* ( print\(out\)) 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 465.8236 cm
+1 0 0 1 62.69291 336.8236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F5 10 Tf (ishelve.py ) Tj /F1 10 Tf (with the option ) Tj /F5 10 Tf (.h ) Tj /F1 10 Tf (\(or any abbreviation of ) Tj /F5 10 Tf (.help) Tj /F1 10 Tf (\) you get:) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 2.200651 Tw 12 TL /F1 10 Tf 0 0 0 rg (A trick has been used here: the ishelve command-line interface has been hidden inside an external) Tj T* 0 Tw .917674 Tw (interface. They are distinct: for instance the external interface recognizes the ) Tj /F4 10 Tf (-h/--help ) Tj /F1 10 Tf (flag whereas) Tj T* 0 Tw (the internal interface only recognizes the ) Tj /F4 10 Tf (.help ) Tj /F1 10 Tf (command:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 396.6236 cm
+1 0 0 1 62.69291 303.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2051,24 +2309,18 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 468.6898 24 re B*
Q
q
-BT 1 0 0 1 0 41.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ python ishelve.py .h) Tj T* (Commands: .help, .showall, .clear, .delete) Tj T* (<) Tj (param) Tj (>) Tj ( ...) Tj T* (<) Tj (param=value) Tj (>) Tj ( ...) Tj T* ET
-Q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ($ python shelve_interpreter.py -h) Tj T* ET
Q
Q
Q
Q
-q
-1 0 0 1 62.69291 376.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can check by hand that the tool work:) Tj T* ET
-Q
Q
q
-1 0 0 1 62.69291 127.4236 cm
+1 0 0 1 62.69291 138.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -2078,66 +2330,69 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 240 re B*
+n -6 -6 468.6898 156 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 221.71 Tm /F5 10 Tf 12 TL ($ python ishelve.py .clear # start from an empty shelve) Tj T* (cleared the shelve) Tj T* ($ python ishelve.py a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* ($ python ishelve.py .showall) Tj T* (b=2) Tj T* (a=1) Tj T* ($ python ishelve.py .del b # abbreviation for .delete) Tj T* (deleted b) Tj T* ($ python ishelve.py a) Tj T* (1) Tj T* ($ python ishelve.py b) Tj T* (b: not found) Tj T* ($ python ishelve.py .cler # mispelled command) Tj T* (usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) Tj T* (ishelve.py: error: unrecognized arguments: .cler) Tj T* ET
+BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (usage: shelve_interpreter.py [-h] [-interactive]) Tj T* ( [subcommands [subcommands ...]]) Tj T* T* (This script works both interactively and non-interactively. Use .help to see) Tj T* (the internal commands.) Tj T* T* (positional arguments:) Tj T* ( subcommands the commands of the underlying ishelve interpreter) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -interactive start interactive interface) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 107.4236 cm
+1 0 0 1 62.69291 118.4236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Command-line scripts have many advantages, but are no substitute for a real interactive application.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Thanks to this ingenuous trick, the script can be run both interactively and non-interactively:) 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 (3) Tj T* -238.1649 0 Td ET
+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
-% 'R97': class PDFStream
-97 0 obj
+% 'R117': class PDFStream
+117 0 obj
% page stream
-<< /Length 4316 >>
+<< /Length 4141 >>
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 727.8236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Turning a script into an interactive application) Tj T* ET
-Q
-Q
q
-1 0 0 1 62.69291 702.0236 cm
+1 0 0 1 0 0 cm
q
-BT 1 0 0 1 0 28.82 Tm .663468 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you have a script with a large startup time which must be run multiple times, it is best to turn it into an) Tj T* 0 Tw 1.919213 Tw (interactive application, so that the startup is performed only once. ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (provides an ) Tj /F5 10 Tf (Interpreter) Tj T* 0 Tw /F1 10 Tf (class just for this purpose.) Tj T* ET
-Q
-Q
+1 0 0 1 6.6 6.6 cm
q
-1 0 0 1 62.69291 672.0236 cm
+.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 16.82 Tm 1.293984 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F5 10 Tf (Interpreter ) Tj /F1 10 Tf (class wraps the main function of a script and provides an ) Tj /F5 10 Tf (.interact ) Tj /F1 10 Tf (method to) Tj T* 0 Tw (start an interactive interpreter reading commands from the console.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python shelve_interpreter.py .clear # non-interactive use) Tj T* (cleared the shelve) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 654.0236 cm
+1 0 0 1 62.69291 695.8236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (You could define an interactive interpreter on top of ) Tj /F5 10 Tf (ishelve ) Tj /F1 10 Tf (as follows:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 2.649318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is an usage session, using ) Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (to enable readline features \() Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (is available in Unix-like) Tj T* 0 Tw (systems\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 416.8236 cm
+1 0 0 1 62.69291 434.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2147,52 +2402,48 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 228 re B*
+n -6 -6 468.6898 252 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 209.71 Tm /F5 10 Tf 12 TL (import plac, ishelve) Tj T* T* (@plac.annotations\() Tj T* ( interactive=\('start interactive interface', 'flag'\),) Tj T* ( subcommands='the commands of the underlying ishelve interpreter'\)) Tj T* (def main\(interactive, *subcommands\):) Tj T* ( """) Tj T* ( This script works both interactively and non-interactively.) Tj T* ( Use .help to see the internal commands.) Tj T* ( """) Tj T* ( if interactive:) Tj T* ( plac.Interpreter\(ishelve.main\).interact\(\)) Tj T* ( else:) Tj T* ( for out in plac.call\(ishelve.main, subcommands\):) Tj T* ( print\(out\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 233.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ rlwrap python shelve_interpreter.py -i # interactive use) Tj T* (usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) 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 ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .show) 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 372.8236 cm
+1 0 0 1 62.69291 378.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.829984 Tw 12 TL /F1 10 Tf 0 0 0 rg (A trick has been used here: the ishelve command-line interface has been hidden inside and external) Tj T* 0 Tw .917674 Tw (interface. They are distinct: for instance the external interface recognizes the ) Tj /F5 10 Tf (-h/--help ) Tj /F1 10 Tf (flag whereas) Tj T* 0 Tw (the internal interface only recognizes the ) Tj /F5 10 Tf (.help ) Tj /F1 10 Tf (command:) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm .256412 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (.interact ) Tj /F1 10 Tf (method reads commands from the console and send them to the underlying interpreter,) Tj T* 0 Tw .065984 Tw (until the user send a CTRL-D command \(CTRL-Z in Windows\). There is a default argument ) Tj /F4 10 Tf (prompt='i) Tj (>) Tj T* 0 Tw .81936 Tw (' ) Tj /F1 10 Tf (which can be used to change the prompt. The message displayed by default is the argparse-provided) Tj T* 0 Tw (usage message, but can be customized by setting an ) Tj /F4 10 Tf (.intro ) Tj /F1 10 Tf (attribute on the main function.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 207.6236 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 348.6236 cm
q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+BT 1 0 0 1 0 16.82 Tm 1.03811 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that ) Tj /F4 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is available only if you are using a recent version of Python \() Tj (>) Tj (= 2.5\),) Tj T* 0 Tw (because it is a context manager object which uses extended generators internally.) Tj T* ET
Q
+Q
+q
+1 0 0 1 62.69291 318.6236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F5 10 Tf 12 TL (usage: shelve_interpreter.py [-h] [-interactive]) Tj T* ( [subcommands [subcommands ...]]) Tj T* T* (This script works both interactively and non-interactively. Use .help to see) Tj T* (the internal commands.) Tj T* T* (positional arguments:) Tj T* ( subcommands the commands of the underlying ishelve interpreter) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -interactive start interactive interface) Tj T* ET
-Q
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 3.034269 Tw (You can conveniently test your application in interactive mode. However manual testing is a poor) Tj T* 0 Tw (substitute for automatic testing.) Tj T* ET
Q
Q
+q
+1 0 0 1 62.69291 285.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Testing a plac application) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 187.6236 cm
+1 0 0 1 62.69291 267.6236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Thanks to this ingenuous trick, the script can be run both interactively and non-interactively:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (In principle, one could write automatic tests for the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (application by using ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (directly:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 142.4236 cm
+1 0 0 1 62.69291 102.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -2202,41 +2453,47 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 156 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F5 10 Tf 12 TL ($ python shelve_interpreter.py .clear # non-interactive use) Tj T* (cleared the shelve) Tj T* ET
+BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (# test_ishelve.py) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( assert plac.call\(ishelve.main, ['.clear']\) == ['cleared the shelve']) Tj T* ( assert plac.call\(ishelve.main, ['a=1']\) == ['setting a=1']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['1']) Tj T* ( assert plac.call\(ishelve.main, ['.delete=a']\) == ['deleted a']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['a: not found']) Tj T* T* (if __name__ == '__main__':) Tj T* ( test\(\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 110.4236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.049318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is an usage session, using ) Tj /F5 10 Tf (rlwrap ) Tj /F1 10 Tf (to enable readline features \() Tj /F5 10 Tf (rlwrap ) Tj /F1 10 Tf (is available in Unix-like) Tj T* 0 Tw (systems\):) 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
+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
-% 'R98': class PDFStream
-98 0 obj
+% 'R118': class PDFStream
+118 0 obj
% page stream
-<< /Length 4748 >>
+<< /Length 6129 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 511.8236 cm
+1 0 0 1 62.69291 729.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .390651 Tw 12 TL /F1 10 Tf 0 0 0 rg (However, using ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (is not especially nice. The big issue is that ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (responds to invalid) Tj T* 0 Tw 1.249987 Tw (input by printing an error message on stderr and by raising a ) Tj /F4 10 Tf (SystemExit) Tj /F1 10 Tf (: this is certainly not a nice) Tj T* 0 Tw (thing to do in a test.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 687.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.616457 Tw 12 TL /F1 10 Tf 0 0 0 rg (As a consequence of this behavior it is impossible to test for invalid commands, unless you wrap the) Tj T* 0 Tw .259985 Tw /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (exception by hand each time \(a possibly you do something with the error message in stderr) Tj T* 0 Tw (too\). Luckily, ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (offers a better testing support through the ) Tj /F4 10 Tf (check ) Tj /F1 10 Tf (method of ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (objects:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 533.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2246,38 +2503,45 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 252 re B*
+n -6 -6 468.6898 144 re B*
Q
q
-BT 1 0 0 1 0 233.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ rlwrap python shelve_interpreter.py -i # interactive use) Tj T* (usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) 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 ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (# test_ishelve2.py) Tj T* (from __future__ import with_statement) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( with plac.Interpreter\(ishelve.main\) as i:) Tj T* ( i.check\('.clear', 'cleared the shelve'\)) Tj T* ( i.check\('a=1', 'setting a=1'\)) Tj T* ( i.check\('a', '1'\)) Tj T* ( i.check\('.delete=a', 'deleted a'\)) Tj T* ( i.check\('a', 'a: not found'\)) Tj T* ET
+Q
+Q
Q
Q
Q
+q
+1 0 0 1 62.69291 489.8236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 6.299974 Tw 12 TL /F1 10 Tf 0 0 0 rg (The method ) Tj /F4 10 Tf (.check\(given_input, expected_output\) ) Tj /F1 10 Tf (works on strings and raises an) Tj T* 0 Tw .971318 Tw /F4 10 Tf (AssertionError ) Tj /F1 10 Tf (if the output produced by the interpreter is different from the expected output for the) Tj T* 0 Tw (given input.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 455.8236 cm
+1 0 0 1 62.69291 459.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm .256412 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F5 10 Tf (.interact ) Tj /F1 10 Tf (method reads commands from the console and send them to the underlying interpreter,) Tj T* 0 Tw 3.197126 Tw (until the user send a CTRL-D command \(CTRL-Z in Windows\). There are two default arguments) Tj T* 0 Tw .365318 Tw /F5 10 Tf (prompt='i) Tj (> ') Tj /F1 10 Tf (, and ) Tj /F5 10 Tf (intro=None ) Tj /F1 10 Tf (which can be used to change the prompt and the message displayed) Tj T* 0 Tw (at the beginning, which by default is the argparse-provided usage message.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 2.179982 Tw 12 TL /F4 10 Tf 0 0 0 rg (AssertionError ) Tj /F1 10 Tf (is catched by tools like ) Tj /F4 10 Tf (py.test ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (nosetests ) Tj /F1 10 Tf (and actually ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (tests are) Tj T* 0 Tw (intended to be run with such tools.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 425.8236 cm
+1 0 0 1 62.69291 405.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.03811 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that ) Tj /F5 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is available only if you are using a recent version of Python \() Tj (>) Tj (= 2.5\),) Tj T* 0 Tw (because it is a context manager object which uses extended generators internally.) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm .239984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Interpreters offer a minor syntactic advantage with respect to calling ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (directly, but they offer a) Tj T* 0 Tw .96748 Tw /F5 10 Tf (major ) Tj /F1 10 Tf (semantic advantage when things go wrong \(read exceptions\): an ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (object internally) Tj T* 0 Tw 1.181318 Tw (invokes something like ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (, but it wraps all exceptions, so that ) Tj /F4 10 Tf (i.check ) Tj /F1 10 Tf (is guaranteed not to) Tj T* 0 Tw (raise any exception except ) Tj /F4 10 Tf (AssertionError) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 371.8236 cm
+1 0 0 1 62.69291 387.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm .909984 Tw 12 TL /F1 10 Tf 0 0 0 rg (The distribution of ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (includes a runner script named ) Tj /F5 10 Tf (plac_runner.py ) Tj /F1 10 Tf (which will be installed in a) Tj T* 0 Tw .44748 Tw (suitable directory in your system by distutils \(say in ) Tj /F5 10 Tf (\\usr\\local\\bin\\plac_runner.py ) Tj /F1 10 Tf (in a Unix-like) Tj T* 0 Tw .877765 Tw (operative system\). The easiest way to turn a script into an interactive application is to use the runner. If) Tj T* 0 Tw (you put this alias in your bashrc) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Even the ) Tj /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (exception is captured and you can write your test as) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 365.8236 cm
+1 0 0 1 62.69291 381.8236 cm
Q
q
-1 0 0 1 62.69291 353.8236 cm
+1 0 0 1 62.69291 369.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2285,85 +2549,85 @@ q
1 0 0 1 20 0 cm
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F5 10 Tf 12 TL (alias plac="rlwrap plac_runner.py") Tj T* ET
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (i.check\('-cler', 'SystemExit: unrecognized arguments: -cler'\)) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 353.8236 cm
+1 0 0 1 62.69291 369.8236 cm
Q
q
-1 0 0 1 62.69291 323.8236 cm
+1 0 0 1 62.69291 351.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm .957485 Tw 12 TL /F1 10 Tf 0 0 0 rg (\(or you define a suitable ) Tj /F5 10 Tf (plac.bat ) Tj /F1 10 Tf (script in Windows\) you can run the original ) Tj /F5 10 Tf (ishelve.py ) Tj /F1 10 Tf (script in) Tj T* 0 Tw (interactive mode as follows:) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (without risk of exiting from the Python interpreter.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 242.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
-Q
+1 0 0 1 62.69291 309.8236 cm
q
-BT 1 0 0 1 0 53.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ plac -i ishelve.py) Tj T* (usage: plac_runner.py ishelve.py [.help] [.showall] [.clear]) Tj T* ( [.delete DELETE] [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) Tj T* (i) Tj (>) Tj T* ET
-Q
-Q
-Q
+BT 1 0 0 1 0 28.82 Tm 1.422651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a second advantage of interpreters: if the main function contains some initialization code and) Tj T* 0 Tw .454651 Tw (finalization code \() Tj /F4 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (functions\) they will be run only once at the beginning and) Tj T* 0 Tw (at the end of the interpreter loop. ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (instead ignores the initialization/finalization code.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 210.6236 cm
+1 0 0 1 62.69291 276.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm .981412 Tw 12 TL /F1 10 Tf 0 0 0 rg (In other words, there is no need to write the interactive wrapper \() Tj /F5 10 Tf (shelve_interpreter.py) Tj /F1 10 Tf (\) by hand,) Tj T* 0 Tw (the plac runner does the job for you.) Tj T* ET
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac easy tests) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 180.6236 cm
+1 0 0 1 62.69291 234.8236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 3.034269 Tw (You can conveniently test your application in interactive mode. However manual testing is a poor) Tj T* 0 Tw (substitute for automatic testing.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 1.517126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Writing your tests in terms of ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (is certainly an improvement over writing them in) Tj T* 0 Tw 1.807318 Tw (terms of ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (, but they are still too low-level for my taste. The ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class provides) Tj T* 0 Tw (support for doctest-style tests, a.k.a. ) Tj /F5 10 Tf (plac easy tests) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 147.6236 cm
+1 0 0 1 62.69291 192.8236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Testing a plac application) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 2.142209 Tw 12 TL /F1 10 Tf 0 0 0 rg (By using plac easy tests you can cut and paste your interactive session and turn it into a runnable) Tj T* 0 Tw .519213 Tw (automatics test! Consider for instance the following file ) Tj /F4 10 Tf (ishelve.placet ) Tj /F1 10 Tf (\(the ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (extension is a) Tj T* 0 Tw (mnemonic for plac easy tests\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 129.6236 cm
+1 0 0 1 62.69291 99.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 84 re B*
+Q
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (In principle, one could write automatic tests for the ) Tj /F5 10 Tf (ishelve ) Tj /F1 10 Tf (application by using ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (directly:) Tj T* ET
+BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (#!ishelve.py) Tj T* (i) Tj (>) Tj ( .clear # start from a clean state) 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* 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
+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
-% 'R99': class PDFStream
-99 0 obj
+% 'R119': class PDFStream
+119 0 obj
% page stream
-<< /Length 5743 >>
+<< /Length 5794 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 583.8236 cm
+1 0 0 1 62.69291 667.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2373,151 +2637,148 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 180 re B*
+n -6 -6 468.6898 96 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 161.71 Tm /F5 10 Tf 12 TL (# test_ishelve.py) Tj T* (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* T* (if __name__ == '__main__':) Tj T* ( test\(\)) Tj T* ET
+BT 1 0 0 1 0 77.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .cler # spelling error) Tj T* (SystemExit: unrecognized arguments: .cler) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 539.8236 cm
+1 0 0 1 62.69291 611.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm .390651 Tw 12 TL /F1 10 Tf 0 0 0 rg (However, using ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (is not especially nice. The big issue is that ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (responds to invalid) Tj T* 0 Tw 1.249987 Tw (input by printing an error message on stderr and by raising a ) Tj /F5 10 Tf (SystemExit) Tj /F1 10 Tf (: this is certainly not a nice) Tj T* 0 Tw (thing to do in a test.) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm .697132 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the precence of the shebang line containing the name of the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (tool to test \(a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (tool is just a) Tj T* 0 Tw 1.511751 Tw (Python module with a function called ) Tj /F4 10 Tf (main) Tj /F1 10 Tf (\). The shebang is ignored by the interpreter \(it looks like a) Tj T* 0 Tw .487608 Tw (comment to it\) but it is there so that external tools \(say a test runner\) can infer the plac interpreter to use) Tj T* 0 Tw (to test the file.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 497.8236 cm
+1 0 0 1 62.69291 581.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.616457 Tw 12 TL /F1 10 Tf 0 0 0 rg (As a consequence of this behavior it is impossible to test for invalid commands, unless you wrap the) Tj T* 0 Tw .259985 Tw /F5 10 Tf (SystemExit ) Tj /F1 10 Tf (exception by hand each time \(a possibly you do something with the error message in stderr) Tj T* 0 Tw (too\). Luckily, ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (offers a better testing support through the ) Tj /F5 10 Tf (check ) Tj /F1 10 Tf (method of ) Tj /F5 10 Tf (Interpreter ) Tj /F1 10 Tf (objects:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 2.419984 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can test ) Tj /F4 10 Tf (ishelve.placet ) Tj /F1 10 Tf (file by calling the ) Tj /F4 10 Tf (.doctest ) Tj /F1 10 Tf (method of the interpreter, as in this) Tj T* 0 Tw (example:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 344.6236 cm
+1 0 0 1 62.69291 537.0393 cm
q
q
-1 0 0 1 0 0 cm
+.988825 0 0 .988825 0 0 cm
q
-1 0 0 1 6.6 6.6 cm
+1 0 0 1 6.6 6.674587 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 144 re B*
+n -6 -6 474 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 125.71 Tm /F5 10 Tf 12 TL (# test_ishelve2.py) Tj T* (from __future__ import with_statement) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( with plac.Interpreter\(ishelve.main\) as i:) Tj T* ( i.check\('.clear', 'cleared the shelve'\)) Tj T* ( i.check\('a=1', 'setting a=1'\)) Tj T* ( i.check\('a', '1'\)) Tj T* ( i.check\('.delete=a', 'deleted a'\)) Tj T* ( i.check\('a', 'a: not found'\)) Tj T* ET
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python -c"import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).doctest\(open\('ishelve.placet'\), verbose=True\)") Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 300.6236 cm
+1 0 0 1 62.69291 469.0393 cm
q
-BT 1 0 0 1 0 28.82 Tm 6.299974 Tw 12 TL /F1 10 Tf 0 0 0 rg (The method ) Tj /F5 10 Tf (.check\(given_input, expected_output\) ) Tj /F1 10 Tf (works on strings and raises an) Tj T* 0 Tw .971318 Tw /F5 10 Tf (AssertionError ) Tj /F1 10 Tf (if the output produced by the interpreter is different from the expected output for the) Tj T* 0 Tw (given input.) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm 2.74122 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F4 10 Tf (Interpreter.doctests ) Tj /F1 10 Tf (invokes ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (multiple times inside the same) Tj T* 0 Tw 1.091098 Tw (context and compare the output with the expected output: if even a check fails, the whole test fail. The) Tj T* 0 Tw .568221 Tw (easy tests supported by ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (are ) Tj /F5 10 Tf (not ) Tj /F1 10 Tf (unittests: they should be used to model user interaction when the) Tj T* 0 Tw .000574 Tw (order of the operations matters. Since the single subtests in a ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (file are not independent, it makes) Tj T* 0 Tw (sense to exit immediately at the first failure.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 246.6236 cm
+1 0 0 1 62.69291 403.0393 cm
q
-BT 1 0 0 1 0 40.82 Tm 2.179982 Tw 12 TL /F5 10 Tf 0 0 0 rg (AssertionError ) Tj /F1 10 Tf (is catched by tools like ) Tj /F5 10 Tf (py.test ) Tj /F1 10 Tf (and ) Tj /F5 10 Tf (nosetests ) Tj /F1 10 Tf (and actually ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (tests are) Tj T* 0 Tw .678935 Tw (intended to be run with such tools. If you want to use the ) Tj /F5 10 Tf (unittest ) Tj /F1 10 Tf (module in the standard library you) Tj T* 0 Tw 2.081098 Tw (can, but I am not going to support it directly \(reminder: ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is opinionated and I dislike the unittest) Tj T* 0 Tw (module\).) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .414431 Tw 12 TL /F1 10 Tf 0 0 0 rg (The support for doctests in ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (comes nearly for free, thanks to the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (module in the standard library,) Tj T* 0 Tw .352765 Tw (which is able to parse simple languages as the ones you can implement with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. In particular, thanks to) Tj T* 0 Tw .061318 Tw 0 0 .501961 rg (shlex) Tj 0 0 0 rg (, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize comments \(the default comment character is ) Tj /F4 10 Tf (#) Tj /F1 10 Tf (\), continuation lines, escape) Tj T* 0 Tw 1.54061 Tw (sequences and more. Look at the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (documentation if you need to customize how the language is) Tj T* 0 Tw (interpreted.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 192.6236 cm
+1 0 0 1 62.69291 349.0393 cm
q
-BT 1 0 0 1 0 40.82 Tm .239984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Interpreters offer a minor syntactic advantage with respect to calling ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (directly, but they offer a) Tj T* 0 Tw .96748 Tw /F4 10 Tf (major ) Tj /F1 10 Tf (semantic advantage when things go wrong \(read exceptions\): an ) Tj /F5 10 Tf (Interpreter ) Tj /F1 10 Tf (object internally) Tj T* 0 Tw 1.181318 Tw (invokes something like ) Tj /F5 10 Tf (plac.call) Tj /F1 10 Tf (, but it wraps all exceptions, so that ) Tj /F5 10 Tf (i.check ) Tj /F1 10 Tf (is guaranteed not to) Tj T* 0 Tw (raise any exception except ) Tj /F5 10 Tf (AssertionError) Tj /F1 10 Tf (.) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm .136654 Tw 12 TL /F1 10 Tf 0 0 0 rg (In addition, I have implemented from scratch some support for line number recognition, so that if a test fail) Tj T* 0 Tw .042093 Tw (you get the line number of the failing command. This is especially useful if your tests are stored in external) Tj T* 0 Tw .86408 Tw (files \(plac easy tests does not need to be in a file: you can just pass to the ) Tj /F4 10 Tf (.doctest ) Tj /F1 10 Tf (method a list of) Tj T* 0 Tw (strings corresponding to the lines of the file\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 174.6236 cm
+1 0 0 1 62.69291 319.0393 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Even the ) Tj /F5 10 Tf (SystemExit ) Tj /F1 10 Tf (exception is captured and you can write your test as) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .12683 Tw 12 TL /F1 10 Tf 0 0 0 rg (At the present plac easy tests do not use any code from the doctest module, but the situation may change) Tj T* 0 Tw (in the future \(it would be nice if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (could reuse doctests directives like ELLIPSIS\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 168.6236 cm
+1 0 0 1 62.69291 289.0393 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.447318 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is straighforward to integrate your ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (tests with standard testing tools. For instance, you can) Tj T* 0 Tw (integrate your doctests with nose or py.test as follow:) Tj T* ET
+Q
Q
q
-1 0 0 1 62.69291 156.6236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-BT 1 0 0 1 0 2 Tm T* ET
+1 0 0 1 62.69291 101.8642 cm
q
-1 0 0 1 20 0 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F5 10 Tf 12 TL (i.check\('-cler', 'SystemExit: unrecognized arguments: -cler'\)) Tj T* ET
-Q
-Q
+.988825 0 0 .988825 0 0 cm
q
-Q
-Q
+1 0 0 1 6.6 6.674587 cm
q
-1 0 0 1 62.69291 156.6236 cm
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 474 180 re B*
Q
q
-1 0 0 1 62.69291 138.6236 cm
-q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (without risk of exiting from the Python interpreter.) Tj T* ET
+BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL (import os, shlex, plac) Tj T* T* (def test_doct\(\):) Tj T* ( """) Tj T* ( Find all the doctests in the current directory and run them with the) Tj T* ( corresponding plac interpreter \(the shebang rules!\)) Tj T* ( """) Tj T* ( placets = [f for f in os.listdir\('.'\) if f.endswith\('.placet'\)]) Tj T* ( for placet in placets:) Tj T* ( lines = list\(open\(placet\)\)) Tj T* ( assert lines[0].startswith\('#!'\), 'Missing or incorrect shebang line!') Tj T* ( firstline = lines[0][2:] # strip the shebang) Tj T* ( main = plac.import_main\(*shlex.split\(firstline\)\)) Tj T* ( yield plac.Interpreter\(main\).doctest, lines[1:]) Tj T* ET
+Q
Q
Q
-q
-1 0 0 1 62.69291 96.62362 cm
-q
-BT 1 0 0 1 0 28.82 Tm 1.422651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a second advantage of interpreters: if the main function contains some initialization code and ) Tj T* 0 Tw .454651 Tw (finalization code \() Tj /F5 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (functions\) they will be run only once at the beginning and ) Tj T* 0 Tw .385984 Tw (at the end of the interpreter loop. ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (also executes the initialization/finalization code, but it runs) Tj T* 0 Tw 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
+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
-% 'R100': class PDFStream
-100 0 obj
+% 'R120': class PDFStream
+120 0 obj
% page stream
-<< /Length 4275 >>
+<< /Length 4648 >>
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 693.0236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (it at each call, and that may be too expensive.) Tj T* ET
+BT 1 0 0 1 0 64.82 Tm 1.44811 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here you should notice that usage of ) Tj /F4 10 Tf (plac.import_main) Tj /F1 10 Tf (, an utility which is able to import the main) Tj T* 0 Tw 1.040465 Tw (function of the script specified in the shabng line. You can use both the full path name of the tool, or a) Tj T* 0 Tw .87686 Tw (relative path name. In this case the runner look at the environment variable ) Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (and it searches) Tj T* 0 Tw 1.900651 Tw (the plac tool in the directories specified there \() Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (is just a string containing directory names) Tj T* 0 Tw .56332 Tw (separated by colons\). If the variable ) Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (is not defined, it just looks in the current directory. If the) Tj T* 0 Tw (plac tool is not found, an ) Tj /F4 10 Tf (ImportError ) Tj /F1 10 Tf (is raised.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 720.0236 cm
+1 0 0 1 62.69291 660.0236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac easytests) Tj T* ET
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac batch scripts) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 678.0236 cm
+1 0 0 1 62.69291 618.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.517126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Writing your tests in terms of ) Tj /F5 10 Tf (Interpreter.check ) Tj /F1 10 Tf (is certainly an improvement over writing them in) Tj T* 0 Tw 1.807318 Tw (terms of ) Tj /F5 10 Tf (plac.call) Tj /F1 10 Tf (, but they are still too low-level for my taste. The ) Tj /F5 10 Tf (Interpreter ) Tj /F1 10 Tf (class provides) Tj T* 0 Tw (support for doctest-style tests, a.k.a. ) Tj /F4 10 Tf (plac easytests) Tj /F1 10 Tf (.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .772093 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is pretty easy to realize that an interactive interpreter can also be used to run batch scripts: instead of) Tj T* 0 Tw .504692 Tw (reading the commands from the console, it is enough to read the commands from a file. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreters) Tj T* 0 Tw (provide an ) Tj /F4 10 Tf (.execute ) Tj /F1 10 Tf (method to perform just that.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 636.0236 cm
+1 0 0 1 62.69291 552.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm 2.142209 Tw 12 TL /F1 10 Tf 0 0 0 rg (By using plac easy tests you can cut and paste your interactive session and turn it into a runnable) Tj T* 0 Tw .519213 Tw (automatics test! Consider for instance the following file ) Tj /F5 10 Tf (ishelve.placet ) Tj /F1 10 Tf (\(the ) Tj /F5 10 Tf (.placet ) Tj /F1 10 Tf (extension is a) Tj T* 0 Tw (mnemonic for plac easytests\):) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .098935 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is just a subtle point to notice: whereas in an interactive loop one wants to manage all exceptions, a) Tj T* 0 Tw 3.866412 Tw (batch script should not in the background in case of unexpected errors. The implementation of) Tj T* 0 Tw .103059 Tw /F4 10 Tf (Interpreter.execute ) Tj /F1 10 Tf (makes sure that any error raised by ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (internally is re-raised. In other) Tj T* 0 Tw .407045 Tw (words, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreters ) Tj /F5 10 Tf (wrap the errors, but does not eat them) Tj /F1 10 Tf (: the errors are always accessible and can) Tj T* 0 Tw (be re-raised on demand.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 458.8236 cm
+1 0 0 1 62.69291 534.0236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (In particular consider the following batch file, which contains a syntax error \() Tj /F4 10 Tf (.dl ) Tj /F1 10 Tf (instead of ) Tj /F4 10 Tf (.del) Tj /F1 10 Tf (\):) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 428.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2527,78 +2788,98 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 168 re B*
+n -6 -6 468.6898 96 re B*
Q
q
-BT 1 0 0 1 0 149.71 Tm 12 TL /F5 10 Tf 0 0 0 rg (#!ishelve.py) Tj T* (i) Tj (>) Tj ( .clear # start from a clean state) 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 ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .cler # spelling error) Tj T* (SystemExit: unrecognized arguments: .cler) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL (#!ishelve.py) Tj T* (.clear ) Tj T* (a=1 b=2) Tj T* (.show) Tj T* (.del a) Tj T* (.dl b) Tj T* (.show) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 414.8236 cm
+1 0 0 1 62.69291 396.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm .697132 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the precence of the shebang line containing the name of the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (tool to test \(a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (tool is just a) Tj T* 0 Tw .785868 Tw (Python module with a function called ) Tj /F5 10 Tf (main) Tj /F1 10 Tf (\). You can doctest it by calling the ) Tj /F5 10 Tf (.doctest ) Tj /F1 10 Tf (method of the) Tj T* 0 Tw (interpreter) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .26686 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you execute the batch file, the interpreter will raise a ) Tj /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (with an appropriated error message) Tj T* 0 Tw (at the ) Tj /F4 10 Tf (.dl ) Tj /F1 10 Tf (line and the last command will ) Tj /F5 10 Tf (not ) Tj /F1 10 Tf (be executed:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 370.0393 cm
+1 0 0 1 62.69291 207.6236 cm
q
q
-.988825 0 0 .988825 0 0 cm
+1 0 0 1 0 0 cm
q
-1 0 0 1 6.6 6.674587 cm
+1 0 0 1 6.6 6.6 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 474 36 re B*
+n -6 -6 468.6898 180 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F5 10 Tf 12 TL ($ python -c"import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).doctest\(open\('ishelve.placet'\), verbose=True\)") Tj T* ET
+BT 1 0 0 1 0 161.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python -c "import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).execute\(open\('ishelve.plac'\), verbose=True\)") Tj T* (i) Tj (>) Tj ( .clear) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (a=1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( .dl b) Tj T* (unrecognized arguments: .dl) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 350.0393 cm
+1 0 0 1 62.69291 163.6236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (and you will get the following output:) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .159988 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (verbose ) Tj /F1 10 Tf (flag is there to show the lines which are being interpreted \(prefixed by ) Tj /F4 10 Tf (i) Tj (>) Tj /F1 10 Tf (\). This is done on) Tj T* 0 Tw 1.359988 Tw (purpose, so that you can cut and paste the output of the batch script and turn it into a ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (test) Tj T* 0 Tw (\(cool, isn't it?\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 184.8393 cm
-q
+1 0 0 1 56.69291 56.69291 cm
q
-1 0 0 1 0 0 cm
+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
+% 'R121': class PDFStream
+121 0 obj
+% page stream
+<< /Length 3980 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 6.6 6.6 cm
+1 0 0 1 62.69291 744.0236 cm
q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Containers of commands) Tj T* ET
+Q
Q
q
-BT 1 0 0 1 0 137.71 Tm 12 TL /F5 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .clear # start from a clean state) Tj T* (-) Tj (>) Tj ( cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (-) Tj (>) Tj ( setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* (-) Tj (>) Tj ( 1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (-) Tj (>) Tj ( deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (-) Tj (>) Tj ( a: not found) Tj T* (i) Tj (>) Tj ( .cler # spelling error) Tj T* (-) Tj (>) Tj ( SystemExit: unrecognized arguments: .cler) Tj T* ET
+1 0 0 1 62.69291 702.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.182485 Tw 12 TL /F1 10 Tf 0 0 0 rg (When I discussed the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (implementation in the ) Tj 0 0 .501961 rg (basic documentation) Tj 0 0 0 rg (, I said that it looked like a) Tj T* 0 Tw 1.483988 Tw (poor man implementation of an object system as a chain of elifs; I also said that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was able to do) Tj T* 0 Tw (better. Here I will substantiate my claim.) Tj T* ET
Q
Q
+q
+1 0 0 1 62.69291 660.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .89104 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is actually able to infer a set of subparsers from a generic container of commands. This is useful if) Tj T* 0 Tw 3.125814 Tw (you want to implement ) Tj /F5 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line application featuring) Tj T* 0 Tw (subcommands is subversion\).) Tj T* ET
Q
Q
+q
+1 0 0 1 62.69291 618.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .015868 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a container of commands is any object with a ) Tj /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or) Tj T* 0 Tw 2.550888 Tw (methods which are valid commands. A command container may have initialization/finalization hooks) Tj T* 0 Tw (\() Tj /F4 10 Tf (__enter__/__exit__) Tj /F1 10 Tf (\) and dispatch hooks \() Tj /F4 10 Tf (__missing__) Tj /F1 10 Tf (, invoked for invalid command names\).) Tj T* ET
+Q
Q
q
-1 0 0 1 62.69291 164.8393 cm
+1 0 0 1 62.69291 600.0236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can also run placets following the shebang convention directly with the plac runner:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Using this feature the shelve interface can be rewritten in a more object-oriented way as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 119.6393 cm
+1 0 0 1 62.69291 98.82362 cm
q
q
1 0 0 1 0 0 cm
@@ -2608,11 +2889,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 492 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F5 10 Tf 12 TL ($ plac --easytest ishelve.placet) Tj T* (run 1 plac easy test\(s\)) Tj T* ET
+BT 1 0 0 1 0 473.71 Tm /F4 10 Tf 12 TL (# ishelve2.py) Tj T* (import shelve, os, sys, plac) Tj T* T* (class ShelveInterface\(object\):) Tj T* ( "A minimal interface over a shelve object") Tj T* ( commands = 'set', 'show', 'showall', 'delete') Tj T* ( @plac.annotations\() Tj T* ( configfile=\('path name of the shelve', 'option'\)\)) Tj T* ( def __init__\(self, configfile='~/conf.shelve'\):) Tj T* ( self.fname = os.path.expanduser\(configfile\)) Tj T* ( self.intro = 'Operating on %s. Available commands:\\n%s' % \() Tj T* ( self.fname, '\\n'.join\(c for c in self.commands\)\)) Tj T* ( def __enter__\(self\):) Tj T* ( self.sh = shelve.open\(self.fname\)) Tj T* ( return self) 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' % \(name, self.sh[name]\) # no error checking) Tj T* ( def showall\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s' % \(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] # no error checking) Tj T* ( def __exit__\(self, etype, exc, tb\):) Tj T* ( self.sh.close\(\)) Tj T* T* (main = ShelveInterface # the main 'function' can also be a class!) Tj T* T* ET
Q
Q
Q
@@ -2629,26 +2910,14 @@ Q
endstream
endobj
-% 'R101': class PDFStream
-101 0 obj
+% 'R122': class PDFStream
+122 0 obj
% page stream
-<< /Length 6279 >>
+<< /Length 4384 >>
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 .567356 Tw 12 TL /F1 10 Tf 0 0 0 rg (The runner ignore the extension, so you can actually use any extension your like, but ) Tj /F4 10 Tf (it relies on the first) Tj T* 0 Tw .750941 Tw (line of the file to correspond to an existing plac tool) Tj /F1 10 Tf (, so you cannot skip it and you cannot write a wrong) Tj T* 0 Tw (shebang.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 699.0236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .537209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The plac runner does not provide any test discovery facility, but you can use standard Unix tools to help.) Tj T* 0 Tw (For instance, you can run all the ) Tj /F5 10 Tf (.placet ) Tj /F1 10 Tf (files into a directory and its subdirectories as follows:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 665.8236 cm
+1 0 0 1 62.69291 715.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2658,78 +2927,85 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F5 10 Tf 12 TL ($ find . -name \\*.placet | xargs plac_runner.py -e) Tj T* ET
-Q
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( i = plac.Interpreter\(main\(\)\)) Tj T* ( i.interact\(\)) Tj T* ET
Q
Q
Q
Q
-q
-1 0 0 1 62.69291 597.8236 cm
-q
-BT 1 0 0 1 0 52.82 Tm 2.74122 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F5 10 Tf (Interpreter.doctests ) Tj /F1 10 Tf (invokes ) Tj /F5 10 Tf (Interpreter.check ) Tj /F1 10 Tf (multiple times inside the same) Tj T* 0 Tw 1.091098 Tw (context and compare the output with the expected output: if even a check fails, the whole test fail. The) Tj T* 0 Tw .568221 Tw (easy tests supported by ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (are ) Tj /F4 10 Tf (not ) Tj /F1 10 Tf (unittests: they should be used to model user interaction when the) Tj T* 0 Tw .000574 Tw (order of the operations matters. Since the single subtests in a ) Tj /F5 10 Tf (.placet ) Tj /F1 10 Tf (file are not independent, it makes) Tj T* 0 Tw (sense to exit immediately at the first failure.) Tj T* ET
-Q
Q
q
-1 0 0 1 62.69291 531.8236 cm
-q
-BT 1 0 0 1 0 52.82 Tm .414431 Tw 12 TL /F1 10 Tf 0 0 0 rg (The support for doctests in ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (comes nearly for free, thanks to the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (module in the standard library,) Tj T* 0 Tw .352765 Tw (which is able to parse simple languages as the ones you can implement with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. In particular, thanks to) Tj T* 0 Tw .061318 Tw 0 0 .501961 rg (shlex) Tj 0 0 0 rg (, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize comments \(the default comment character is ) Tj /F5 10 Tf (#) Tj /F1 10 Tf (\), continuation lines, escape) Tj T* 0 Tw 1.54061 Tw (sequences and more. Look at the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (documentation if you need to customize how the language is) Tj T* 0 Tw (interpreted.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 477.8236 cm
+1 0 0 1 62.69291 623.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm .136654 Tw 12 TL /F1 10 Tf 0 0 0 rg (In addition, I have implemented from scratch some support for line number recognition, so that if a test fail) Tj T* 0 Tw .042093 Tw (you get the line number of the failing command. This is especially useful if your tests are stored in external) Tj T* 0 Tw .86408 Tw (files \(plac easy tests does not need to be in a file: you can just pass to the ) Tj /F5 10 Tf (.doctest ) Tj /F1 10 Tf (method a list of) Tj T* 0 Tw (strings corresponding to the lines of the file\).) Tj T* ET
+BT 1 0 0 1 0 76.82 Tm .885366 Tw 12 TL /F4 10 Tf 0 0 0 rg (plac.Interpreter ) Tj /F1 10 Tf (objects wrap context manager objects consistently. In other words, if you wrap an) Tj T* 0 Tw 2.323828 Tw (object with ) Tj /F4 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (methods, they are invoked in the right order \() Tj /F4 10 Tf (__enter__) Tj T* 0 Tw .23528 Tw /F1 10 Tf (before the interpreter loop starts and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (after the interpreter loop ends, both in the regular and in) Tj T* 0 Tw 1.916412 Tw (the exceptional case\). In our example, the methods ) Tj /F4 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (make sure the the) Tj T* 0 Tw .339398 Tw (shelve is opened and closed correctly even in the case of exceptions. Notice that I have not implemented) Tj T* 0 Tw .814104 Tw (any error checking in the ) Tj /F4 10 Tf (show ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (delete ) Tj /F1 10 Tf (methods on purpose, to verify that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (works correctly in) Tj T* 0 Tw (the presence of exceptions.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 447.8236 cm
+1 0 0 1 62.69291 605.8236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 2.497984 Tw (It is straighforward to integrate your .placet tests with standard testing tools. For instance, you can) Tj T* 0 Tw (integrate your doctests with nose or py.test as follow:) Tj T* ET
+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 262.8826 cm
+1 0 0 1 62.69291 284.6236 cm
q
q
-.976496 0 0 .976496 0 0 cm
+1 0 0 1 0 0 cm
q
-1 0 0 1 6.6 6.758862 cm
+1 0 0 1 6.6 6.6 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 480 180 re B*
+n -6 -6 468.6898 312 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 161.71 Tm /F5 10 Tf 12 TL (import os, plac) Tj T* T* (def test_doct\(\):) Tj T* ( """) Tj T* ( Find all the doctests in the current directory and run them with the) Tj T* ( corresponding plac tool.) Tj T* ( """) Tj T* ( transcripts = [f for f in os.listdir\('.'\) if f.endswith\('.placet'\)]) Tj T* ( for transcript in transcripts:) Tj T* ( lines = list\(open\(transcript\)\)) Tj T* ( assert lines[0].startswith\('#!'\), 'Missing or incorrect shebang line!') Tj T* ( tool_path = lines[0][2:].strip\(\) # get the path to the tool to test) Tj T* ( main = plac.import_main\(tool_path\)) Tj T* ( yield plac.Interpreter\(main\).doctest, lines[1:]) Tj T* ET
+BT 1 0 0 1 0 293.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ rlwrap python ishelve2.py) Tj T* (Operating on /home/micheles/conf.shelve. Available commands:) Tj T* (set) Tj T* (show) Tj T* (showall) Tj T* (delete) Tj T* (i) Tj (>) Tj ( delete) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a pippo) Tj T* (setting a=pippo) Tj T* (i) Tj (>) Tj ( set b lippo) Tj T* (setting b=lippo) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (a = pippo) Tj T* (i) Tj (>) Tj ( show a b) Tj T* (a = pippo) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( del a) Tj T* (deleting a) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( delete a) Tj T* (DBNotFoundError: \(-30988, 'DB_NOTFOUND: No matching key/data pair found'\)) Tj T* (i) Tj (>) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 182.8826 cm
+1 0 0 1 62.69291 252.6236 cm
q
-BT 1 0 0 1 0 64.82 Tm .774651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here you should notice that usage of ) Tj /F5 10 Tf (plac.import_main\(path\)) Tj /F1 10 Tf (, an utility which is able to import the) Tj T* 0 Tw 1.065488 Tw (main function of the specified script. You can use both the full path name of the tool, or a relative path) Tj T* 0 Tw .22332 Tw (name. In this case the runner look at the environment variable ) Tj /F5 10 Tf (PLACPATH ) Tj /F1 10 Tf (and it searches the plac tool in) Tj T* 0 Tw 2.706136 Tw (the directories specified there \() Tj /F5 10 Tf (PLACPATH ) Tj /F1 10 Tf (is just a string containing directory names separated by) Tj T* 0 Tw .201488 Tw (colons\). If the variable ) Tj /F5 10 Tf (PLACPATH ) Tj /F1 10 Tf (is not defined, it just looks in the current directory. If the plac tool is not) Tj T* 0 Tw (found, an ) Tj /F5 10 Tf (ImportError ) Tj /F1 10 Tf (is raised.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 2.571235 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that in interactive mode the traceback is hidden, unless you pass the ) Tj /F4 10 Tf (verbose ) Tj /F1 10 Tf (flag to the) Tj T* 0 Tw /F4 10 Tf (Interpreter.interact ) Tj /F1 10 Tf (method.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 149.8826 cm
+1 0 0 1 62.69291 198.6236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac batch scripts) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm .532209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The interactive mode of ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (can be used as a replacement of the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module in the standard library.) Tj T* 0 Tw .66881 Tw (There are a few differences, however. For instance you miss tab completion, even if use ) Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (\(you get) Tj T* 0 Tw .092651 Tw (persistent command history for free, however\). This is not a big issue, since ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (understands command) Tj T* 0 Tw (abbreviations.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 180.6236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If an abbreviation is ambiguous, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (warns you:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 107.8826 cm
+1 0 0 1 62.69291 111.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 60 re B*
+Q
q
-BT 1 0 0 1 0 28.82 Tm .772093 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is pretty easy to realize that an interactive interpreter can also be used to run batch scripts: instead of) Tj T* 0 Tw .504692 Tw (reading the commands from the console, it is enough to read the commands from a file. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreters) Tj T* 0 Tw (provide an ) Tj /F5 10 Tf (.execute ) Tj /F1 10 Tf (method to perform just that:) Tj T* ET
+BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ rlwrap python ishelve2.py) Tj T* (usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...) Tj T* (i) Tj (>) Tj ( sh) Tj T* (NameError: Ambiguous command 'sh': matching ['showall', 'show']) Tj T* ET
+Q
+Q
+Q
Q
Q
q
@@ -2743,14 +3019,26 @@ Q
endstream
endobj
-% 'R102': class PDFStream
-102 0 obj
+% 'R123': class PDFStream
+123 0 obj
% page stream
-<< /Length 5218 >>
+<< /Length 5097 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 739.8236 cm
+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 (For ) Tj /F6 17.5 Tf (cmd ) Tj /F2 17.5 Tf (lovers) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 642.0236 cm
+q
+BT 1 0 0 1 0 88.82 Tm .391567 Tw 12 TL /F1 10 Tf 0 0 0 rg (I have been using the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module of the standard library for years. I have also written a much enhanced) Tj T* 0 Tw .212619 Tw /F4 10 Tf (cmd2 ) Tj /F1 10 Tf (module which we are using internally at work and from which I have taken some ideas used in ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* 0 Tw .13832 Tw (In many ways ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (makes the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module obsolete, but I realize why many nostalgic souls would still use) Tj T* 0 Tw 1.053984 Tw 0 0 .501961 rg (cmd) Tj 0 0 0 rg (, especially until ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not grow real auto-completion features, instead of relying on ) Tj 0 0 .501961 rg (rlwrap) Tj 0 0 0 rg (. But) Tj T* 0 Tw 1.12561 Tw (there must not be competition between ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and ) Tj 0 0 .501961 rg (cmd) Tj 0 0 0 rg (: actually the two can happily work togethere. For) Tj T* 0 Tw .338988 Tw (this reason I have put in the ) Tj /F4 10 Tf (plac_ext ) Tj /F1 10 Tf (module a few lines of code for gluing together ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (and ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (, the) Tj T* 0 Tw 1.229269 Tw /F4 10 Tf (cmd_interface) Tj /F1 10 Tf (. Using the ) Tj /F4 10 Tf (cmd_interface ) Tj /F1 10 Tf (is quite trivial: give to it a plac command container and) Tj T* 0 Tw (you will get in exchange a ) Tj /F4 10 Tf (cmd.Cmd ) Tj /F1 10 Tf (object:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 548.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2760,30 +3048,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
+n -6 -6 468.6898 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F5 10 Tf 12 TL (plac.Interpreter\(main\).execute\(line_iterator\)) Tj T* ET
-Q
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# cmd_ext.py) Tj T* (from plac_ext import cmd_interface) Tj T* (import ishelve2) Tj T* T* (if __name__ == '__main__':) Tj T* ( cmd_interface\(ishelve2.main\(\)\).cmdloop\(\)) Tj T* ET
Q
Q
Q
Q
-q
-1 0 0 1 62.69291 671.8236 cm
-q
-BT 1 0 0 1 0 52.82 Tm .567765 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is just a subtle point to notice: whereas in an interactive loop one wants to manage all exceptions,) Tj T* 0 Tw 1.371988 Tw (in a batch script we want to make sure that the script does not continue in the background in case of) Tj T* 0 Tw .395814 Tw (unexpected errors. The implementation of ) Tj /F5 10 Tf (Interpreter.execute ) Tj /F1 10 Tf (makes sure that any error raised by) Tj T* 0 Tw 1.826651 Tw /F5 10 Tf (plac.call ) Tj /F1 10 Tf (internally is re-raised. In other words, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreters ) Tj /F4 10 Tf (wrap the errors, but does not eat) Tj T* 0 Tw (them) Tj /F1 10 Tf (: the errors are always accessible and can be re-raised on demand.) Tj T* ET
-Q
Q
q
-1 0 0 1 62.69291 653.8236 cm
+1 0 0 1 62.69291 528.8236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (In particular consider the following batch file, which contains a syntax error \() Tj /F5 10 Tf (.dl ) Tj /F1 10 Tf (instead of ) Tj /F5 10 Tf (.del) 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 (Here is an example of interactive session:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 548.6236 cm
+1 0 0 1 62.69291 267.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2793,24 +3076,64 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 96 re B*
+n -6 -6 468.6898 252 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F5 10 Tf 12 TL (#!ishelve.py) Tj T* (.clear ) Tj T* (a=1 b=2) Tj T* (.show) Tj T* (.del a) Tj T* (.dl b) Tj T* (.show) Tj T* ET
+BT 1 0 0 1 0 233.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python cmd_ex.py) Tj T* (\(Cmd\) help) Tj T* T* (Documented commands \(type help ) Tj (<) Tj (topic) Tj (>) Tj (\):) Tj T* (========================================) Tj T* (delete set show showall) Tj T* T* (Undocumented commands:) Tj T* (======================) Tj T* (EOF help) Tj T* T* (\(Cmd\) set a 1) Tj T* (setting a=1) Tj T* (\(Cmd\) show a) Tj T* (a = 1) Tj T* (\(Cmd\) showall) Tj T* (a = 1) Tj T* (\(Cmd\) delete b) Tj T* (KeyError: 'b') Tj T* (\(Cmd\) EOF [or CTRL-D]) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 504.6236 cm
+1 0 0 1 62.69291 235.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm .26686 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you execute the batch file, the interpreter will raise a ) Tj /F5 10 Tf (SystemExit ) Tj /F1 10 Tf (with an appropriated error message) Tj T* 0 Tw .163798 Tw (at the ) Tj /F5 10 Tf (.dl ) Tj /F1 10 Tf (line and the last command will ) Tj /F4 10 Tf (not ) Tj /F1 10 Tf (be executed. The easiest way to execute the batch file is to) Tj T* 0 Tw (invoke the ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (runner:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .504983 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally the ) Tj /F4 10 Tf (cmd_interface ) Tj /F1 10 Tf (builds a ) Tj /F4 10 Tf (cmd.Cmd ) Tj /F1 10 Tf (class and adds to it the ) Tj /F4 10 Tf (do_ ) Tj /F1 10 Tf (methods corresponding) Tj T* 0 Tw (to the commands in the container, then it returns a ) Tj /F4 10 Tf (cmd.Cmd ) Tj /F1 10 Tf (instance.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 327.4236 cm
+1 0 0 1 62.69291 193.6236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .089488 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (cmd_interface ) Tj /F1 10 Tf (is just a proof of concept: it is there so that you can study the source code and see) Tj T* 0 Tw .93832 Tw (an example of integration of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (with a different framework. It may change and even go away in future) Tj T* 0 Tw (releases of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 160.6236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The plac runner) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 94.62362 cm
+q
+BT 1 0 0 1 0 52.82 Tm 1.531318 Tw 12 TL /F1 10 Tf 0 0 0 rg (The distribution of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (includes a runner script named ) Tj /F4 10 Tf (plac_runner.py) Tj /F1 10 Tf (, which will be installed in a) Tj T* 0 Tw .44748 Tw (suitable directory in your system by ) Tj 0 0 .501961 rg (distutils ) Tj 0 0 0 rg (\(say in ) Tj /F4 10 Tf (\\usr\\local\\bin\\plac_runner.py ) Tj /F1 10 Tf (in a Unix-like) Tj T* 0 Tw .680651 Tw (operative system\). The runner provides many facilities to run ) Tj /F4 10 Tf (.plac ) Tj /F1 10 Tf (scripts and ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (files, as well) Tj T* 0 Tw 1.47311 Tw (as Python modules containg a ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (object, which can be a function, a command container object or) Tj T* 0 Tw (even a command container class.) 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
+% 'R124': class PDFStream
+124 0 obj
+% page stream
+<< /Length 4203 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 741.0236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.994269 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, suppose you want to execute a script containing commands defined in the ) Tj /F4 10 Tf (ishelve2) Tj T* 0 Tw /F1 10 Tf (module like the following one:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 671.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2820,71 +3143,86 @@ 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
-BT 1 0 0 1 0 149.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ plac --batch --verbose ishelve.batch) Tj T* (i) Tj (>) Tj ( .clear) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (a=1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( .dl b) Tj T* (unrecognized arguments: .dl) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (#!ishelve2.py -c ~/conf.shelve) Tj T* (set a 1) Tj T* (del a) Tj T* (del a # intentional error) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 283.4236 cm
+1 0 0 1 62.69291 627.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm .274431 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F5 10 Tf (--verbose ) Tj /F1 10 Tf (flag is there to show the lines which are being interpreted \(prefixed by ) Tj /F5 10 Tf (i) Tj (>) Tj /F1 10 Tf (\). This is done) Tj T* 0 Tw .633322 Tw (on purpose, so that you can cut and paste the output of the batch script and turn it into a ) Tj /F5 10 Tf (.placet ) Tj /F1 10 Tf (test) Tj T* 0 Tw (\(cool, isn't it?\).) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .575868 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first line of the ) Tj /F4 10 Tf (.plac ) Tj /F1 10 Tf (script contains the name of the python module containing the plac interpreter) Tj T* 0 Tw 2.327209 Tw (and the arguments which must be passed to its main function in order to be able to instantiate an) Tj T* 0 Tw (interpreter object. The other lines contains commands. Then you can run the script as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 250.4236 cm
+1 0 0 1 62.69291 538.6505 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Command containers) Tj T* ET
-Q
-Q
q
-1 0 0 1 62.69291 208.4236 cm
+.952737 0 0 .952737 0 0 cm
+q
+1 0 0 1 6.6 6.927412 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 492 84 re B*
+Q
q
-BT 1 0 0 1 0 28.82 Tm .001751 Tw 12 TL /F1 10 Tf 0 0 0 rg (When I discussed the ) Tj /F5 10 Tf (ishelve ) Tj /F1 10 Tf (implementation, I said that it looked like a poor man implementation of an) Tj T* 0 Tw .937608 Tw (object system as a chain of elifs; I also said that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was able to do better. Here I will substantiate my) Tj T* 0 Tw (claim.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ($ plac_runner.py --batch ishelve2.plac) Tj T* (setting a=1) Tj T* (deleting a) Tj T* (Traceback \(most recent call last\):) Tj T* ( ...) Tj T* (_bsddb.DBNotFoundError: \(-30988, 'DB_NOTFOUND: No matching key/data pair found'\)) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 166.4236 cm
+1 0 0 1 62.69291 506.6505 cm
q
-BT 1 0 0 1 0 28.82 Tm .89104 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is actually able to infer a set of subparsers from a generic container of commands. This is useful if) Tj T* 0 Tw 3.125814 Tw (you want to implement ) Tj /F4 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line application featuring) Tj T* 0 Tw (subcommands is subversion\).) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 2.79186 Tw (The last command intentionally contained an error, to show that the plac runner does not eat the) Tj T* 0 Tw (traceback.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 124.4236 cm
+1 0 0 1 62.69291 476.6505 cm
q
-BT 1 0 0 1 0 28.82 Tm .015868 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a container of commands is any object with a ) Tj /F5 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or) Tj T* 0 Tw 2.550888 Tw (methods which are valid commands. A command container may have initialization/finalization hooks) Tj T* 0 Tw (\() Tj /F5 10 Tf (__enter__/__exit__) Tj /F1 10 Tf (\) and dispatch hooks \() Tj /F5 10 Tf (__missing__) Tj /F1 10 Tf (, invoked for invalid command names\).) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .437633 Tw (The runner can also be used to run Python modules in interactive mode and non-interactive mode. If you) Tj T* 0 Tw (put this alias in your bashrc) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 106.4236 cm
+1 0 0 1 62.69291 470.6505 cm
+Q
q
+1 0 0 1 62.69291 458.6505 cm
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Using this feature the shelve interface can be rewritten in a more object-oriented way as follows:) Tj T* ET
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (alias plac="rlwrap plac_runner.py") Tj T* ET
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+Q
+Q
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
+1 0 0 1 62.69291 458.6505 cm
+Q
+q
+1 0 0 1 62.69291 428.6505 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.955318 Tw 12 TL /F1 10 Tf 0 0 0 rg (\(or you define a suitable ) Tj /F4 10 Tf (plac.bat ) Tj /F1 10 Tf (script in Windows\) you can run the ) Tj /F4 10 Tf (ishelve2.py ) Tj /F1 10 Tf (script in) Tj T* 0 Tw (interactive mode as follows:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R103': class PDFStream
-103 0 obj
-% page stream
-<< /Length 3735 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 259.8236 cm
+1 0 0 1 62.69291 239.4505 cm
q
q
1 0 0 1 0 0 cm
@@ -2894,27 +3232,39 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 504 re B*
+n -6 -6 468.6898 180 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 485.71 Tm /F5 10 Tf 12 TL (# ishelve2.py) Tj T* (import shelve, os, sys) 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', 'showall', 'delete') Tj T* ( def __init__\(self, fname\):) Tj T* ( self.fname = fname) Tj T* ( def __enter__\(self\):) Tj T* ( self.sh = shelve.open\(self.fname\)) Tj T* ( return self) 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' % \(name, self.sh[name]\)) Tj T* ( def showall\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s' % \(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* ( def __exit__\(self, etype, exc, tb\):) Tj T* ( self.sh.close\(\)) Tj T* T* (main = ShelveInterface\(os.path.expanduser\('~/conf.shelve'\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
+BT 1 0 0 1 0 161.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ plac -i ishelve2.py) Tj T* (Operating on /home/micheles/conf.shelve. Available commands:) Tj T* (set) Tj T* (show) Tj T* (showall) Tj T* (delete) Tj T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show b) Tj T* (b = 2) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 143.8236 cm
+1 0 0 1 62.69291 219.4505 cm
q
-BT 1 0 0 1 0 100.82 Tm .645318 Tw 12 TL /F1 10 Tf 0 0 0 rg (You should notice that ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (understands the context manager protocol: if you call an object with) Tj T* 0 Tw 1.275697 Tw /F5 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (methods, they are invoked in the right order \() Tj /F5 10 Tf (__enter__ ) Tj /F1 10 Tf (before the call) Tj T* 0 Tw .842485 Tw (and ) Tj /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (after the call, both in the regular and in the exceptional case\). Since ) Tj /F5 10 Tf (plac.call ) Tj /F1 10 Tf (does) Tj T* 0 Tw 1.542485 Tw (not use the ) Tj /F5 10 Tf (with ) Tj /F1 10 Tf (statement internally, such feature works even in old versions of Python, before the) Tj T* 0 Tw 1.049269 Tw (introduction of the context manager protocol \(in Python 2.5\). In our example, the methods ) Tj /F5 10 Tf (__enter__) Tj T* 0 Tw .399398 Tw /F1 10 Tf (and ) Tj /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (make sure the the shelve is opened and closed correctly even in the case of exceptions.) Tj T* 0 Tw .223516 Tw (Notice that I have not implemented any error checking in the ) Tj /F5 10 Tf (show ) Tj /F1 10 Tf (and ) Tj /F5 10 Tf (delete ) Tj /F1 10 Tf (methods on purpose, to) Tj T* 0 Tw .754987 Tw (verify that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (works correctly in the presence of exceptions \(in particular I want to show that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does) Tj T* 0 Tw (not "eat" the traceback\).) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Now you can cut and paste the interactive session an turns into into a ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (file like the following:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 125.8236 cm
+1 0 0 1 62.69291 90.25045 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
+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
+BT 1 0 0 1 0 101.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (#!ishelve2.py -configfile=~/test.shelve) Tj T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show b) Tj T* (b = 2) Tj T* ET
+Q
+Q
+Q
Q
Q
q
@@ -2928,42 +3278,60 @@ Q
endstream
endobj
-% 'R104': class PDFStream
-104 0 obj
+% 'R125': class PDFStream
+125 0 obj
% page stream
-<< /Length 4597 >>
+<< /Length 5347 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 535.2235 cm
+1 0 0 1 62.69291 741.0236 cm
q
+BT 1 0 0 1 0 16.82 Tm 2.145697 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the first line specifies a test database ) Tj /F4 10 Tf (~/test.shelve) Tj /F1 10 Tf (, to avoid clobbering your default) Tj T* 0 Tw (shelve. If you mispell the arguments in the first line plac will give you an ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (error message \(just try\).) Tj T* ET
+Q
+Q
q
-.952737 0 0 .952737 0 0 cm
+1 0 0 1 62.69291 723.0236 cm
q
-1 0 0 1 6.6 6.927412 cm
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can run placets following the shebang convention directly with the plac runner:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 677.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 492 240 re B*
+n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 221.71 Tm /F5 10 Tf 12 TL ($ alias conf="python ishelve2.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 showall) 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 del a # an abbreviation) Tj T* (deleting a) Tj T* ($ conf showall) Tj T* (b = lippo) Tj T* ($ conf delete a # notice the full traceback) Tj T* (Traceback \(most recent call last\):) Tj T* ( ...) Tj T* (_bsddb.DBNotFoundError: \(-30988, 'DB_NOTFOUND: No matching key/data pair found'\)) Tj T* ET
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ plac --test ishelve2.placet) Tj T* (run 1 plac test\(s\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 515.2235 cm
+1 0 0 1 62.69291 633.8236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Notice that in script mode you get the full traceback, whereas in interactive mode the traceback is hidden:) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .32104 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you want to see the output of the tests, pass the ) Tj /F4 10 Tf (-v/--verbose ) Tj /F1 10 Tf (flag. Notice that he runner ignore the) Tj T* 0 Tw .24856 Tw (extension, so you can actually use any extension your like, but ) Tj /F5 10 Tf (it relies on the first line of the file to invoke) Tj T* 0 Tw (the corresponding plac tool with the given arguments) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 603.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .537209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The plac runner does not provide any test discovery facility, but you can use standard Unix tools to help.) Tj T* 0 Tw (For instance, you can run all the ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (files into a directory and its subdirectories as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 434.0235 cm
+1 0 0 1 62.69291 570.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2973,36 +3341,59 @@ 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
-BT 1 0 0 1 0 53.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ plac -i ishelve2.py) Tj T* (usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...) Tj T* (i) Tj (>) Tj ( del a) Tj T* (DBNotFoundError: \(-30988, 'DB_NOTFOUND: No matching key/data pair found'\)) Tj T* (i) Tj (>) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ($ find . -name \\*.placet | xargs plac_runner.py -t) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 414.0235 cm
+1 0 0 1 62.69291 538.6236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can see the traceback if you start the runner in verbose mode \() Tj /F5 10 Tf (plac -vi ishelve2.py) Tj /F1 10 Tf (\).) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .760988 Tw 12 TL /F1 10 Tf 0 0 0 rg (The plac runner expects the main function of your script to return a plac tool, i.e. a function or an object) Tj T* 0 Tw (with a ) Tj /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute. It this is not the case the runner gracefully exits.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 520.6236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (It also works in non-interactive mode, if you call it as) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 360.0235 cm
+1 0 0 1 62.69291 514.6236 cm
+Q
+q
+1 0 0 1 62.69291 502.6236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
q
-BT 1 0 0 1 0 40.82 Tm .532209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The interactive mode of ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (can be used as a replacement of the ) Tj /F5 10 Tf (cmd ) Tj /F1 10 Tf (module in the standard library.) Tj T* 0 Tw 1.00311 Tw (There are a few differences, however. For instance you miss tab completion, even if use ) Tj /F5 10 Tf (rlwrap ) Tj /F1 10 Tf (\(you) Tj T* 0 Tw 2.092651 Tw (get persistent command history for free, however\). This is not a big issue, since ) Tj /F5 10 Tf (plac ) Tj /F1 10 Tf (understands) Tj T* 0 Tw /F4 10 Tf (command abbreviations ) Tj /F1 10 Tf (\(in all modes, not only in interactive mode\).) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ($ plac module.py args ...) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 342.0235 cm
+Q
+Q
+q
+1 0 0 1 62.69291 502.6236 cm
+Q
+q
+1 0 0 1 62.69291 484.6236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (If an abbreviation is ambiguous, plac warns you:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is an example:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 272.8235 cm
+1 0 0 1 62.69291 415.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3015,40 +3406,40 @@ q
n -6 -6 468.6898 60 re B*
Q
q
-BT 1 0 0 1 0 41.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ plac ishelve2.py -i) Tj T* (usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...) Tj T* (i) Tj (>) Tj ( sh) Tj T* (NameError: Ambiguous command 'sh': matching ['showall', 'show']) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ($ plac ishelve.py a=1) Tj T* (setting a=1) Tj T* ($ plac ishelve.py .show) Tj T* (a=1) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 252.8235 cm
+1 0 0 1 62.69291 383.4236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Giving the same abbreviation in script mode gives the same error but also shows the full traceback.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .18936 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that it non-interactive mode the runner just invokes ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (object of the Python) Tj T* 0 Tw (module.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 219.8235 cm
+1 0 0 1 62.69291 350.4236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A non class-based example) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 177.8235 cm
+1 0 0 1 62.69291 308.4236 cm
q
-BT 1 0 0 1 0 28.82 Tm .907209 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not force you to use classes to define command containers. Even a simple function can be a) Tj T* 0 Tw 1.796651 Tw (valid command container, it is enough to add to it a ) Tj /F5 10 Tf (.commands ) Tj /F1 10 Tf (attribute and possibly ) Tj /F5 10 Tf (__enter__) Tj T* 0 Tw /F1 10 Tf (and/or ) Tj /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (attributes.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .907209 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not force you to use classes to define command containers. Even a simple function can be a) Tj T* 0 Tw 1.796651 Tw (valid command container, it is enough to add to it a ) Tj /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute and possibly ) Tj /F4 10 Tf (__enter__) Tj T* 0 Tw /F1 10 Tf (and/or ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (attributes.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 147.8235 cm
+1 0 0 1 62.69291 278.4236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.757318 Tw (A Python module is a perfect container of commands. As an example, consider the following module) Tj T* 0 Tw (implementing a fake Version Control System:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .327485 Tw (In particular, a Python module is a perfect container of commands. As an example, consider the following) Tj T* 0 Tw (module implementing a fake Version Control System:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 90.62352 cm
+1 0 0 1 62.69291 89.22362 cm
q
q
1 0 0 1 0 0 cm
@@ -3058,11 +3449,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 180 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F5 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* ET
+BT 1 0 0 1 0 161.71 Tm /F4 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\(url='url of the source code'\)) Tj T* (def checkout\(url\):) Tj T* ( "A fake checkout command") Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\(message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( "A fake commit command") Tj T* ET
Q
Q
Q
@@ -3079,14 +3470,14 @@ Q
endstream
endobj
-% 'R105': class PDFStream
-105 0 obj
+% 'R126': class PDFStream
+126 0 obj
% page stream
-<< /Length 3415 >>
+<< /Length 3894 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 427.8236 cm
+1 0 0 1 62.69291 559.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3096,30 +3487,30 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 336 re B*
+n -6 -6 468.6898 204 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 317.71 Tm /F5 10 Tf 12 TL T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\(url='url of the source code'\)) Tj T* (def checkout\(url\):) Tj T* ( "A fake checkout command") Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\(message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( "A fake commit command") Tj T* ( return \('commit ', message\)) Tj T* T* (@plac.annotations\(quiet=\('summary information', 'flag', 'q'\)\)) Tj T* (def status\(quiet\):) Tj T* ( "A fake status command") Tj T* ( return \('status ', quiet\)) Tj T* T* (def __missing__\(name\):) Tj T* ( return 'Command %r does not exist' % name) Tj T* T* (def __exit__\(etype, exc, tb\):) Tj T* ( "Will be called automatically at the end of the call/cmdloop") Tj T* ( if etype in \(None, GeneratorExit\): # success) Tj T* ( print\('ok'\)) Tj T* T* (main = __import__\(__name__\) # the module imports itself!) Tj T* ET
+BT 1 0 0 1 0 185.71 Tm /F4 10 Tf 12 TL ( return \('commit ', message\)) Tj T* T* (@plac.annotations\(quiet=\('summary information', 'flag', 'q'\)\)) Tj T* (def status\(quiet\):) Tj T* ( "A fake status command") Tj T* ( return \('status ', quiet\)) Tj T* T* (def __missing__\(name\):) Tj T* ( return 'Command %r does not exist' % name) Tj T* T* (def __exit__\(etype, exc, tb\):) Tj T* ( "Will be called automatically at the end of the call/cmdloop") Tj T* ( if etype in \(None, GeneratorExit\): # success) Tj T* ( print\('ok'\)) Tj T* T* (main = __import__\(__name__\) # the module imports itself!) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 383.8236 cm
+1 0 0 1 62.69291 515.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm .060651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that I have defined both and ) Tj /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (hook and a ) Tj /F5 10 Tf (__missing__ ) Tj /F1 10 Tf (hook, invoked for non-existing) Tj T* 0 Tw .592651 Tw (commands. The real trick here is the line ) Tj /F5 10 Tf (main = __import__\(__name__\)) Tj /F1 10 Tf (, which define ) Tj /F5 10 Tf (main ) Tj /F1 10 Tf (to be) Tj T* 0 Tw (an alias for the current module.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .431318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that I have defined both an ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (hook and a ) Tj /F4 10 Tf (__missing__ ) Tj /F1 10 Tf (hook, invoked for non-existing) Tj T* 0 Tw .592651 Tw (commands. The real trick here is the line ) Tj /F4 10 Tf (main = __import__\(__name__\)) Tj /F1 10 Tf (, which define ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (to be) Tj T* 0 Tw (an alias for the current module.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 353.8236 cm
+1 0 0 1 62.69291 485.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.259986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F5 10 Tf (vcs ) Tj /F1 10 Tf (module does not contain an ) Tj /F5 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block, but you can still run it) Tj T* 0 Tw (through the plac runner \(try ) Tj /F5 10 Tf (plac vcs.py -h) Tj /F1 10 Tf (\):) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.259986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (vcs ) Tj /F1 10 Tf (module does not contain an ) Tj /F4 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block, but you can still run it) Tj T* 0 Tw (through the plac runner \(try ) Tj /F4 10 Tf (plac vcs.py -h) Tj /F1 10 Tf (\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 212.6236 cm
+1 0 0 1 62.69291 344.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3133,20 +3524,20 @@ n -6 -6 468.6898 132 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F5 10 Tf 12 TL (usage: plac_runner.py 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
+BT 1 0 0 1 0 113.71 Tm /F4 10 Tf 12 TL (usage: plac_runner.py 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 192.6236 cm
+1 0 0 1 62.69291 324.6236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can get help for the subcommands by postponing ) Tj /F5 10 Tf (-h ) Tj /F1 10 Tf (after the name of the command:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can get help for the subcommands by postponing ) Tj /F4 10 Tf (-h ) Tj /F1 10 Tf (after the name of the command:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 99.42362 cm
+1 0 0 1 62.69291 207.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3156,35 +3547,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 108 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F5 10 Tf 12 TL ($ plac vcs.py status -h) Tj T* (usage: vcs.py status [-h] [-q]) Tj T* T* (A fake status command) Tj T* T* (optional arguments:) Tj T* ET
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py status -h) Tj T* (usage: vcs.py status [-h] [-q]) Tj T* T* (A fake status command) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -q, --quiet summary information) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 175.4236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.064985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice how the docstring of the command is automatically shown in usage message, as well as the) Tj T* 0 Tw (documentation for the sub flag ) Tj /F4 10 Tf (-q) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 157.4236 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 4.82 Tm /F1 10 Tf 12 TL (Here is an example of a non-interactive session:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R106': class PDFStream
-106 0 obj
-% page stream
-<< /Length 3695 >>
-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 100.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3194,31 +3581,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 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F5 10 Tf 12 TL ( -h, --help show this help message and exit) Tj T* ( -q, --quiet summary information) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py check url) Tj T* (checkout) Tj T* (url) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 2.064985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice how the docstring of the command is automatically shown in usage message, as well as the) Tj T* 0 Tw (documentation for the sub flag ) Tj /F5 10 Tf (-q) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 677.8236 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 (Here is an example of a non-interactive session:) Tj T* ET
+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
+% 'R127': class PDFStream
+127 0 obj
+% page stream
+<< /Length 5650 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 512.6236 cm
+1 0 0 1 62.69291 679.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3228,25 +3619,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+n -6 -6 468.6898 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F5 10 Tf 12 TL ($ plac vcs.py check url) Tj T* (ok) Tj T* (checkout) Tj T* (url) Tj T* ($ plac vcs.py st -q) Tj T* (ok) Tj T* (status) Tj T* (True) Tj T* ($ plac vcs.py co) Tj T* (ok) Tj T* (commit) Tj T* (None) Tj T* ET
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py st -q) Tj T* (status) Tj T* (True) Tj T* ($ plac vcs.py co) Tj T* (commit) Tj T* (None) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 492.6236 cm
+1 0 0 1 62.69291 659.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (and here is an interactive session:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 291.4236 cm
+1 0 0 1 62.69291 458.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3259,68 +3650,45 @@ q
n -6 -6 468.6898 192 re B*
Q
q
-BT 1 0 0 1 0 173.71 Tm 12 TL /F5 10 Tf 0 0 0 rg ($ plac -i vcs.py) Tj T* (usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...) Tj T* (i) Tj (>) Tj ( check url) Tj T* (checkout) Tj T* (url) Tj T* (i) Tj (>) Tj ( st -q) Tj T* (status) Tj T* (True) Tj T* (i) Tj (>) Tj ( co) Tj T* (commit) Tj T* (None) Tj T* (i) Tj (>) Tj ( sto) Tj T* (Command 'sto' does not exist) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* (ok) Tj T* ET
+BT 1 0 0 1 0 173.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ plac -i vcs.py) Tj T* (usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...) Tj T* (i) Tj (>) Tj ( check url) Tj T* (checkout) Tj T* (url) Tj T* (i) Tj (>) Tj ( st -q) Tj T* (status) Tj T* (True) Tj T* (i) Tj (>) Tj ( co) Tj T* (commit) Tj T* (None) Tj T* (i) Tj (>) Tj ( sto) Tj T* (Command 'sto' does not exist) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* (ok) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 235.4236 cm
+1 0 0 1 62.69291 426.6236 cm
q
-BT 1 0 0 1 0 40.82 Tm 2.986905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the invocation of the ) Tj /F5 10 Tf (__missing__ ) Tj /F1 10 Tf (hook for non-existing commands. Notice also that the) Tj T* 0 Tw .488735 Tw /F5 10 Tf (__exit__ ) Tj /F1 10 Tf (hook gets called differently in interactive mode and non-interactive mode: in the first case it is) Tj T* 0 Tw .66811 Tw (called at the end of the interactive loop with a ) Tj /F5 10 Tf (GeneratorExit ) Tj /F1 10 Tf (exception, whereas in the second case) Tj T* 0 Tw (there is no exception.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 2.986905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the invocation of the ) Tj /F4 10 Tf (__missing__ ) Tj /F1 10 Tf (hook for non-existing commands. Notice also that the) Tj T* 0 Tw /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (hook gets called only in interactive mode.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 205.4236 cm
+1 0 0 1 62.69291 396.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.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 (situations, it is best to use a custom class.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 151.4236 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 /F5 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 /F5 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 /F5 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R107': class PDFStream
-107 0 obj
-% page stream
-<< /Length 5984 >>
-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 363.6236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Writing your own plac runner) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 690.0236 cm
+1 0 0 1 62.69291 309.6236 cm
q
-BT 1 0 0 1 0 40.82 Tm .167209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The runner included in the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (distribution is intentionally kept small \(around 40 lines of code\) so that you) Tj T* 0 Tw .081294 Tw (can study it and write your own runner if want to. If you need to go to such level of detail, you should know) Tj T* 0 Tw .42061 Tw (that the most important method of the ) Tj /F5 10 Tf (Interpreter ) Tj /F1 10 Tf (class is the ) Tj /F5 10 Tf (.send ) Tj /F1 10 Tf (method, which takes strings in) Tj T* 0 Tw (input and returns a four-tuple with attributes ) Tj /F5 10 Tf (.str) Tj /F1 10 Tf (, ) Tj /F5 10 Tf (.etype) Tj /F1 10 Tf (, ) Tj /F5 10 Tf (.exc ) Tj /F1 10 Tf (and ) Tj /F5 10 Tf (.tb) Tj /F1 10 Tf (:) Tj T* ET
+BT 1 0 0 1 0 40.82 Tm .167209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The runner included in the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (distribution is intentionally kept small \(around 50 lines of code\) so that you) Tj T* 0 Tw .081294 Tw (can study it and write your own runner if want to. If you need to go to such level of detail, you should know) Tj T* 0 Tw .42061 Tw (that the most important method of the ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class is the ) Tj /F4 10 Tf (.send ) Tj /F1 10 Tf (method, which takes strings in) Tj T* 0 Tw (input and returns a four-tuple with attributes ) Tj /F4 10 Tf (.str) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (.etype) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (.exc ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (.tb) Tj /F1 10 Tf (:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 684.0236 cm
+1 0 0 1 62.69291 303.6236 cm
Q
q
-1 0 0 1 62.69291 684.0236 cm
+1 0 0 1 62.69291 303.6236 cm
Q
q
-1 0 0 1 62.69291 666.0236 cm
+1 0 0 1 62.69291 285.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3333,20 +3701,20 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F5 10 Tf 0 0 0 rg (.str ) Tj /F1 10 Tf (is the output of the command, if successful \(a string\);) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (.str ) Tj /F1 10 Tf (is the output of the command, if successful \(a string\);) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 666.0236 cm
+1 0 0 1 62.69291 285.6236 cm
Q
q
-1 0 0 1 62.69291 666.0236 cm
+1 0 0 1 62.69291 285.6236 cm
Q
q
-1 0 0 1 62.69291 648.0236 cm
+1 0 0 1 62.69291 267.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3359,20 +3727,20 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F5 10 Tf 0 0 0 rg (.etype ) Tj /F1 10 Tf (is the class of the exception, if the command fail;) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (.etype ) Tj /F1 10 Tf (is the class of the exception, if the command fail;) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 648.0236 cm
+1 0 0 1 62.69291 267.6236 cm
Q
q
-1 0 0 1 62.69291 648.0236 cm
+1 0 0 1 62.69291 267.6236 cm
Q
q
-1 0 0 1 62.69291 630.0236 cm
+1 0 0 1 62.69291 249.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3385,20 +3753,20 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F5 10 Tf 0 0 0 rg (.exc ) Tj /F1 10 Tf (is the exception instance;) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (.exc ) Tj /F1 10 Tf (is the exception instance;) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 630.0236 cm
+1 0 0 1 62.69291 249.6236 cm
Q
q
-1 0 0 1 62.69291 630.0236 cm
+1 0 0 1 62.69291 249.6236 cm
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 231.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3411,32 +3779,32 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F5 10 Tf 0 0 0 rg (.tb ) Tj /F1 10 Tf (is the traceback.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (.tb ) Tj /F1 10 Tf (is the traceback.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 231.6236 cm
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 231.6236 cm
Q
q
-1 0 0 1 62.69291 570.0236 cm
+1 0 0 1 62.69291 189.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm .937485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover the ) Tj /F5 10 Tf (__str__ ) Tj /F1 10 Tf (representation of the output object is redefined to return the output string if the) Tj T* 0 Tw 2.686651 Tw (command was successful or the error message if the command failed \(actually it returns the error) Tj T* 0 Tw (message preceded by the name of the exception class\).) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .937485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover the ) Tj /F4 10 Tf (__str__ ) Tj /F1 10 Tf (representation of the output object is redefined to return the output string if the) Tj T* 0 Tw 2.686651 Tw (command was successful or the error message if the command failed \(actually it returns the error) Tj T* 0 Tw (message preceded by the name of the exception class\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 552.0236 cm
+1 0 0 1 62.69291 171.6236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (For instance, if you send a mispelled option to the interpreter a ) Tj /F5 10 Tf (SystemExit ) Tj /F1 10 Tf (will be trapped:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (For instance, if you send a mispelled option to the interpreter a ) Tj /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (will be trapped:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 458.8236 cm
+1 0 0 1 62.69291 90.42362 cm
q
q
1 0 0 1 0 0 cm
@@ -3446,30 +3814,67 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 72 re B*
Q
q
-BT 1 0 0 1 0 65.71 Tm 12 TL /F5 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( from ishelve import ishelve) Tj T* (>) Tj (>) Tj (>) Tj ( with plac.Interpreter\(ishelve\) as i:) Tj T* (... print\(i.send\('.cler'\)\)) Tj T* (...) Tj T* (SystemExit: unrecognized arguments: .cler) Tj T* ET
+BT 1 0 0 1 0 53.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( from ishelve import ishelve) Tj T* (>) Tj (>) Tj (>) Tj ( with plac.Interpreter\(ishelve\) as i:) Tj T* (... print\(i.send\('.cler'\)\)) Tj T* (...) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 426.8236 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 16.82 Tm 2.90561 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is important to invoke the ) Tj /F5 10 Tf (.send ) Tj /F1 10 Tf (method inside the context manager, otherwise you will get a) Tj T* 0 Tw /F5 10 Tf (RuntimeError) 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 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R128': class PDFStream
+128 0 obj
+% page stream
+<< /Length 6119 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 739.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 24 re B*
+Q
+q
+BT 1 0 0 1 0 5.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (SystemExit: unrecognized arguments: .cler) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 384.8236 cm
+1 0 0 1 62.69291 707.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.90561 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is important to invoke the ) Tj /F4 10 Tf (.send ) Tj /F1 10 Tf (method inside the context manager, otherwise you will get a) Tj T* 0 Tw /F4 10 Tf (RuntimeError) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 665.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 28.82 Tm /F1 10 Tf 12 TL .29311 Tw (For instance, suppose you want to implement a graphical runner for a plac-based interpreter with two text) Tj T* 0 Tw 1.548221 Tw (widgets: one to enter the commands and one to display the results. Suppose you want to display the) Tj T* 0 Tw (errors with tracebacks in red. You will need to code something like that \(pseudocode follows\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 147.6236 cm
+1 0 0 1 62.69291 428.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3483,44 +3888,27 @@ n -6 -6 468.6898 228 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 209.71 Tm /F5 10 Tf 12 TL (input_widget = WidgetReadingInput\(\)) Tj T* (output_widget = WidgetDisplayingOutput\(\)) Tj T* T* (def send\(interpreter, line\):) Tj T* ( out = interpreter.send\(line\)) Tj T* ( if out.tb: # there was an error) Tj T* ( output_widget.display\(out.tb, color='red'\)) Tj T* ( else:) Tj T* ( output_widget.display\(out.str\)) Tj T* T* (main = plac.import_main\(tool_path\) # get the main object) Tj T* T* (with plac.Interpreter\(main\) as i:) Tj T* ( def callback\(event\):) Tj T* ( if event.user_pressed_ENTER\(\):) Tj T* ( send\(i, input_widget.last_line\)) Tj T* ( input_widget.addcallback\(callback\)) Tj T* ( gui_mainloop.start\(\)) Tj T* ET
+BT 1 0 0 1 0 209.71 Tm /F4 10 Tf 12 TL (input_widget = WidgetReadingInput\(\)) Tj T* (output_widget = WidgetDisplayingOutput\(\)) Tj T* T* (def send\(interpreter, line\):) Tj T* ( out = interpreter.send\(line\)) Tj T* ( if out.tb: # there was an error) Tj T* ( output_widget.display\(out.tb, color='red'\)) Tj T* ( else:) Tj T* ( output_widget.display\(out.str\)) Tj T* T* (main = plac.import_main\(tool_path\) # get the main object) Tj T* T* (with plac.Interpreter\(main\) as i:) Tj T* ( def callback\(event\):) Tj T* ( if event.user_pressed_ENTER\(\):) Tj T* ( send\(i, input_widget.last_line\)) Tj T* ( input_widget.addcallback\(callback\)) Tj T* ( gui_mainloop.start\(\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 115.6236 cm
+1 0 0 1 62.69291 396.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .102765 Tw (You can adapt the pseudocode to your GUI toolkit of choice and you can also change the file associations) Tj T* 0 Tw (in such a way that clicking on a plac tool file the graphical user interface starts.) Tj T* ET
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 342.6236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 40.82 Tm 2.090651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a final ) Tj /F5 10 Tf (caveat) Tj /F1 10 Tf (: since the plac interpreter loop is implemented via extended generators, plac) Tj T* 0 Tw .988651 Tw (interpreters are single threaded: you will get an error if you ) Tj /F4 10 Tf (.send ) Tj /F1 10 Tf (commands from separated threads.) Tj T* 0 Tw .947882 Tw (You can circumvent the problem by using a queue. If EXIT is a sentinel value to signal exiting from the) Tj T* 0 Tw (interpreter look, you can write code like this:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R108': class PDFStream
-108 0 obj
-% page stream
-<< /Length 6953 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 717.0236 cm
-q
-BT 1 0 0 1 0 40.82 Tm 2.090651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a final ) Tj /F4 10 Tf (caveat) Tj /F1 10 Tf (: since the plac interpreter loop is implemented via extended generators, plac) Tj T* 0 Tw .988651 Tw (interpreters are single threaded: you will get an error if you ) Tj /F5 10 Tf (.send ) Tj /F1 10 Tf (commands from separated threads.) Tj T* 0 Tw .947882 Tw (You can circumvent the problem by using a queue. If EXIT is a sentinel value to signal exiting from the) Tj T* 0 Tw (interpreter look, you can write code like this:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 285.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3534,38 +3922,38 @@ n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F5 10 Tf 12 TL (with interpreter:) Tj T* ( for input_value in iter\(input_queue.get, EXIT\):) Tj T* ( output_queue.put\(interpreter.send\(input_value\)\)) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (with interpreter:) Tj T* ( for input_value in iter\(input_queue.get, EXIT\):) Tj T* ( output_queue.put\(interpreter.send\(input_value\)\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 627.8236 cm
+1 0 0 1 62.69291 253.4236 cm
q
BT 1 0 0 1 0 16.82 Tm .106098 Tw 12 TL /F1 10 Tf 0 0 0 rg (The same trick also work for processes; you could run the interpreter loop in a separate process and send) Tj T* 0 Tw (commands to it via the Queue class provided by the ) Tj 0 0 .501961 rg (multiprocessing ) Tj 0 0 0 rg (module.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 594.8236 cm
+1 0 0 1 62.69291 220.4236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Summary) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 552.8236 cm
+1 0 0 1 62.69291 178.4236 cm
q
BT 1 0 0 1 0 28.82 Tm 2.203318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Once ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (claimed to be the easiest command-line arguments parser in the world. Having read this) Tj T* 0 Tw .673322 Tw (document you may think that it is not so easy after all. But it is a false impression. Actually the rules are) Tj T* 0 Tw (quite simple:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 546.8236 cm
+1 0 0 1 62.69291 172.4236 cm
Q
q
-1 0 0 1 62.69291 546.8236 cm
+1 0 0 1 62.69291 172.4236 cm
Q
q
-1 0 0 1 62.69291 528.8236 cm
+1 0 0 1 62.69291 154.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3578,20 +3966,20 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you want to implement a command-line script, use ) Tj /F5 10 Tf (plac.call) 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 (if you want to implement a command-line script, use ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (;) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 528.8236 cm
+1 0 0 1 62.69291 154.4236 cm
Q
q
-1 0 0 1 62.69291 528.8236 cm
+1 0 0 1 62.69291 154.4236 cm
Q
q
-1 0 0 1 62.69291 468.8236 cm
+1 0 0 1 62.69291 94.42362 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3604,7 +3992,7 @@ Q
q
1 0 0 1 23 45 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you want to implement a command interpreter, use ) Tj /F5 10 Tf (plac.Interpreter) 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 (if you want to implement a command interpreter, use ) Tj /F4 10 Tf (plac.Interpreter) Tj /F1 10 Tf (:) Tj T* ET
Q
Q
q
@@ -3627,7 +4015,7 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (for an interactive interpreter, call the ) Tj /F5 10 Tf (.interact ) Tj /F1 10 Tf (method;) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (for an interactive interpreter, call the ) Tj /F4 10 Tf (.interact ) Tj /F1 10 Tf (method;) Tj T* ET
Q
Q
q
@@ -3653,7 +4041,7 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (for an batch interpreter, call the ) Tj /F5 10 Tf (.execute ) Tj /F1 10 Tf (method;) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (for an batch interpreter, call the ) Tj /F4 10 Tf (.execute ) Tj /F1 10 Tf (method;) Tj T* ET
Q
Q
q
@@ -3669,13 +4057,30 @@ q
Q
Q
q
-1 0 0 1 62.69291 468.8236 cm
+1 0 0 1 62.69291 94.42362 cm
Q
q
-1 0 0 1 62.69291 468.8236 cm
+1 0 0 1 62.69291 94.42362 cm
Q
q
-1 0 0 1 62.69291 438.8236 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 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R129': class PDFStream
+129 0 obj
+% page stream
+<< /Length 4712 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 735.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3688,20 +4093,20 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 16.82 Tm 5.126647 Tw 12 TL /F1 10 Tf 0 0 0 rg (for testing call the ) Tj /F5 10 Tf (Interpreter.check ) Tj /F1 10 Tf (method in the appropriate context or use the) Tj T* 0 Tw /F5 10 Tf (Interpreter.doctest ) Tj /F1 10 Tf (feature;) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 5.126647 Tw 12 TL /F1 10 Tf 0 0 0 rg (for testing call the ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (method in the appropriate context or use the) Tj T* 0 Tw /F4 10 Tf (Interpreter.doctest ) Tj /F1 10 Tf (feature;) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 438.8236 cm
+1 0 0 1 62.69291 735.0236 cm
Q
q
-1 0 0 1 62.69291 438.8236 cm
+1 0 0 1 62.69291 735.0236 cm
Q
q
-1 0 0 1 62.69291 420.8236 cm
+1 0 0 1 62.69291 717.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3714,45 +4119,45 @@ Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you need to go at a lower level, you may need to call the ) Tj /F5 10 Tf (Interpreter.send ) Tj /F1 10 Tf (method.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you need to go at a lower level, you may need to call the ) Tj /F4 10 Tf (Interpreter.send ) Tj /F1 10 Tf (method.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 420.8236 cm
+1 0 0 1 62.69291 717.0236 cm
Q
q
-1 0 0 1 62.69291 420.8236 cm
+1 0 0 1 62.69291 717.0236 cm
Q
q
-1 0 0 1 62.69291 402.8236 cm
+1 0 0 1 62.69291 699.0236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Moreover, remember that ) Tj /F5 10 Tf (plac_runner.py ) Tj /F1 10 Tf (is your friend.) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Moreover, remember that ) Tj /F4 10 Tf (plac_runner.py ) Tj /F1 10 Tf (is your friend.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 369.8236 cm
+1 0 0 1 62.69291 666.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Appendix: custom annotation objects) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 339.8236 cm
+1 0 0 1 62.69291 636.0236 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 /F5 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 /F5 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
+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 309.8236 cm
+1 0 0 1 62.69291 606.0236 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 180.6236 cm
+1 0 0 1 62.69291 476.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3766,21 +4171,21 @@ n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F5 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 (# 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 160.6236 cm
+1 0 0 1 62.69291 456.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 91.42362 cm
+1 0 0 1 62.69291 279.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3790,35 +4195,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 468.6898 168 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F5 10 Tf 12 TL (# example11.py) Tj T* (import plac) Tj T* (from annotations import Positional) Tj T* T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 259.6236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message you get:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R109': class PDFStream
-109 0 obj
-% page stream
-<< /Length 1943 >>
-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 130.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3832,44 +4227,40 @@ n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F5 10 Tf 12 TL (@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 101.71 Tm /F4 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 623.8236 cm
+1 0 0 1 62.69291 98.42362 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 .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 ET
Q
Q
q
-1 0 0 1 62.69291 494.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
+1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F5 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R130': class PDFStream
+130 0 obj
+% page stream
+<< /Length 379 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 450.6236 cm
+1 0 0 1 62.69291 753.0236 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 /F5 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F5 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
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
Q
Q
q
@@ -3883,275 +4274,296 @@ Q
endstream
endobj
-% 'R110': class PDFPageLabels
-110 0 obj
+% 'R131': class PDFPageLabels
+131 0 obj
% Document Root
<< /Nums [ 0
- 111 0 R
+ 132 0 R
1
- 112 0 R
+ 133 0 R
2
- 113 0 R
+ 134 0 R
3
- 114 0 R
+ 135 0 R
4
- 115 0 R
+ 136 0 R
5
- 116 0 R
+ 137 0 R
6
- 117 0 R
+ 138 0 R
7
- 118 0 R
+ 139 0 R
8
- 119 0 R
+ 140 0 R
9
- 120 0 R
+ 141 0 R
10
- 121 0 R
+ 142 0 R
11
- 122 0 R
+ 143 0 R
12
- 123 0 R
+ 144 0 R
13
- 124 0 R
+ 145 0 R
14
- 125 0 R
+ 146 0 R
15
- 126 0 R ] >>
+ 147 0 R ] >>
endobj
-% 'R111': class PDFPageLabel
-111 0 obj
+% 'R132': class PDFPageLabel
+132 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R112': class PDFPageLabel
-112 0 obj
+% 'R133': class PDFPageLabel
+133 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R113': class PDFPageLabel
-113 0 obj
+% 'R134': class PDFPageLabel
+134 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R114': class PDFPageLabel
-114 0 obj
+% 'R135': class PDFPageLabel
+135 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R115': class PDFPageLabel
-115 0 obj
+% 'R136': class PDFPageLabel
+136 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R116': class PDFPageLabel
-116 0 obj
+% 'R137': class PDFPageLabel
+137 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R117': class PDFPageLabel
-117 0 obj
+% 'R138': class PDFPageLabel
+138 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R118': class PDFPageLabel
-118 0 obj
+% 'R139': class PDFPageLabel
+139 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R119': class PDFPageLabel
-119 0 obj
+% 'R140': class PDFPageLabel
+140 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R120': class PDFPageLabel
-120 0 obj
+% 'R141': class PDFPageLabel
+141 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R121': class PDFPageLabel
-121 0 obj
+% 'R142': class PDFPageLabel
+142 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R122': class PDFPageLabel
-122 0 obj
+% 'R143': class PDFPageLabel
+143 0 obj
% None
<< /S /D
/St 12 >>
endobj
-% 'R123': class PDFPageLabel
-123 0 obj
+% 'R144': class PDFPageLabel
+144 0 obj
% None
<< /S /D
/St 13 >>
endobj
-% 'R124': class PDFPageLabel
-124 0 obj
+% 'R145': class PDFPageLabel
+145 0 obj
% None
<< /S /D
/St 14 >>
endobj
-% 'R125': class PDFPageLabel
-125 0 obj
+% 'R146': class PDFPageLabel
+146 0 obj
% None
<< /S /D
/St 15 >>
endobj
-% 'R126': class PDFPageLabel
-126 0 obj
+% 'R147': class PDFPageLabel
+147 0 obj
% None
<< /S /D
/St 16 >>
endobj
xref
-0 127
+0 148
0000000000 65535 f
0000000113 00000 n
-0000000259 00000 n
-0000000424 00000 n
-0000000599 00000 n
-0000000781 00000 n
-0000001022 00000 n
-0000001263 00000 n
-0000001504 00000 n
-0000001745 00000 n
-0000001986 00000 n
-0000002228 00000 n
-0000002470 00000 n
-0000002712 00000 n
-0000002955 00000 n
-0000003198 00000 n
-0000003441 00000 n
-0000003684 00000 n
-0000003927 00000 n
-0000004170 00000 n
-0000004413 00000 n
-0000004656 00000 n
-0000004899 00000 n
-0000005142 00000 n
-0000005385 00000 n
-0000005628 00000 n
-0000005871 00000 n
-0000006113 00000 n
-0000006364 00000 n
-0000006616 00000 n
-0000006888 00000 n
-0000007140 00000 n
-0000007379 00000 n
-0000007574 00000 n
-0000007826 00000 n
-0000008078 00000 n
-0000008330 00000 n
-0000008569 00000 n
-0000008744 00000 n
-0000008987 00000 n
-0000009239 00000 n
-0000009476 00000 n
-0000010069 00000 n
-0000010364 00000 n
-0000010614 00000 n
-0000010851 00000 n
-0000011161 00000 n
-0000011441 00000 n
-0000011736 00000 n
-0000011972 00000 n
-0000012288 00000 n
-0000012540 00000 n
-0000012777 00000 n
-0000013103 00000 n
-0000013355 00000 n
-0000013615 00000 n
-0000013867 00000 n
-0000014128 00000 n
-0000014380 00000 n
-0000014641 00000 n
-0000014877 00000 n
-0000015248 00000 n
-0000015500 00000 n
-0000015752 00000 n
-0000015989 00000 n
-0000016324 00000 n
-0000016576 00000 n
-0000016814 00000 n
-0000017140 00000 n
-0000017378 00000 n
-0000017681 00000 n
-0000017963 00000 n
-0000018259 00000 n
-0000018496 00000 n
-0000018813 00000 n
-0000019084 00000 n
-0000019336 00000 n
-0000019574 00000 n
-0000019895 00000 n
-0000020177 00000 n
-0000020337 00000 n
-0000020598 00000 n
-0000020724 00000 n
-0000020897 00000 n
-0000021109 00000 n
-0000021333 00000 n
-0000021535 00000 n
-0000021725 00000 n
-0000021919 00000 n
-0000022113 00000 n
-0000022314 00000 n
-0000022518 00000 n
-0000022702 00000 n
-0000022882 00000 n
-0000023126 00000 n
-0000030384 00000 n
-0000033470 00000 n
-0000040412 00000 n
-0000044829 00000 n
-0000049678 00000 n
-0000055523 00000 n
-0000059901 00000 n
-0000066283 00000 n
-0000071604 00000 n
-0000075442 00000 n
-0000080142 00000 n
-0000083660 00000 n
-0000087458 00000 n
-0000093545 00000 n
-0000100601 00000 n
-0000102651 00000 n
-0000102963 00000 n
-0000103042 00000 n
-0000103121 00000 n
-0000103200 00000 n
-0000103279 00000 n
-0000103358 00000 n
-0000103437 00000 n
-0000103516 00000 n
-0000103595 00000 n
-0000103674 00000 n
-0000103754 00000 n
-0000103834 00000 n
-0000103914 00000 n
-0000103994 00000 n
-0000104074 00000 n
-0000104154 00000 n
+0000000270 00000 n
+0000000435 00000 n
+0000000610 00000 n
+0000000791 00000 n
+0000001043 00000 n
+0000001293 00000 n
+0000001551 00000 n
+0000001712 00000 n
+0000001906 00000 n
+0000002148 00000 n
+0000002390 00000 n
+0000002632 00000 n
+0000002874 00000 n
+0000003116 00000 n
+0000003359 00000 n
+0000003602 00000 n
+0000003845 00000 n
+0000004088 00000 n
+0000004331 00000 n
+0000004574 00000 n
+0000004803 00000 n
+0000004989 00000 n
+0000005232 00000 n
+0000005475 00000 n
+0000005718 00000 n
+0000005961 00000 n
+0000006204 00000 n
+0000006447 00000 n
+0000006690 00000 n
+0000006933 00000 n
+0000007176 00000 n
+0000007419 00000 n
+0000007662 00000 n
+0000007904 00000 n
+0000008156 00000 n
+0000008408 00000 n
+0000008660 00000 n
+0000008912 00000 n
+0000009164 00000 n
+0000009407 00000 n
+0000009644 00000 n
+0000010256 00000 n
+0000010513 00000 n
+0000010831 00000 n
+0000011088 00000 n
+0000011330 00000 n
+0000011642 00000 n
+0000011939 00000 n
+0000012191 00000 n
+0000012443 00000 n
+0000012695 00000 n
+0000012955 00000 n
+0000013207 00000 n
+0000013468 00000 n
+0000013720 00000 n
+0000013981 00000 n
+0000014218 00000 n
+0000014608 00000 n
+0000014859 00000 n
+0000015095 00000 n
+0000015422 00000 n
+0000015694 00000 n
+0000015946 00000 n
+0000016183 00000 n
+0000016519 00000 n
+0000016771 00000 n
+0000017028 00000 n
+0000017265 00000 n
+0000017601 00000 n
+0000017860 00000 n
+0000018112 00000 n
+0000018364 00000 n
+0000018623 00000 n
+0000018882 00000 n
+0000019134 00000 n
+0000019391 00000 n
+0000019643 00000 n
+0000019902 00000 n
+0000020161 00000 n
+0000020413 00000 n
+0000020665 00000 n
+0000020917 00000 n
+0000021169 00000 n
+0000021407 00000 n
+0000021837 00000 n
+0000022134 00000 n
+0000022384 00000 n
+0000022622 00000 n
+0000022935 00000 n
+0000023232 00000 n
+0000023469 00000 n
+0000023787 00000 n
+0000024058 00000 n
+0000024296 00000 n
+0000024623 00000 n
+0000024861 00000 n
+0000025165 00000 n
+0000025448 00000 n
+0000025611 00000 n
+0000025891 00000 n
+0000026020 00000 n
+0000026196 00000 n
+0000026416 00000 n
+0000026622 00000 n
+0000026817 00000 n
+0000027015 00000 n
+0000027217 00000 n
+0000027411 00000 n
+0000027606 00000 n
+0000027811 00000 n
+0000028020 00000 n
+0000028208 00000 n
+0000028392 00000 n
+0000028638 00000 n
+0000037365 00000 n
+0000041701 00000 n
+0000045945 00000 n
+0000052177 00000 n
+0000058074 00000 n
+0000062825 00000 n
+0000066908 00000 n
+0000071395 00000 n
+0000076595 00000 n
+0000080901 00000 n
+0000086351 00000 n
+0000090348 00000 n
+0000096101 00000 n
+0000102323 00000 n
+0000107138 00000 n
+0000107623 00000 n
+0000107935 00000 n
+0000108014 00000 n
+0000108093 00000 n
+0000108172 00000 n
+0000108251 00000 n
+0000108330 00000 n
+0000108409 00000 n
+0000108488 00000 n
+0000108567 00000 n
+0000108646 00000 n
+0000108726 00000 n
+0000108806 00000 n
+0000108886 00000 n
+0000108966 00000 n
+0000109046 00000 n
+0000109126 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\001z\236\304t\027\345#m\031\\1\234-\227\346) (\001z\236\304t\027\345#m\031\\1\234-\227\346)]
+ [(\356\212\223m\215cGtv\230\324LxP\321{) (\356\212\223m\215cGtv\230\324LxP\321{)]
- /Info 80 0 R
- /Root 79 0 R
- /Size 127 >>
+ /Info 100 0 R
+ /Root 99 0 R
+ /Size 148 >>
startxref
-104203
+109175
%%EOF
diff --git a/plac/doc/plac_adv.txt b/plac/doc/plac_adv.txt
index 378e692..5e1146a 100644
--- a/plac/doc/plac_adv.txt
+++ b/plac/doc/plac_adv.txt
@@ -1,35 +1,36 @@
Testing and scripting your applications with plac
=========================================================
+:Author: Michele Simionato
+:E-mail: michele.simionato@gmail.com
+:Date: June 2010
+:Download page: http://pypi.python.org/pypi/plac
+:Project page: http://micheles.googlecode.com/hg/plac/doc/plac.html
+:Installation: ``easy_install -U plac``
+:License: BSD license
+:Requires: Python 2.5+
+
+*The present document discusses a few of the advanced use
+cases for plac. It assumes you have already read an understood the
+basic documentation.*
+
.. contents::
Introduction
-----------------------------------------------------
-plac_ has been designed to be simple to use for simple stuff, but its
-power should not be underestimated; it is actually a quite advanced
-tool with a domain of applicability which far exceeds the realm of
-command-line arguments parsers.
-
-In this document I will discuss a few of the advanced use cases for plac_.
-I assume you have already read an understood the `basic documentation`_.
-
-One of the goals of plac_ is to make it dead easy to write
-a scriptable and testable interface for an application, even if originally
-the application is not a command-line application.
+One of the design goals of plac_ is to make it dead easy to write a
+scriptable and testable interface for an application. You can use
+plac_ whenever you have an API with strings in input and strings in
+output, and that includes a *huge* domain of applications.
-You can use plac_ whenever you have an API with strings
-in input and strings in output, and this includes a *huge* domain
-of applications. A string-oriented interface
-is also a scriptable interface. That means that you can define
-a command language for your application and that it is possible
-to write scripts which are interpreted by plac_ and can be run
-as batch scripts to execute any kind of operations.
+A string-oriented interface is a scriptable interface by
+construction. That means that you can define a command language for
+your application and that it is possible to write scripts which are
+interpretable by plac_ and can be run as batch scripts.
Actually, at the most general level, you can see plac_ as a generic tool to
-write domain specific languages (DSL). plac_ makes it dead easy
-to write interpreters for command-oriented languages, both
-interactive interpreters and batch interpreters. With plac_ you
+write domain specific languages (DSL). With plac_ you
can test your application interactively as well as with batch
scripts, and even with the analogous of Python doctests for your
defined language.
@@ -39,89 +40,12 @@ you could easily write an application like twill_ with plac_. Or you
could use it to script your building procedure. Or any other thing,
your imagination is the only limit!
-A simple example: a shelve interface
-----------------------------------------------------------
-
-Since I like to be concrete and to show examples, let me start by
-considering the following use case: you want to store some
-configuration parameters into a Python shelve and you need a
-command-line tool to edit your configuration, i.e. you want a shelve
-interface.
-A possible implementation using plac_ could be the following:
-
-.. include:: ishelve.py
- :literal:
-
-A few notes are in order:
-
-1. I have disabled the ordinary help provided by argparse_ and I have
- implemented a custom help command.
-2. I have changed the prefix character used to recognize the options
- to a dot: I like to change the prefix character when I disable the
- default help.
-3. A plac-specific feature, i.e. keyword arguments recognition is put
- to good use to make it possible to store a value in the shelve with
- the syntax ``param_name=param_value``.
-4. varargs are used to retrieve parameters from the shelve and some
- error checking is performed in the case of missing parameters
-5. A command to clear the shelve is implemented as a flag (``.clear``).
-6. A command to delete a given parameter is implemented as an option
- (``.delete``).
-7. There is an option with default (``.filename=conf.shelve``) to store
- the filename of the shelve.
-8. All things considered, the code looks like a poor man object oriented
- interface implemented with a chain of elifs instead of methods. Of course,
- plac_ can do better than that, but I am starting from a low-level approach
- first.
-
-.. If the script is invoked with no arguments, the default help is
- invoked: this is done by invoking the argparse_ low-level method
- ``.format_help``. In other words, the recognition of the couple
- ``-h/--help`` is disabled, but the original help feature is still there.
-
-If you run ``ishelve.py`` without arguments you get the following
-message::
-
- $ python ishelve.py
- no arguments passed, use .help to see the available commands
-
-If you run ``ishelve.py`` with the option ``.h`` (or any abbreviation
-of ``.help``) you get::
-
- $ python ishelve.py .h
- Commands: .help, .showall, .clear, .delete
- <param> ...
- <param=value> ...
-
-You can check by hand that the tool work::
-
- $ python ishelve.py .clear # start from an empty shelve
- cleared the shelve
- $ python ishelve.py a=1 b=2
- setting a=1
- setting b=2
- $ python ishelve.py .showall
- b=2
- a=1
- $ python ishelve.py .del b # abbreviation for .delete
- deleted b
- $ python ishelve.py a
- 1
- $ python ishelve.py b
- b: not found
- $ python ishelve.py .cler # mispelled command
- usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
- ishelve.py: error: unrecognized arguments: .cler
-
-Command-line scripts have many advantages, but are no substitute
-for a real interactive application.
-
-Turning a script into an interactive application
+From scripts to interactive applications
------------------------------------------------------------
-If you have a script with a large startup time which must be run
+Command-line scripts have many advantages, but are no substitute
+for interactive applications.
+In particular, if you have a script with a large startup time which must be run
multiple times, it is best to turn it into an interactive application,
so that the startup is performed only once. ``plac`` provides an
``Interpreter`` class just for this purpose.
@@ -130,16 +54,19 @@ The ``Interpreter`` class wraps the main function of a script and
provides an ``.interact`` method to start an interactive interpreter
reading commands from the console.
-You could define an interactive interpreter on top of ``ishelve`` as
+For instance, you can define an interactive interpreter on top of the
+``ishelve`` script introduced in the `basic documentation`_ as
follows:
.. include:: shelve_interpreter.py
:literal:
A trick has been used here: the ishelve command-line interface has been
-hidden inside and external interface. They are distinct: for instance
+hidden inside an external interface. They are distinct: for instance
the external interface recognizes the ``-h/--help`` flag whereas the
-internal interface only recognizes the ``.help`` command:
+internal interface only recognizes the ``.help`` command::
+
+ $ python shelve_interpreter.py -h
.. include:: shelve_interpreter.help
:literal:
@@ -150,8 +77,8 @@ and non-interactively::
$ python shelve_interpreter.py .clear # non-interactive use
cleared the shelve
-Here is an usage session, using ``rlwrap`` to enable readline features
-(``rlwrap`` is available in Unix-like systems)::
+Here is an usage session, using rlwrap_ to enable readline features
+(rlwrap_ is available in Unix-like systems)::
$ rlwrap python shelve_interpreter.py -i # interactive use
usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]
@@ -177,38 +104,16 @@ Here is an usage session, using ``rlwrap`` to enable readline features
The ``.interact`` method
reads commands from the console and send them to the
underlying interpreter, until the user send a CTRL-D
-command (CTRL-Z in Windows). There are two default
-arguments ``prompt='i> '``, and ``intro=None`` which
-can be used to change the prompt and the message displayed
-at the beginning, which by default is the argparse-provided
-usage message.
+command (CTRL-Z in Windows). There is a default
+argument ``prompt='i> '`` which
+can be used to change the prompt. The message displayed
+by default is the argparse-provided usage message, but can be
+customized by setting an ``.intro`` attribute on the main function.
Notice that ``plac.Interpreter`` is available only if you are using a recent
version of Python (>= 2.5), because it is a context manager object
which uses extended generators internally.
-The distribution of ``plac`` includes a runner script named ``plac_runner.py``
-which will be installed in a suitable directory in your system by distutils
-(say in ``\usr\local\bin\plac_runner.py`` in a Unix-like operative system).
-The easiest way to turn a script into an interactive application is
-to use the runner. If you put this alias in your bashrc
-
- ``alias plac="rlwrap plac_runner.py"``
-
-(or you define a suitable ``plac.bat`` script in Windows) you can
-run the original ``ishelve.py`` script in interactive mode as
-follows::
-
- $ plac -i ishelve.py
- usage: plac_runner.py ishelve.py [.help] [.showall] [.clear]
- [.delete DELETE] [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
- i>
-
-In other words, there is no need to write the interactive
-wrapper (``shelve_interpreter.py``) by hand, the plac runner
-does the job for you.
-
You can conveniently test your application in interactive mode.
However manual testing is a poor substitute for automatic testing.
@@ -241,9 +146,7 @@ interpreter is different from the expected output for the given input.
``AssertionError`` is catched by tools like ``py.test`` and
``nosetests`` and actually ``plac`` tests are intended to be run with
-such tools. If you want to use the ``unittest`` module in the
-standard library you can, but I am not going to support it directly
-(reminder: plac_ is opinionated and I dislike the unittest module).
+such tools.
Interpreters offer a minor syntactic advantage with respect to calling
``plac.call`` directly, but they offer a *major* semantic advantage when things
@@ -261,58 +164,37 @@ There is a second advantage of interpreters: if the main function contains some
initialization code and finalization code
(``__enter__`` and ``__exit__`` functions) they will be run only
once at the beginning and at the end of the interpreter loop.
-``plac.call`` also executes the initialization/finalization code,
-but it runs it at each call, and that may be too expensive.
+``plac.call`` instead ignores the initialization/finalization code.
-Plac easytests
+Plac easy tests
---------------------------------------------------------
Writing your tests in terms of ``Interpreter.check`` is certainly an
improvement over writing them in terms of ``plac.call``, but they
are still too low-level for my taste. The ``Interpreter`` class provides
-support for doctest-style tests, a.k.a. *plac easytests*.
+support for doctest-style tests, a.k.a. *plac easy tests*.
By using plac easy tests you can cut and paste your interactive session and
turn it into a runnable automatics test!
Consider for instance the following file ``ishelve.placet`` (the ``.placet``
-extension is a mnemonic for plac easytests):
+extension is a mnemonic for plac easy tests):
.. include:: ishelve.placet
:literal:
Notice the precence of the shebang line containing the name of the
plac_ tool to test (a plac_ tool is just a Python module with a
-function called ``main``). You can doctest it by calling the
-``.doctest`` method of the interpreter
+function called ``main``). The shebang is ignored by the interpreter
+(it looks like a comment to it) but it is there so that external
+tools (say a test runner) can infer the plac interpreter
+to use to test the file.
-::
+You can test ``ishelve.placet`` file by calling the
+``.doctest`` method of the interpreter, as in this example::
$ python -c"import plac, ishelve
plac.Interpreter(ishelve.main).doctest(open('ishelve.placet'), verbose=True)"
-and you will get the following output:
-
-.. include:: doctest_out.txt
- :literal:
-
-You can also run placets following the shebang convention directly with
-the plac runner::
-
- $ plac --easytest ishelve.placet
- run 1 plac easy test(s)
-
-The runner ignore the extension, so you can actually use any extension
-your like, but *it relies on the first line of the file to correspond
-to an existing plac tool*, so you cannot skip it and you cannot write a
-wrong shebang.
-
-The plac runner does not provide any test discovery facility,
-but you can use standard Unix tools to help. For instance, you can
-run all the ``.placet`` files into a directory and its subdirectories
-as follows::
-
- $ find . -name \*.placet | xargs plac_runner.py -e
-
Internally ``Interpreter.doctests`` invokes ``Interpreter.check``
multiple times inside the same context and compare the output with the
expected output: if even a check fails, the whole test fail. The
@@ -336,16 +218,32 @@ stored in external files (plac easy tests does not need to be in
a file: you can just pass to the ``.doctest`` method a list of
strings corresponding to the lines of the file).
-It is straighforward to integrate your .placet tests with standard
-testing tools. For instance, you can integrate your doctests with nose
-or py.test as follow:
+At the present plac easy tests do not use any code from the doctest
+module, but the situation may change in the future (it would be nice
+if plac_ could reuse doctests directives like ELLIPSIS).
-.. include:: test_doct.py
- :literal:
+It is straighforward to integrate your ``.placet`` tests with standard
+testing tools. For instance, you can integrate your doctests with nose
+or py.test as follow::
-Here you should notice that usage of ``plac.import_main(path)``, an utility
-which is able to import the main function of the specified script.
-You can use both the full path name of the
+ import os, shlex, plac
+
+ def test_doct():
+ """
+ Find all the doctests in the current directory and run them with the
+ corresponding plac interpreter (the shebang rules!)
+ """
+ placets = [f for f in os.listdir('.') if f.endswith('.placet')]
+ for placet in placets:
+ lines = list(open(placet))
+ assert lines[0].startswith('#!'), 'Missing or incorrect shebang line!'
+ firstline = lines[0][2:] # strip the shebang
+ main = plac.import_main(*shlex.split(firstline))
+ yield plac.Interpreter(main).doctest, lines[1:]
+
+Here you should notice that usage of ``plac.import_main``, an utility
+which is able to import the main function of the script specified in
+the shabng line. You can use both the full path name of the
tool, or a relative path name. In this case the runner look at the
environment variable ``PLACPATH`` and it searches
the plac tool in the directories specified there (``PLACPATH`` is just
@@ -359,30 +257,28 @@ Plac batch scripts
It is pretty easy to realize that an interactive interpreter can
also be used to run batch scripts: instead of reading the commands from
the console, it is enough to read the commands from a file.
-plac_ interpreters provide an ``.execute`` method to perform just that::
-
- plac.Interpreter(main).execute(line_iterator)
+plac_ interpreters provide an ``.execute`` method to perform just that.
-There is just a subtle point to notice: whereas in an interactive
-loop one wants to manage all exceptions, in a batch script we want to
-make sure that the script does not continue in the background
-in case of unexpected errors. The implementation of ``Interpreter.execute``
-makes sure that any error raised by ``plac.call`` internally is re-raised.
-In other words, plac_ interpreters *wrap the errors, but does not eat them*:
-the errors are always accessible and can be re-raised on demand.
+There is just a subtle point to notice: whereas in an interactive loop
+one wants to manage all exceptions, a batch script should not in the
+background in case of unexpected errors. The implementation of
+``Interpreter.execute`` makes sure that any error raised by
+``plac.call`` internally is re-raised. In other words, plac_
+interpreters *wrap the errors, but does not eat them*: the errors are
+always accessible and can be re-raised on demand.
In particular consider the following batch file, which contains a syntax
error (``.dl`` instead of ``.del``):
-.. include:: ishelve.batch
+.. include:: ishelve.plac
:literal:
If you execute the batch file, the interpreter will raise a ``SystemExit``
with an appropriated error message at the ``.dl`` line and the last command
-will *not* be executed. The easiest way to execute the batch file is
-to invoke the ``plac`` runner::
+will *not* be executed::
- $ plac --batch --verbose ishelve.batch
+ $ python -c "import plac, ishelve
+ plac.Interpreter(ishelve.main).execute(open('ishelve.plac'), verbose=True)"
i> .clear
cleared the shelve
i> a=1 b=2
@@ -396,18 +292,18 @@ to invoke the ``plac`` runner::
i> .dl b
unrecognized arguments: .dl
-The ``--verbose`` flag is there to show the lines which are being interpreted
+The ``verbose`` flag is there to show the lines which are being interpreted
(prefixed by ``i>``). This is done on purpose, so that you can cut and paste
the output of the batch script and turn it into a ``.placet`` test
(cool, isn't it?).
-Command containers
+Containers of commands
----------------------------------------
-When I discussed the ``ishelve`` implementation, I said that it looked
-like a poor man implementation of an object system as a chain of
-elifs; I also said that plac_ was able to do better.
-Here I will substantiate my claim.
+When I discussed the ``ishelve`` implementation in the `basic
+documentation`_, I said that it looked like a poor man implementation
+of an object system as a chain of elifs; I also said that plac_ was
+able to do better. Here I will substantiate my claim.
plac_ is actually able to infer a set of subparsers from a
generic container of commands. This is useful if you want to
@@ -425,71 +321,216 @@ object-oriented way as follows:
.. include:: ishelve2.py
:literal:
-You should notice that ``plac.call`` understands the context manager
-protocol: if you call an object with ``__enter__`` and ``__exit__``
-methods, they are invoked in the right order (``__enter__`` before
-the call and ``__exit__`` after the call, both in the regular
-and in the exceptional case). Since ``plac.call`` does not use
-the ``with`` statement internally, such feature works even in old versions
-of Python, before the introduction of the context manager
-protocol (in Python 2.5). In our example,
-the methods ``__enter__`` and ``__exit__`` make sure the the shelve
-is opened and closed correctly even in the case of exceptions. Notice
-that I have not implemented any error checking in the ``show`` and
-``delete`` methods on purpose, to verify that plac_ works
-correctly in the presence of exceptions (in particular I want to show
-that plac_ does not "eat" the traceback).
+``plac.Interpreter`` objects wrap context manager objects
+consistently. In other words, if you wrap an object with
+``__enter__`` and ``__exit__`` methods, they are invoked in the right
+order (``__enter__`` before the interpreter loop starts and
+``__exit__`` after the interpreter loop ends, both in the regular and
+in the exceptional case). In our example, the methods ``__enter__``
+and ``__exit__`` make sure the the shelve is opened and closed
+correctly even in the case of exceptions. Notice that I have not
+implemented any error checking in the ``show`` and ``delete`` methods
+on purpose, to verify that plac_ works correctly in the presence of
+exceptions.
Here is a session of usage on an Unix-like operating system::
-
- $ alias conf="python ishelve2.py"
- $ conf set a pippo
+
+ $ rlwrap python ishelve2.py
+ Operating on /home/micheles/conf.shelve. Available commands:
+ set
+ show
+ showall
+ delete
+ i> delete
+ deleting everything
+ i> set a pippo
setting a=pippo
- $ conf set b lippo
+ i> set b lippo
setting b=lippo
- $ conf showall
+ i> showall
b = lippo
a = pippo
- $ conf show a b
+ i> show a b
a = pippo
b = lippo
- $ conf del a # an abbreviation
+ i> del a
deleting a
- $ conf showall
+ i> showall
b = lippo
- $ conf delete a # notice the full traceback
- Traceback (most recent call last):
- ...
- _bsddb.DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
-
-Notice that in script mode you get the full traceback, whereas in
-interactive mode the traceback is hidden::
-
- $ plac -i ishelve2.py
- usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...
- i> del a
+ i> delete a
DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
i>
-You can see the traceback if you start the runner in verbose mode
-(``plac -vi ishelve2.py``).
+Notice that in interactive mode the traceback is hidden, unless
+you pass the ``verbose`` flag to the ``Interpreter.interact`` method.
The interactive mode of ``plac`` can be used as a replacement of the
``cmd`` module in the standard library. There are a few differences,
-however. For instance you miss tab completion, even if use ``rlwrap``
+however. For instance you miss tab completion, even if use rlwrap_
(you get persistent command history for free, however). This is not
-a big issue, since ``plac`` understands *command abbreviations*
-(in all modes, not only in interactive mode).
+a big issue, since ``plac`` understands command abbreviations.
-If an abbreviation is ambiguous, plac warns you::
+If an abbreviation is ambiguous, plac_ warns you::
- $ plac ishelve2.py -i
+ $ rlwrap python ishelve2.py
usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...
i> sh
NameError: Ambiguous command 'sh': matching ['showall', 'show']
-Giving the same abbreviation in script mode gives the same error
-but also shows the full traceback.
+For ``cmd`` lovers
+--------------------------------------------------------
+
+I have been using the cmd_ module of the standard library for years.
+I have also written a much enhanced ``cmd2`` module which we are using
+internally at work and from which I have taken some ideas used in plac_.
+In many ways plac_ makes the cmd_ module obsolete,
+but I realize why many nostalgic souls would still use cmd_, especially
+until plac_ does not grow real auto-completion features, instead of
+relying on rlwrap_. But there must not be competition between plac_
+and cmd_: actually the two can happily work togethere. For this
+reason I have put in the ``plac_ext`` module a few lines of code
+for gluing together cmd_ and plac_, the ``cmd_interface``.
+Using the ``cmd_interface`` is quite trivial: give to it a plac
+command container and you will get in exchange a ``cmd.Cmd`` object:
+
+.. include:: cmd_ex.py
+ :literal:
+
+Here is an example of interactive session::
+
+ $ python cmd_ex.py
+ (Cmd) help
+
+ Documented commands (type help <topic>):
+ ========================================
+ delete set show showall
+
+ Undocumented commands:
+ ======================
+ EOF help
+
+ (Cmd) set a 1
+ setting a=1
+ (Cmd) show a
+ a = 1
+ (Cmd) showall
+ a = 1
+ (Cmd) delete b
+ KeyError: 'b'
+ (Cmd) EOF [or CTRL-D]
+
+Internally the ``cmd_interface`` builds a ``cmd.Cmd`` class and adds
+to it the ``do_`` methods corresponding to the commands in the container,
+then it returns a ``cmd.Cmd`` instance.
+
+The ``cmd_interface`` is just a proof of concept: it is there so that you
+can study the source code and see an example of integration of plac_
+with a different framework. It may change and even go away in future
+releases of plac_.
+
+The plac runner
+--------------------------------------------------------
+
+The distribution of plac_ includes a runner script named ``plac_runner.py``,
+which will be installed in a suitable directory in your system by distutils_
+(say in ``\usr\local\bin\plac_runner.py`` in a Unix-like operative system).
+The runner provides many facilities to run ``.plac`` scripts and
+``.placet`` files, as well as Python modules containg a ``main``
+object, which can be a function, a command container object or
+even a command container class.
+
+For instance, suppose you want to execute a script containing commands
+defined in the ``ishelve2`` module like the following one:
+
+.. include:: ishelve2.plac
+ :literal:
+
+The first line of the ``.plac`` script contains the name of the
+python module containing the plac interpreter and the arguments
+which must be passed to its main function in order to be able
+to instantiate an interpreter object. The other lines contains
+commands. Then you can run the script as follows::
+
+ $ plac_runner.py --batch ishelve2.plac
+ setting a=1
+ deleting a
+ Traceback (most recent call last):
+ ...
+ _bsddb.DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
+
+The last command intentionally contained an error, to show that the
+plac runner does not eat the traceback.
+
+The runner can also be used to run Python modules in interactive
+mode and non-interactive mode. If you put this alias in your bashrc
+
+ ``alias plac="rlwrap plac_runner.py"``
+
+(or you define a suitable ``plac.bat`` script in Windows) you can
+run the ``ishelve2.py`` script in interactive mode as
+follows::
+
+ $ plac -i ishelve2.py
+ Operating on /home/micheles/conf.shelve. Available commands:
+ set
+ show
+ showall
+ delete
+ i> del
+ deleting everything
+ i> set a 1
+ setting a=1
+ i> set b 2
+ setting b=2
+ i> show b
+ b = 2
+
+Now you can cut and paste the interactive session an turns into into
+a ``.placet`` file like the following:
+
+.. include:: ishelve2.placet
+ :literal:
+
+Notice that the first line specifies a test database
+``~/test.shelve``, to avoid clobbering your default shelve. If you
+mispell the arguments in the first line plac will give you an
+argparse_ error message (just try).
+
+You can run placets following the shebang convention directly with
+the plac runner::
+
+ $ plac --test ishelve2.placet
+ run 1 plac test(s)
+
+If you want to see the output of the tests, pass the ``-v/--verbose`` flag.
+Notice that he runner ignore the extension, so you can actually use any
+extension your like, but *it relies on the first line of the file to invoke
+the corresponding plac tool with the given arguments*.
+
+The plac runner does not provide any test discovery facility,
+but you can use standard Unix tools to help. For instance, you can
+run all the ``.placet`` files into a directory and its subdirectories
+as follows::
+
+ $ find . -name \*.placet | xargs plac_runner.py -t
+
+The plac runner expects the main function of your script to
+return a plac tool, i.e. a function or an object with a ``.commands``
+attribute. It this is not the case the runner gracefully exits.
+
+It also works in non-interactive mode, if you call it as
+
+ ``$ plac module.py args ...``
+
+Here is an example::
+
+ $ plac ishelve.py a=1
+ setting a=1
+ $ plac ishelve.py .show
+ a=1
+
+Notice that it non-interactive mode the runner just invokes ``plac.call``
+on the ``main`` object of the Python module.
A non class-based example
--------------------------------------------------------
@@ -499,14 +540,14 @@ Even a simple function can be a valid command container, it is
enough to add to it a ``.commands`` attribute and possibly
``__enter__`` and/or ``__exit__`` attributes.
-A Python module is a perfect container of commands. As an
+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:
-Notice that I have defined both and ``__exit__`` hook and a ``__missing__``
+Notice that I have defined both an ``__exit__`` hook and a ``__missing__``
hook, invoked for non-existing commands.
The real trick here is the line ``main = __import__(__name__)``, which
define ``main`` to be an alias for the current module.
@@ -536,15 +577,12 @@ usage message, as well as the documentation for the sub flag ``-q``.
Here is an example of a non-interactive session::
$ plac vcs.py check url
- ok
checkout
url
$ plac vcs.py st -q
- ok
status
True
$ plac vcs.py co
- ok
commit
None
@@ -567,27 +605,18 @@ and here is an interactive session::
ok
Notice the invocation of the ``__missing__`` hook for non-existing commands.
-Notice also that the ``__exit__`` hook gets called differently in interactive
-mode and non-interactive mode: in the first case it is called at the end of the
-interactive loop with a ``GeneratorExit`` exception, whereas in
-the second case there is no exception.
+Notice also that the ``__exit__`` hook gets called only in interactive
+mode.
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.
-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.
-
Writing your own plac runner
----------------------------------------------------------
The runner included in the plac_ distribution is intentionally kept
-small (around 40 lines of code) so that you can study it and write
+small (around 50 lines of code) so that you can study it and write
your own runner if want to. If you need to go to such level
of detail, you should know that the most important method of
the ``Interpreter`` class is the ``.send`` method, which takes
@@ -735,3 +764,11 @@ pretty well for most users.
.. _basic documentation: http://micheles.googlecode.com/hg/plac/doc/plac.html
.. _shlex: http://docs.python.org/library/shlex.html
.. _multiprocessing: http://docs.python.org/library/multiprocessing.html
+.. _distutils: http://docs.python.org/distutils/
+.. _cmd: http://docs.python.org/library/cmd.html
+.. _rlwrap: http://freshmeat.net/projects/rlwrap/
+
+.. If the script is invoked with no arguments, the default help is
+ invoked: this is done by invoking the argparse_ low-level method
+ ``.format_help``. In other words, the recognition of the couple
+ ``-h/--help`` is disabled, but the original help feature is still there.
diff --git a/plac/doc/shelve_interpreter.py b/plac/doc/shelve_interpreter.py
index a19f5c9..b7fb688 100644
--- a/plac/doc/shelve_interpreter.py
+++ b/plac/doc/shelve_interpreter.py
@@ -1,3 +1,4 @@
+# shelve_interpreter.py
import plac, ishelve
@plac.annotations(
diff --git a/plac/doc/test_ishelve.py b/plac/doc/test_ishelve.py
index 23cf2bd..a34809d 100644
--- a/plac/doc/test_ishelve.py
+++ b/plac/doc/test_ishelve.py
@@ -1,14 +1,12 @@
# test_ishelve.py
-import plac
-from ishelve import ishelve
+import plac, 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']
+ assert plac.call(ishelve.main, ['.clear']) == ['cleared the shelve']
+ assert plac.call(ishelve.main, ['a=1']) == ['setting a=1']
+ assert plac.call(ishelve.main, ['a']) == ['1']
+ assert plac.call(ishelve.main, ['.delete=a']) == ['deleted a']
+ assert plac.call(ishelve.main, ['a']) == ['a: not found']
if __name__ == '__main__':
test()
diff --git a/plac/doc/test_plac.py b/plac/doc/test_plac.py
index 6a8e7bb..450f9ff 100644
--- a/plac/doc/test_plac.py
+++ b/plac/doc/test_plac.py
@@ -2,7 +2,7 @@
The tests are runnable with nose, with py.test, or even as standalone script
"""
-import os, sys
+import os, sys, doctest, subprocess
import plac
sys_argv0 = sys.argv[0]
@@ -107,10 +107,10 @@ 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',)", "{'a': '1', 'b': '2'}"], argskw
+ assert argskw == [('arg2',), {'a': '1', 'b': '2'}], argskw
argskw = plac.call(main, ['arg1', 'arg2', 'a=1', '-o', '2'])
- assert argskw == ["('arg2',)", "{'a': '1'}"], argskw
+ assert argskw == [('arg2',), {'a': '1'}], argskw
expect(SystemExit, plac.call, main, ['arg1', 'arg2', 'a=1', 'opt=2'])
@@ -132,12 +132,12 @@ class Cmds(object):
cmds = Cmds()
def test_cmds():
- assert ['commit'] == plac.call(cmds, ['commit'])
+ assert 'commit' == plac.call(cmds, ['commit'])
assert ['help', 'foo'] == plac.call(cmds, ['help', 'foo'])
expect(SystemExit, plac.call, cmds, [])
def test_cmd_abbrevs():
- assert ['commit'] == plac.call(cmds, ['comm'])
+ assert 'commit' == plac.call(cmds, ['comm'])
assert ['help', 'foo'] == plac.call(cmds, ['h', 'foo'])
expect(SystemExit, plac.call, cmds, ['foo'])
@@ -145,7 +145,23 @@ def test_yield():
def main():
for i in (1, 2, 3):
yield i
- assert plac.call(main, []) == ['1', '2', '3']
+ assert plac.call(main, []) == [1, 2, 3]
+
+def test_doctest():
+ failure, tot= doctest.testfile('plac.txt', module_relative=False)
+ assert not failure, failure
+
+def test_batch():
+ for batch in os.listdir('.'):
+ if batch.endswith('.plac'):
+ args = [sys.executable, '../plac_runner.py', '-b', batch]
+ yield subprocess.call, args
+
+def test_placet():
+ for placet in os.listdir('.'):
+ if placet.endswith('.placet'):
+ args = [sys.executable, '../plac_runner.py', '-t', placet]
+ yield subprocess.call, args
if __name__ == '__main__':
n = 0
diff --git a/plac/plac.py b/plac/plac.py
index 361522e..3b7316d 100644
--- a/plac/plac.py
+++ b/plac/plac.py
@@ -30,18 +30,21 @@ See doc/plac.pdf for the documentation.
__version__ = '0.5.0'
-import imp, os, sys
+import imp, os, sys, inspect
from plac_core import *
if sys.version >= '2.5':
- from plac_ext import Interpreter, cmd_interface
+ from plac_ext import Interpreter
try:
PLACDIRS = os.environ.get('PLACPATH', '.').split(':')
except:
raise ValueError('Ill-formed PLACPATH: got %PLACPATHs' % os.environ)
-def import_main(path):
- "An utility to import the main function of a plac tool"
+def import_main(path, *args, **pconf):
+ """
+ An utility to import the main function of a plac tool. It also
+ works with tool factories, if you pass the arguments.
+ """
if not os.path.isabs(path): # relative path, look at PLACDIRS
for placdir in PLACDIRS:
fullpath = os.path.join(placdir, path)
@@ -52,4 +55,11 @@ def import_main(path):
else:
fullpath = path
name, ext = os.path.splitext(os.path.basename(fullpath))
- return imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1)).main
+ tool = imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1)).main
+ if args:
+ tool = parser_from(tool).consume(args) # instantiate the factory
+ elif inspect.isclass(tool):
+ tool = tool() # instantiate it
+ vars(tool).update(pconf)
+ parser_from(tool) # raise a TypeError if not
+ return tool
diff --git a/plac/plac_core.py b/plac/plac_core.py
index defb215..98faa08 100644
--- a/plac/plac_core.py
+++ b/plac/plac_core.py
@@ -26,10 +26,13 @@ def annotations(**ann):
args.append(fas.varargs)
if fas.varkw:
args.append(fas.varkw)
+ ret = ann.pop('return_', None) # return_ is return
for argname in ann:
if argname not in args:
raise NameError(
'Annotating non-existing argument: %s' % argname)
+ if ret:
+ ann['return'] = ret
f.__annotations__ = ann
return f
return annotate
@@ -60,7 +63,7 @@ class Annotation(object):
"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):
+ elif hasattr(obj, '__iter__') and not isinstance(obj, basestring):
return cls(*obj)
return cls(obj)
from_ = classmethod(from_)
@@ -84,10 +87,9 @@ def _parser_from(func, baseparser=None):
(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 = baseparser or ArgumentParser(**pconf(func))
p.func = func
p.argspec = f = getfullargspec(func)
- p.parselist = _parser_call.__get__(p, p.__class__)
# add func.p
if inspect.ismethod(func):
del f.args[0] # remove self
@@ -138,30 +140,26 @@ def _parser_from(func, baseparser=None):
type=a.type, metavar=a.metavar)
return p
-def addsubparser(cmd, func, parser):
- if not hasattr(parser, 'subparsers'):
- parser.subparsers = parser.add_subparsers(
- title='subcommands', help='-h to get additional help')
- subp = parser.subparsers.add_parser(cmd, **pconf(func))
- return parser_from(func, subp)
-
def parser_from(obj, baseparser=None):
"""
- obj can be a function, a bound method, or a generic object with a
+ obj can be a class, 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 inspect.isclass(obj):
+ p = parser_from(obj.__init__)
+ p.func = obj
+ return p
elif hasattr(obj, 'commands'): # a command container
- p = obj.p = baseparser or argparse.ArgumentParser(**pconf(obj))
+ p = obj.p = baseparser or ArgumentParser(**pconf(obj))
for cmd in obj.commands:
- addsubparser(cmd, getattr(obj, cmd), p)
- p.missing = getattr(obj, '__missing__',
- lambda name: p.error('No command %r' % name))
+ p.addsubparser(cmd, getattr(obj, cmd))
+ p.missing = getattr(
+ obj, '__missing__', lambda name: p.error('No command %r' % name))
p.func = lambda : None
p.argspec = getfullargspec(p.func)
- p.parselist = _parser_call.__get__(p, p.__class__)
return p
elif inspect.isfunction(obj) or inspect.ismethod(obj): # error if not func
return _parser_from(obj, baseparser)
@@ -182,7 +180,7 @@ def _extract_kwargs(args):
return arglist, kwargs
def _match_cmd(abbrev, commands):
- "Extract the full command name from an abbreviation or raise a NameError"
+ "Extract the command name from an abbreviation or raise a NameError"
perfect_matches = [name for name in commands if name == abbrev]
if len(perfect_matches) == 1:
return perfect_matches[0]
@@ -191,64 +189,74 @@ def _match_cmd(abbrev, commands):
if n == 1:
return matches[0]
elif n > 1:
- raise NameError('Ambiguous command %r: matching %s' % (abbrev, matches))
+ raise NameError(
+ 'Ambiguous command %r: matching %s' % (abbrev, matches))
-def extract_subparser(p, name_parser_map, arglist):
- "Extract the subparser from the first recognized argument"
- prefix = p.prefix_chars[0]
- for i, arg in enumerate(arglist):
- if not arg.startswith(prefix):
- cmd = _match_cmd(arg, name_parser_map)
- del arglist[i]
- return name_parser_map.get(cmd), arg
- return None, ''
-
-def stringlist(result):
- if hasattr(result, '__iter__') and not isinstance(result, str):
- return [str(x) for x in result]
- else:
- return [str(result)]
-
-def _parser_call(p, arglist):
+class ArgumentParser(argparse.ArgumentParser):
"""
- Given a parser, calls its underlying callable with the arglist.
- Works also for multiparsers by dispatching to the underlying parser.
+ An ArgumentParser with .func and .argspec attributes, and possibly
+ .commands and .subparsers.
"""
- if hasattr(p, 'subparsers'):
- subp, cmd = extract_subparser(p, p.subparsers._name_parser_map, arglist)
- if subp is not None:
- p = subp
- elif subp is None and cmd:
- return stringlist(p.missing(cmd))
- if p.argspec.varkw:
- arglist, kwargs = _extract_kwargs(arglist)
+ def consume(self, args, ignore_extra=False):
+ """Call the underlying function with the args. Works also for
+ command containers, by dispatching to the right subparser."""
+ arglist = list(args)
+ if hasattr(self, 'subparsers'):
+ subp, cmd = self._extract_subparser_cmd(arglist)
+ if subp is None and cmd is not None:
+ return self.missing(cmd)
+ elif subp is not None: # use the subparser
+ self = subp
+ if self.argspec.varkw:
+ arglist, kwargs = _extract_kwargs(arglist)
+ else:
+ kwargs = {}
+ if ignore_extra: # ignore unrecognized arguments
+ ns, self.extra_args = self.parse_known_args(arglist)
+ else:
+ ns = self.parse_args(arglist)
+ args = [getattr(ns, a) for a in self.argspec.args]
+ varargs = getattr(ns, self.argspec.varargs or '', [])
+ collision = set(self.argspec.args) & set(kwargs)
+ if collision:
+ self.error('colliding keyword arguments: %s' % ' '.join(collision))
+ return self.func(*(args + varargs), **kwargs)
+
+ def _extract_subparser_cmd(self, arglist):
+ "Extract the subparser from the first recognized argument"
+ prefix = self.prefix_chars[0]
+ name_parser_map = self.subparsers._name_parser_map
+ for i, arg in enumerate(arglist):
+ if not arg.startswith(prefix):
+ cmd = _match_cmd(arg, name_parser_map)
+ del arglist[i]
+ return name_parser_map.get(cmd), arg
+ return None, None
+
+ def addsubparser(self, cmd, func):
+ "Add a subparser for a command"
+ if not hasattr(self, 'subparsers'):
+ self.subparsers = self.add_subparsers(
+ title='subcommands', help='-h to get additional help')
+ subp = self.subparsers.add_parser(cmd, **pconf(func))
+ return parser_from(func, subp)
+
+def listify(result):
+ "If result is an iterable, convert it into a list, else return it unchanged"
+ if hasattr(result, '__iter__') and not isinstance(result, str):
+ return list(result)
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 stringlist( p.func(*(args + varargs), **kwargs))
+ return result
-def call(obj, arglist=sys.argv[1:]):
+def call(obj, arglist=sys.argv[1:], ignore_extra=False):
"""
- 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.
+ If obj is a function or a bound method, parse the given arglist
+ by using the argument parser inferred from the annotations of obj
+ and call obj with the parsed arguments.
+ If obj is an object with attribute .commands, dispatch to the
+ associated subparser. Returns a list or an atomic object.
+ If ignore_extra is True, unrecognized arguments are stored
+ in the attribute .extra_args of the associated parser for
+ later processing.
"""
- enter = getattr(obj, '__enter__', lambda : None)
- exit = getattr(obj, '__exit__', lambda et, ex, tb: None)
- enter()
- try:
- output = parser_from(obj).parselist(arglist)
- except:
- exit(*sys.exc_info())
- raise
- else:
- exit(None, None, None)
- return output
+ return listify(parser_from(obj).consume(arglist, ignore_extra))
diff --git a/plac/plac_ext.py b/plac/plac_ext.py
index 9f8b60f..54fc851 100644
--- a/plac/plac_ext.py
+++ b/plac/plac_ext.py
@@ -11,7 +11,8 @@ def cmd_interface(obj):
dic = dict(preloop=lambda self: i.__enter__(),
postloop=lambda self: i.__exit__(),
do_EOF=lambda self, line: True,
- default=default)
+ default=default,
+ intro=getattr(i, 'intro', None))
for command in obj.commands:
method = getattr(obj, command)
def do_func(self, line, command=command):
@@ -89,16 +90,19 @@ class Interpreter(object):
line = yield output
arglist = shlex.split(line, self.commentchar)
try:
- lines = self.p.parselist(arglist)
+ lines = plac_core.call(self.obj, arglist)
except:
output = Output(None, *sys.exc_info())
else:
- output = Output(os.linesep.join(lines), None, None, None)
- except:
+ if not hasattr(lines, '__iter__'):
+ raise TypeError('Expected a sequence, got %r' % lines)
+ s = os.linesep.join(map(str, lines))
+ output = Output(s, None, None, None)
+ except GeneratorExit: # regular exit
+ exit(None, None, None)
+ except: # exceptional exit
exit(*sys.exc_info())
raise
- else:
- exit(None, None, None)
def check(self, given_input, expected_output, lineno=None):
"Make sure you get the expected_output from the given_input"
@@ -150,13 +154,13 @@ class Interpreter(object):
out.write('%s\n' % output.str)
out.flush()
- def interact(self, prompt='i> ', intro=None, verbose=False):
+ def interact(self, prompt='i> ', verbose=False):
"""Starts an interactive command loop reading commands from the
consolle. Using rlwrap is recommended."""
- if intro is None:
+ try:
+ print(self.obj.intro)
+ except AttributeError: # no intro
self.p.print_usage()
- else:
- print(intro)
with self:
while True:
try: