summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-06-18 07:35:04 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-06-18 07:35:04 +0200
commit00b953f00515c154dc982cd572f96eedd57558bf (patch)
tree0322d15231bd6baa8837f9c201b934a9b3f0e1da
parentd3675126815e3f134f97357c7eae245097ee2a79 (diff)
downloadmicheles-00b953f00515c154dc982cd572f96eedd57558bf.tar.gz
Gigantic commit with the improvements of plac 0.5
-rw-r--r--plac/CHANGES.txt7
-rw-r--r--plac/README.txt19
-rw-r--r--plac/doc/ishelve.batch7
-rw-r--r--plac/doc/ishelve.help18
-rw-r--r--plac/doc/ishelve.placet13
-rw-r--r--plac/doc/ishelve.py55
-rw-r--r--plac/doc/plac.html201
-rw-r--r--plac/doc/plac.pdf1994
-rw-r--r--plac/doc/plac.txt100
-rw-r--r--plac/doc/plac_adv.html985
-rw-r--r--plac/doc/plac_adv.pdf3588
-rw-r--r--plac/doc/plac_adv.txt750
-rw-r--r--plac/doc/read_stdin.py13
-rw-r--r--plac/doc/shelve_interpreter.help12
-rw-r--r--plac/doc/shelve_interpreter.py18
-rw-r--r--plac/doc/test_ishelve.py14
-rw-r--r--plac/doc/test_ishelve2.py11
-rw-r--r--plac/doc/test_plac.py (renamed from plac/test_plac.py)46
-rw-r--r--plac/doc/vcs.help2
-rw-r--r--plac/doc/vcs.py23
-rw-r--r--plac/plac.py24
-rw-r--r--plac/plac_core.py88
-rw-r--r--plac/plac_ext.py233
-rw-r--r--plac/setup.py2
24 files changed, 6173 insertions, 2050 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt
index f511500..255737d 100644
--- a/plac/CHANGES.txt
+++ b/plac/CHANGES.txt
@@ -1,9 +1,10 @@
HISTORY
----------
-0.5.0 Introduced smart options and removed the default formatter class.
- Made the split plac_code and plac_ext and added an Interpreter class
- (2010-06-XX).
+0.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.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/README.txt b/plac/README.txt
index 4cbcab6..687e986 100644
--- a/plac/README.txt
+++ b/plac/README.txt
@@ -1,5 +1,5 @@
-plac, the easiest command line arguments parser in the world
-============================================================
+plac, the smartest command line arguments parser in the world
+=============================================================
:Author: Michele Simionato
:E-mail: michele.simionato@gmail.com
@@ -40,24 +40,31 @@ Run
::
- $ python test_plac.py
+ $ python doc/test_plac.py
or
::
- $ nosetests test_plac.py
+ $ nosetests doc
or
::
- $ py.test test_plac.py
+ $ py.test doc
Documentation
--------------
-You can choose between the `HTML version`_ and the `PDF version`_ .
+You can choose between the `HTML version`_ and the `PDF version`_:
.. _HTML version: http://micheles.googlecode.com/hg/plac/doc/plac.html
.. _PDF version: http://micheles.googlecode.com/hg/plac/doc/plac.pdf
+
+There is also an additional documentation for advanced usages of plac,
+such as using plac_ for testing/scripting an application and to
+write domain specific languages (DSL):
+
+.. _HTML version: http://micheles.googlecode.com/hg/plac/doc/plac_adv.html
+.. _PDF version: http://micheles.googlecode.com/hg/plac/doc/plac_adv.pdf
diff --git a/plac/doc/ishelve.batch b/plac/doc/ishelve.batch
new file mode 100644
index 0000000..ef378b3
--- /dev/null
+++ b/plac/doc/ishelve.batch
@@ -0,0 +1,7 @@
+#!ishelve.py
+.clear
+a=1 b=2
+.show
+.del a
+.dl b
+.show
diff --git a/plac/doc/ishelve.help b/plac/doc/ishelve.help
new file mode 100644
index 0000000..d68a099
--- /dev/null
+++ b/plac/doc/ishelve.help
@@ -0,0 +1,18 @@
+usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
+ [.filename /home/micheles/conf.shelve]
+ [params [params ...]] [setters [setters ...]]
+
+Simple interface to a shelve
+
+positional arguments:
+ params names of the parameters in the shelve
+ setters setters param=value
+
+optional arguments:
+ .help show help
+ .showall show all parameters in the shelve
+ .clear clear the shelve
+ .delete DELETE delete an element
+ .filename /home/micheles/conf.shelve
+ filename of the shelve
+
diff --git a/plac/doc/ishelve.placet b/plac/doc/ishelve.placet
new file mode 100644
index 0000000..87c1b32
--- /dev/null
+++ b/plac/doc/ishelve.placet
@@ -0,0 +1,13 @@
+#!ishelve.py
+i> .clear # start from a clean state
+cleared the shelve
+i> a=1
+setting a=1
+i> a
+1
+i> .del a
+deleted a
+i> a
+a: not found
+i> .cler # spelling error
+SystemExit: unrecognized arguments: .cler
diff --git a/plac/doc/ishelve.py b/plac/doc/ishelve.py
new file mode 100644
index 0000000..812b7ce
--- /dev/null
+++ b/plac/doc/ishelve.py
@@ -0,0 +1,55 @@
+# ishelve.py
+import os, shelve
+import plac
+
+DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
+
+@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):
+ "A simple interface to a shelve"
+ 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 '<param> ...'
+ yield '<param=value> ...'
+ 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)
diff --git a/plac/doc/plac.html b/plac/doc/plac.html
index 6339cc3..87e8840 100644
--- a/plac/doc/plac.html
+++ b/plac/doc/plac.html
@@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" />
+<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" />
<style type="text/css">
@@ -428,7 +428,7 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
</tr>
<tr class="field"><th class="docinfo-name">Project page:</th><td class="field-body"><a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">http://micheles.googlecode.com/hg/plac/doc/plac.html</a></td>
</tr>
-<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal"><span class="pre">easy_install</span> <span class="pre">-U</span> <span class="pre">plac</span></tt></td>
+<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>
@@ -521,7 +521,7 @@ if __name__ == '__main__':
sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:]))
</pre>
-<p>As you see the whole <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__'</span></tt> block (nine lines)
+<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
the language should recognize the main function and pass to it the
command line arguments automatically; unfortunaly this is unlikely to
@@ -610,12 +610,12 @@ if __name__ == '__main__':
</pre>
<p>Here I want to perform a query on a database table, by extracting the
-today's data: it makes sense for <tt class="docutils literal"><span class="pre">today</span></tt> to be a default argument.
-If there is a most used table (in this example a table called <tt class="docutils literal"><span class="pre">'product'</span></tt>)
+today's data: it makes sense for <tt class="docutils literal">today</tt> to be a default argument.
+If there is a most used table (in this example a table called <tt class="docutils literal">'product'</tt>)
it also makes sense to make it a default argument. Performing the parsing
of the command lines arguments by hand takes 8 ugly lines of boilerplate
(using <a class="reference external" href="http://argparse.googlecode.com">argparse</a> would require about the same number of lines).
-With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal"><span class="pre">__main__</span></tt> block reduces to the usual two lines:</p>
+With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal">__main__</tt> block reduces to the usual two lines:</p>
<pre class="literal-block">
if __name__ == '__main__':
import plac; plac.call(main)
@@ -674,7 +674,7 @@ let's the machine take care of the details.</p>
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, in the sense that it
delivers the programmer from the burden of writing the parser, but is
less of a hack: instead of extracting the parser from the docstring of
-the module, it extracts it from the signature of the <tt class="docutils literal"><span class="pre">main</span></tt>
+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
@@ -693,7 +693,7 @@ if __name__ == '__main__':
import plac; plac.call(main)
</pre>
-<p>Here the arguments of the <tt class="docutils literal"><span class="pre">main</span></tt> function have been annotated with
+<p>Here the arguments of the <tt class="docutils literal">main</tt> function have been annotated with
strings which are intented to be used in the help message:</p>
<pre class="literal-block">
usage: example7_.py [-h] dsn [scripts [scripts ...]]
@@ -735,10 +735,10 @@ if __name__ == '__main__':
import plac; plac.call(main)
</pre>
-<p>Here the argument <tt class="docutils literal"><span class="pre">command</span></tt> has been annotated with the tuple
-<tt class="docutils literal"><span class="pre">(&quot;SQL</span> <span class="pre">query&quot;,</span> <span class="pre">'option',</span> <span class="pre">'c')</span></tt>: the first string is the help string
+<p>Here the argument <tt class="docutils literal">command</tt> has been annotated with the tuple
+<tt class="docutils literal">(&quot;SQL query&quot;, 'option', 'c')</tt>: the first string is the help string
which will appear in the usage message, the second string tells <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-that <tt class="docutils literal"><span class="pre">command</span></tt> is an option and the third string that there is also
+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>.
The usage message is the following:</p>
<pre class="literal-block">
@@ -762,7 +762,7 @@ $ python3 example8.py --command=&quot;select * from table&quot; dsn
executing select * from table on dsn
</pre>
<p>The third argument in the function annotation can be omitted: in such
-case it will be assumed to be <tt class="docutils literal"><span class="pre">None</span></tt>. The consequence is that
+case it will be assumed to be <tt class="docutils literal">None</tt>. The consequence is that
the usual dichotomy between long and short options (GNU-style options)
disappears: we get <em>smart options</em>, which have the single character prefix
of short options and behave like both long and short options, since
@@ -803,8 +803,8 @@ $ python3 example6.py -com=&quot;select&quot; dsn
usage: example6.py [-h] [-command COMMAND] dsn
example6.py: error: unrecognized arguments: -com=select
</pre>
-<p>If the option is not passed, the variable <tt class="docutils literal"><span class="pre">command</span></tt>
-will get the value <tt class="docutils literal"><span class="pre">None</span></tt>. However, it is possible to specify a non-trivial
+<p>If the option is not passed, the variable <tt class="docutils literal">command</tt>
+will get the value <tt class="docutils literal">None</tt>. However, it is possible to specify a non-trivial
default. Here is an example:</p>
<pre class="literal-block">
# example8_.py
@@ -838,7 +838,7 @@ executing 'select * from table' on dsn
<div class="section" id="scripts-with-flags">
<h1><a class="toc-backref" href="#id5">Scripts with flags</a></h1>
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize flags, i.e. boolean options which are
-<tt class="docutils literal"><span class="pre">True</span></tt> if they are passed to the command line and <tt class="docutils literal"><span class="pre">False</span></tt>
+<tt class="docutils literal">True</tt> if they are passed to the command line and <tt class="docutils literal">False</tt>
if they are absent. Here is an example:</p>
<pre class="literal-block">
# example9.py
@@ -868,16 +868,21 @@ $ python3 example9.py -v dsn
connecting to dsn
</pre>
<p>Notice that it is an error trying to specify a default for flags: the
-default value for a flag is always <tt class="docutils literal"><span class="pre">False</span></tt>. If you feel the need to
+default value for a flag is always <tt class="docutils literal">False</tt>. If you feel the need to
implement non-boolean flags, you should use an option with two
choices, as explained in the &quot;more features&quot; section.</p>
<p>For consistency with the way the usage message is printed, I suggest
you to follow the Flag-Option-Required-Default (FORD) convention: in
-the <tt class="docutils literal"><span class="pre">main</span></tt> function write first the flag arguments, then the option
+the <tt class="docutils literal">main</tt> function write first the flag arguments, then the option
arguments, then the required arguments and finally the default
arguments. This is just a convention and you are not forced to use it,
except for the default arguments (including the varargs) which must
stay at the end as it is required by the Python syntax.</p>
+<p>I also suggests to specify a one-character abbreviation for flags: in
+this way you can use the GNU-style composition of flags (i.e. <tt class="docutils literal"><span class="pre">-zxvf</span></tt>
+is an abbreviation of <tt class="docutils literal"><span class="pre">-z</span> <span class="pre">-x</span> <span class="pre">-v</span> <span class="pre">-f</span></tt>). I usually do not provide
+the one-character abbreviation for options, since it does not make sense
+to compose them.</p>
</div>
<div class="section" id="plac-for-python-2-x-users">
<h1><a class="toc-backref" href="#id6">plac for Python 2.X users</a></h1>
@@ -903,7 +908,7 @@ main.__annotations__ = dict(
<p>One should be careful to match the keys of the annotation dictionary
with the names of the arguments in the annotated function; for lazy
people with Python 2.4 available the simplest way is to use the
-<tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator that performs the check for you:</p>
+<tt class="docutils literal">plac.annotations</tt> decorator that performs the check for you:</p>
<pre class="literal-block">
&#64;plac.annotations(
dsn=&quot;Database dsn&quot;,
@@ -912,7 +917,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"><span class="pre">plac.annotations</span></tt> decorator. Notice however
+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>
</div>
<div class="section" id="more-features">
@@ -924,18 +929,18 @@ the features of <a class="reference external" href="http://argparse.googlecode.c
in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Until now, I have only showed simple annotations, but in
general an annotation is a 6-tuple of the form</p>
<blockquote>
-<tt class="docutils literal"><span class="pre">(help,</span> <span class="pre">kind,</span> <span class="pre">abbrev,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar)</span></tt></blockquote>
-<p>where <tt class="docutils literal"><span class="pre">help</span></tt> is the help message, <tt class="docutils literal"><span class="pre">kind</span></tt> is a string in the set {
-<tt class="docutils literal"><span class="pre">&quot;flag&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;option&quot;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;positional&quot;</span></tt>}, <tt class="docutils literal"><span class="pre">abbrev</span></tt> is a
-one-character string, <tt class="docutils literal"><span class="pre">type</span></tt> is a callable taking a string in input,
-<tt class="docutils literal"><span class="pre">choices</span></tt> is a discrete sequence of values and <tt class="docutils literal"><span class="pre">metavar</span></tt> is a string.</p>
-<p><tt class="docutils literal"><span class="pre">type</span></tt> is used to automagically convert the command line arguments
+<tt class="docutils literal">(help, kind, abbrev, type, choices, metavar)</tt></blockquote>
+<p>where <tt class="docutils literal">help</tt> is the help message, <tt class="docutils literal">kind</tt> is a string in the set {
+<tt class="docutils literal">&quot;flag&quot;</tt>, <tt class="docutils literal">&quot;option&quot;</tt>, <tt class="docutils literal">&quot;positional&quot;</tt>}, <tt class="docutils literal">abbrev</tt> is a
+one-character string, <tt class="docutils literal">type</tt> is a callable taking a string in input,
+<tt class="docutils literal">choices</tt> is a discrete sequence of values and <tt class="docutils literal">metavar</tt> is a string.</p>
+<p><tt class="docutils literal">type</tt> is used to automagically convert the command line arguments
from the string type to any Python type; by default there is no
-conversion and <tt class="docutils literal"><span class="pre">type=None</span></tt>.</p>
-<p><tt class="docutils literal"><span class="pre">choices</span></tt> is used to restrict the number of the valid
-options; by default there is no restriction i.e. <tt class="docutils literal"><span class="pre">choices=None</span></tt>.</p>
-<p><tt class="docutils literal"><span class="pre">metavar</span></tt> is used to change the argument name in the usage message
-(and only there); by default the metavar is <tt class="docutils literal"><span class="pre">None</span></tt>: this means that
+conversion and <tt class="docutils literal">type=None</tt>.</p>
+<p><tt class="docutils literal">choices</tt> is used to restrict the number of the valid
+options; by default there is no restriction i.e. <tt class="docutils literal">choices=None</tt>.</p>
+<p><tt class="docutils literal">metavar</tt> is used to change the argument name in the usage message
+(and only there); by default the metavar is <tt class="docutils literal">None</tt>: this means that
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>
@@ -960,13 +965,13 @@ if __name__ == '__main__':
print(plac.call(main)[0])
</pre>
-<p>Usually the main function of a script works by side effects and returns
-<tt class="docutils literal"><span class="pre">None</span></tt>; in this example instead I choose to return the number and to
-print it in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block.</p>
+<p>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"><span class="pre">None</span></tt>.</p>
+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 ...]]
@@ -981,7 +986,7 @@ optional arguments:
-h, --help show this help message and exit
</pre>
-<p>Notice that the docstring of the <tt class="docutils literal"><span class="pre">main</span></tt> function has been automatically added
+<p>Notice that the docstring of the <tt class="docutils literal">main</tt> function has been automatically added
to the usage message. Here are a couple of examples of use:</p>
<pre class="literal-block">
$ python example10.py add 1 2 3 4
@@ -993,8 +998,8 @@ 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"><span class="pre">plac.call</span></tt> are stringified by invoking
-<tt class="docutils literal"><span class="pre">str</span></tt> on each of them.
+the 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.
@@ -1004,7 +1009,7 @@ For instance a doctest may look like this:</p>
&gt;&gt;&gt; plac.call(example10.main, ['add', '1', '2'])
['3.0']
</pre>
-<p><tt class="docutils literal"><span class="pre">plac.call</span></tt> works for generators too:</p>
+<p><tt class="docutils literal">plac.call</tt> works for generators too:</p>
<pre class="doctest-block">
&gt;&gt;&gt; def main(n):
... for i in range(int(n)):
@@ -1012,18 +1017,18 @@ For instance a doctest may look like this:</p>
&gt;&gt;&gt; plac.call(main, ['3'])
['0', '1', '2']
</pre>
-<p>However, you should notice that <tt class="docutils literal"><span class="pre">plac.call</span></tt> is <em>eager</em>, not lazy:
+<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"><span class="pre">list(result)</span></tt> to the result of a <tt class="docutils literal"><span class="pre">plac.call</span></tt>.</p>
+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"><span class="pre">print</span></tt> with <tt class="docutils literal"><span class="pre">yield</span></tt> in
+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>
</div>
<div class="section" id="a-realistic-example">
<h1><a class="toc-backref" href="#id8">A realistic example</a></h1>
<p>Here is a more realistic script using most of the features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to
run SQL queries on a database by relying on <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a>. Notice the usage
-of the <tt class="docutils literal"><span class="pre">type</span></tt> feature to automagically convert a SQLAlchemy connection
+of the <tt class="docutils literal">type</tt> feature to automagically convert a SQLAlchemy connection
string into a <a class="reference external" href="http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html">SqlSoup</a> object:</p>
<pre class="literal-block">
# dbcli.py
@@ -1058,11 +1063,11 @@ if __name__ == '__main__':
</pre>
<p>You can see the <em>yield-is-print</em> pattern here: instead of using
-<tt class="docutils literal"><span class="pre">print</span></tt> in the main function, we use <tt class="docutils literal"><span class="pre">yield</span></tt>, and we perform the
-print in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block. The advantage of the pattern is that
+<tt class="docutils literal">print</tt> in the main function, we use <tt class="docutils literal">yield</tt>, and we 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"><span class="pre">sys.stdout</span></tt> to a
-<tt class="docutils literal"><span class="pre">StringIO</span></tt> object and we know that redirecting <tt class="docutils literal"><span class="pre">sys.stdout</span></tt> is
+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>
<p>Here is the usage message:</p>
<pre class="literal-block">
@@ -1088,7 +1093,7 @@ example.</p>
<h1><a class="toc-backref" href="#id9">Keyword arguments</a></h1>
<p>Starting from release 0.4, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports keyword arguments. In
practice that means that if your main function has keyword arguments,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> treats specially arguments of the form <tt class="docutils literal"><span class="pre">&quot;name=value&quot;</span></tt> in the
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> treats specially arguments of the form <tt class="docutils literal">&quot;name=value&quot;</tt> in the
command line. Here is an example:</p>
<pre class="literal-block">
# example12.py
@@ -1132,7 +1137,7 @@ args=('a1', 'a2')
kw={'name': 'value'}
</pre>
<p>When using keyword arguments, one must be careful to use names which
-are not alreay taken; for instance in this examples the name <tt class="docutils literal"><span class="pre">opt</span></tt>
+are not alreay taken; for instance in this examples the name <tt class="docutils literal">opt</tt>
is taken:</p>
<pre class="literal-block">
$ python example12.py 1 2 kw1=1 kw2=2 opt=0
@@ -1154,7 +1159,7 @@ following assumes knowledge of <a class="reference external" href="http://argpar
<li>plac does not support the destination concept: the destination
coincides with the name of the argument, always. This restriction
has some drawbacks. For instance, suppose you want to define a long
-option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal"><span class="pre">yield</span></tt>,
+option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal">yield</tt>,
which is a Python keyword, and since you cannot introduce an
argument with that name in a function definition, it is impossible
to implement it. Your choices are to change the name of the long
@@ -1164,16 +1169,16 @@ documentation puts it: <em>Required options are generally considered bad
form - normal users expect options to be optional. You should avoid
the use of required options whenever possible.</em></li>
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to
-define generalized two-value flags with values different from <tt class="docutils literal"><span class="pre">True</span></tt>
-and <tt class="docutils literal"><span class="pre">False</span></tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but
+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
since you can use options with two choices instead, and in any case
-the conversion from <tt class="docutils literal"><span class="pre">{True,</span> <span class="pre">False}</span></tt> to any couple of values
+the conversion from <tt class="docutils literal">{True, False}</tt> to any couple of values
can be trivially implemented with a ternary operator
-(<tt class="docutils literal"><span class="pre">value1</span> <span class="pre">if</span> <span class="pre">flag</span> <span class="pre">else</span> <span class="pre">value2</span></tt>), I have removed it (KISS rules!).</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal"><span class="pre">nargs</span></tt> options directly (it uses them internally,
+(<tt class="docutils literal">value1 if flag else value2</tt>), I have removed it (KISS rules!).</li>
+<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal">nargs</tt> options directly (it uses them internally,
though, to implement flag recognition). The reason it that all the use
cases of interest to me are covered by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and did not feel the need
-to increase the learning curve by adding direct support for <tt class="docutils literal"><span class="pre">nargs</span></tt>.</li>
+to increase the learning curve by adding direct support for <tt class="docutils literal">nargs</tt>.</li>
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does support subparsers, but you must read the <a class="reference external" href="in-writing">advanced usage
document</a> to see how it works.</li>
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support actions directly. This also
@@ -1181,12 +1186,67 @@ looks like a feature too advanced for the goals of <a class="reference external"
that the ability to define your own annotation objects (again, see
the <a class="reference external" href="in-writing">advanced usage document</a>) may mitigate the need for custom actions.</li>
</ul>
-<p>I should stress that if you want to access all of the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features
-from <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you can use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> and you will get
-the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object. The the full power of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>
-is then available to you: you can use <tt class="docutils literal"><span class="pre">add_argument</span></tt>, <tt class="docutils literal"><span class="pre">add_subparsers()</span></tt>,
-etc. In other words, while some features are not supported directly,
-<em>all</em> features are supported indirectly.</p>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can leverage directly on many <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features.</p>
+<p>For instance, you can make invisible an argument in the usage message
+simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or
+<tt class="docutils literal">argparse.SUPPRESS</tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a>
+directly.</p>
+<p>It is also possible to pass options to the underlying
+<tt class="docutils literal">argparse.ArgumentParser</tt> object (currently it accepts the default
+arguments <tt class="docutils literal">description</tt>, <tt class="docutils literal">epilog</tt>, <tt class="docutils literal">prog</tt>, <tt class="docutils literal">usage</tt>,
+<tt class="docutils literal">add_help</tt>, <tt class="docutils literal">argument_default</tt>, <tt class="docutils literal">parents</tt>, <tt class="docutils literal">prefix_chars</tt>,
+<tt class="docutils literal">fromfile_prefix_chars</tt>, <tt class="docutils literal">conflict_handler</tt>, <tt class="docutils literal">formatter_class</tt>).
+It is enough to set such attributes on the <tt class="docutils literal">main</tt> function. For
+instance</p>
+<pre class="literal-block">
+def main(...):
+ pass
+
+main.add_help = False
+</pre>
+<p>disables the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This
+mechanism does not look particularly elegant, but it works well
+enough. I assume that the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be happy with
+the defaults and would not want to change them; still it is possible
+if she wants to.</p>
+<p>For instance, by setting the <tt class="docutils literal">description</tt> attribute, it is possible
+to add a comment to the usage message (by default the docstring of the
+<tt class="docutils literal">main</tt> function is used as description).</p>
+<p>It is also possible to change the option prefix; for
+instance if your script must run under Windows and you want to use &quot;/&quot;
+as option prefix you can add the line:</p>
+<pre class="literal-block">
+main.prefix_chars='/-'
+</pre>
+<p>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>
+<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
+invoking the <tt class="docutils literal">plac.parser_from</tt> utility function:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import plac
+&gt;&gt;&gt; def main(arg):
+... pass
+...
+&gt;&gt;&gt; print plac.parser_from(main)
+ArgumentParser(prog='', usage=None, description=None, version=None,
+formatter_class=&lt;class 'argparse.HelpFormatter'&gt;, conflict_handler='error',
+add_help=True)
+</pre>
+<p>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
+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>
+of the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> directly without calling the main function.</p>
+<p>Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> to
+understand the meaning of the other options. If there is a set of
+options that you use very often, you may consider writing a decorator
+adding such options to the <tt class="docutils literal">main</tt> function for you. For simplicity,
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic except the addition of the <tt class="docutils literal">.p</tt>
+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>
@@ -1209,19 +1269,15 @@ easier than <a class="reference external" href="http://pypi.python.org/pypi/plac
</div>
<div class="section" id="the-future">
<h1><a class="toc-backref" href="#id12">The future</a></h1>
-<p>Currently <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is around 170 lines of code, not counting blanks,
+<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: after all I wrote the first version of it in
-three days, including the tests, the documentation and the time to
-learn <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. At the moment I have already written some powerful
-extensions for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and I plan to release them in the 0.5 release, once
-I finish writing <a class="reference external" href="in-writing">advanced usage document</a> .
-The extensions will be released together with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> but in a separated
-module.</p>
+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>
</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>
@@ -1240,8 +1296,8 @@ of functions annotations in Python 3.</li>
</ol>
<p>Putting together these two observations with the original idea of inferring the
parser I decided to build an <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object from function
-annotations. The <tt class="docutils literal"><span class="pre">optionparser</span></tt> name was ruled out, since I was
-now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal"><span class="pre">argparse_plus</span></tt> was also ruled out,
+annotations. The <tt class="docutils literal">optionparser</tt> name was ruled out, since I was
+now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal">argparse_plus</tt> was also ruled out,
since the typical usage was completely different from the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> usage.</p>
<p>I made a research on PyPI and the name <em>clap</em> (Command Line Arguments Parser)
was not taken, so I renamed everything to clap. After two days
@@ -1249,6 +1305,9 @@ 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 d002352..3fbb344 100644
--- a/plac/doc/plac.pdf
+++ b/plac/doc/plac.pdf
@@ -279,7 +279,7 @@ endobj
/Dest [ 78 0 R
/XYZ
62.69291
- 443.4236
+ 401.4236
0 ]
/Rect [ 62.69291
473.5936
@@ -297,7 +297,7 @@ endobj
/Dest [ 78 0 R
/XYZ
62.69291
- 443.4236
+ 401.4236
0 ]
/Rect [ 527.0227
473.5936
@@ -315,7 +315,7 @@ endobj
/Dest [ 86 0 R
/XYZ
62.69291
- 695.8236
+ 647.8236
0 ]
/Rect [ 62.69291
455.5936
@@ -333,7 +333,7 @@ endobj
/Dest [ 86 0 R
/XYZ
62.69291
- 695.8236
+ 647.8236
0 ]
/Rect [ 527.0227
455.5936
@@ -351,7 +351,7 @@ endobj
/Dest [ 90 0 R
/XYZ
62.69291
- 254.0849
+ 218.0849
0 ]
/Rect [ 62.69291
437.5936
@@ -369,7 +369,7 @@ endobj
/Dest [ 90 0 R
/XYZ
62.69291
- 254.0849
+ 218.0849
0 ]
/Rect [ 521.4627
437.5936
@@ -384,10 +384,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 93 0 R
+ /Dest [ 94 0 R
/XYZ
62.69291
- 180.6236
+ 765.0236
0 ]
/Rect [ 62.69291
419.5936
@@ -402,10 +402,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 93 0 R
+ /Dest [ 94 0 R
/XYZ
62.69291
- 180.6236
+ 765.0236
0 ]
/Rect [ 521.4627
419.5936
@@ -420,10 +420,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 97 0 R
+ /Dest [ 116 0 R
/XYZ
62.69291
- 152.2236
+ 765.0236
0 ]
/Rect [ 62.69291
401.5936
@@ -438,10 +438,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 97 0 R
+ /Dest [ 116 0 R
/XYZ
62.69291
- 152.2236
+ 765.0236
0 ]
/Rect [ 521.4627
401.5936
@@ -456,10 +456,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 126 0 R
+ /Dest [ 138 0 R
/XYZ
62.69291
- 393.0236
+ 420.6236
0 ]
/Rect [ 62.69291
383.5936
@@ -474,10 +474,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 126 0 R
+ /Dest [ 138 0 R
/XYZ
62.69291
- 393.0236
+ 420.6236
0 ]
/Rect [ 521.4627
383.5936
@@ -492,10 +492,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 146 0 R
+ /Dest [ 138 0 R
/XYZ
62.69291
- 765.0236
+ 231.6236
0 ]
/Rect [ 62.69291
365.5936
@@ -510,10 +510,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 146 0 R
+ /Dest [ 138 0 R
/XYZ
62.69291
- 765.0236
+ 231.6236
0 ]
/Rect [ 521.4627
365.5936
@@ -528,10 +528,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 146 0 R
+ /Dest [ 152 0 R
/XYZ
62.69291
- 630.0236
+ 765.0236
0 ]
/Rect [ 62.69291
347.5936
@@ -546,10 +546,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 146 0 R
+ /Dest [ 152 0 R
/XYZ
62.69291
- 630.0236
+ 765.0236
0 ]
/Rect [ 521.4627
347.5936
@@ -766,12 +766,12 @@ endobj
43 0 R
44 0 R
45 0 R ]
- /Contents 164 0 R
+ /Contents 170 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -890,12 +890,12 @@ endobj
51 0 R
52 0 R
53 0 R ]
- /Contents 165 0 R
+ /Contents 171 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -989,12 +989,12 @@ endobj
57 0 R
58 0 R
59 0 R ]
- /Contents 166 0 R
+ /Contents 172 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1056,12 +1056,12 @@ endobj
<< /Annots [ 61 0 R
62 0 R
63 0 R ]
- /Contents 167 0 R
+ /Contents 173 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1187,12 +1187,12 @@ endobj
69 0 R
70 0 R
71 0 R ]
- /Contents 168 0 R
+ /Contents 174 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1222,12 +1222,12 @@ endobj
74 0 obj
% Page dictionary
<< /Annots [ 73 0 R ]
- /Contents 169 0 R
+ /Contents 175 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1257,12 +1257,12 @@ endobj
76 0 obj
% Page dictionary
<< /Annots [ 75 0 R ]
- /Contents 170 0 R
+ /Contents 176 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1282,9 +1282,9 @@ endobj
0
0 ]
/Rect [ 110.2829
- 383.9936
+ 341.9936
132.8629
- 395.9936 ]
+ 353.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1292,12 +1292,12 @@ endobj
78 0 obj
% Page dictionary
<< /Annots [ 77 0 R ]
- /Contents 171 0 R
+ /Contents 177 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1317,9 +1317,9 @@ endobj
0
0 ]
/Rect [ 303.6429
- 711.3936
+ 663.3936
324.7629
- 723.3936 ]
+ 675.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1332,9 +1332,9 @@ endobj
0
0 ]
/Rect [ 157.3904
- 660.3936
+ 612.3936
179.9938
- 672.3936 ]
+ 624.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1347,9 +1347,9 @@ endobj
0
0 ]
/Rect [ 184.0634
- 648.3936
+ 600.3936
223.5234
- 660.3936 ]
+ 612.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1362,9 +1362,9 @@ endobj
0
0 ]
/Rect [ 62.69291
- 636.3936
+ 588.3936
102.1529
- 648.3936 ]
+ 600.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1377,9 +1377,9 @@ endobj
0
0 ]
/Rect [ 192.7997
- 636.3936
+ 588.3936
237.9391
- 648.3936 ]
+ 600.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1392,9 +1392,9 @@ endobj
0
0 ]
/Rect [ 324.4372
- 636.3936
+ 588.3936
342.7772
- 648.3936 ]
+ 600.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1407,9 +1407,9 @@ endobj
0
0 ]
/Rect [ 355.0429
- 444.3936
+ 396.3936
397.2829
- 456.3936 ]
+ 408.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1423,12 +1423,12 @@ endobj
83 0 R
84 0 R
85 0 R ]
- /Contents 172 0 R
+ /Contents 178 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1448,9 +1448,9 @@ endobj
0
0 ]
/Rect [ 338.1568
- 218.6549
+ 182.6549
360.5113
- 230.6549 ]
+ 194.6549 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1463,9 +1463,9 @@ endobj
0
0 ]
/Rect [ 110.6843
- 206.6549
+ 170.6549
169.0343
- 218.6549 ]
+ 182.6549 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1478,9 +1478,9 @@ endobj
0
0 ]
/Rect [ 168.3029
- 194.6549
+ 158.6549
208.8829
- 206.6549 ]
+ 170.6549 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1490,12 +1490,12 @@ endobj
<< /Annots [ 87 0 R
88 0 R
89 0 R ]
- /Contents 173 0 R
+ /Contents 179 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1506,8 +1506,27 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER75': class PDFDictionary
+% 'Page11': class PDFPage
91 0 obj
+% Page dictionary
+<< /Contents 180 0 R
+ /MediaBox [ 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 >>
+endobj
+% 'Annot.NUMBER75': class PDFDictionary
+92 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1515,14 +1534,14 @@ endobj
0
0 ]
/Rect [ 185.0709
- 145.1936
+ 729.5936
208.0228
- 157.1936 ]
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER76': class PDFDictionary
-92 0 obj
+93 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1530,23 +1549,23 @@ endobj
0
0 ]
/Rect [ 220.5998
- 133.1936
+ 717.5936
243.819
- 145.1936 ]
+ 729.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page11': class PDFPage
-93 0 obj
+% 'Page12': class PDFPage
+94 0 obj
% Page dictionary
-<< /Annots [ 91 0 R
- 92 0 R ]
- /Contents 174 0 R
+<< /Annots [ 92 0 R
+ 93 0 R ]
+ /Contents 181 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1558,7 +1577,7 @@ endobj
/Type /Page >>
endobj
% 'Annot.NUMBER77': class PDFDictionary
-94 0 obj
+95 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1566,14 +1585,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 116.7936
+ 729.5936
84.8789
- 128.7936 ]
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER78': class PDFDictionary
-95 0 obj
+96 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1581,14 +1600,14 @@ endobj
0
0 ]
/Rect [ 466.5307
- 116.7936
+ 729.5936
509.8367
- 128.7936 ]
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER79': class PDFDictionary
-96 0 obj
+97 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1596,34 +1615,12 @@ endobj
0
0 ]
/Rect [ 124.3929
- 92.79362
+ 705.5936
163.8529
- 104.7936 ]
+ 717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page12': class PDFPage
-97 0 obj
-% Page dictionary
-<< /Annots [ 94 0 R
- 95 0 R
- 96 0 R ]
- /Contents 175 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 163 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
% 'Annot.NUMBER80': class PDFDictionary
98 0 obj
<< /A << /S /URI
@@ -1633,9 +1630,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 687.5936
+ 624.5936
127.9329
- 699.5936 ]
+ 636.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1648,9 +1645,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 669.5936
+ 606.5936
107.9337
- 681.5936 ]
+ 618.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1663,9 +1660,9 @@ endobj
0
0 ]
/Rect [ 308.5389
- 669.5936
+ 606.5936
351.8997
- 681.5936 ]
+ 618.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1678,9 +1675,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 627.5936
+ 564.5936
108.3529
- 639.5936 ]
+ 576.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1693,9 +1690,9 @@ endobj
0
0 ]
/Rect [ 277.2428
- 627.5936
+ 564.5936
321.0228
- 639.5936 ]
+ 576.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1708,9 +1705,9 @@ endobj
0
0 ]
/Rect [ 404.5839
- 615.5936
+ 552.5936
426.0657
- 627.5936 ]
+ 564.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1723,9 +1720,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 561.5936
+ 498.5936
108.61
- 573.5936 ]
+ 510.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1738,9 +1735,9 @@ endobj
0
0 ]
/Rect [ 459.2622
- 549.5936
+ 486.5936
481.289
- 561.5936 ]
+ 498.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1753,9 +1750,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 519.5936
+ 456.5936
108.9242
- 531.5936 ]
+ 468.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1763,14 +1760,14 @@ endobj
107 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
/Rect [ 340.9248
- 519.5936
+ 456.5936
470.1087
- 531.5936 ]
+ 468.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1783,9 +1780,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 489.5936
+ 426.5936
107.9247
- 501.5936 ]
+ 438.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1798,9 +1795,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 477.5936
+ 414.5936
104.0329
- 489.5936 ]
+ 426.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1808,14 +1805,14 @@ endobj
110 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
/Rect [ 489.2227
- 477.5936
+ 414.5936
532.176
- 489.5936 ]
+ 426.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1823,14 +1820,14 @@ endobj
111 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
/Rect [ 85.69291
- 465.5936
+ 402.5936
159.6229
- 477.5936 ]
+ 414.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1838,14 +1835,14 @@ endobj
112 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 330.9361
- 444.5936
- 376.9555
- 456.5936 ]
+ /Rect [ 62.69291
+ 381.5936
+ 83.81291
+ 393.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1853,14 +1850,14 @@ endobj
113 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 446.2044
- 444.5936
- 471.1038
- 456.5936 ]
+ /Rect [ 219.4229
+ 381.5936
+ 261.6629
+ 393.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1868,14 +1865,14 @@ endobj
114 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
/Border [ 0
0
0 ]
- /Rect [ 321.0443
- 432.5936
- 399.3474
- 444.5936 ]
+ /Rect [ 455.2227
+ 351.5936
+ 534.3667
+ 363.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1883,214 +1880,207 @@ endobj
115 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 420.5936
- 107.3744
- 432.5936 ]
+ /Rect [ 325.7268
+ 184.3936
+ 347.4138
+ 196.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER98': class PDFDictionary
+% 'Page13': class PDFPage
116 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
+ 104 0 R
+ 105 0 R
+ 106 0 R
+ 107 0 R
+ 108 0 R
+ 109 0 R
+ 110 0 R
+ 111 0 R
+ 112 0 R
+ 113 0 R
+ 114 0 R
+ 115 0 R ]
+ /Contents 182 0 R
+ /MediaBox [ 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 >>
+endobj
+% 'Annot.NUMBER98': class PDFDictionary
+117 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
- /Rect [ 109.0098
- 357.5936
- 131.9967
- 369.5936 ]
+ /Rect [ 242.2129
+ 699.3936
+ 365.0629
+ 711.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER99': class PDFDictionary
-117 0 obj
+118 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
/Border [ 0
0
0 ]
- /Rect [ 397.2929
- 333.5936
- 415.6329
- 345.5936 ]
+ /Rect [ 327.2261
+ 681.3936
+ 410.5152
+ 693.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER100': class PDFDictionary
-118 0 obj
+119 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/opterator) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 85.69291
- 312.5936
- 128.4929
- 324.5936 ]
+ /Rect [ 275.5829
+ 490.1936
+ 317.8229
+ 502.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER101': class PDFDictionary
-119 0 obj
+120 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/CLIArgs) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 85.69291
- 294.5936
- 124.5929
- 306.5936 ]
+ /Rect [ 307.9178
+ 472.1936
+ 351.5999
+ 484.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER102': class PDFDictionary
-120 0 obj
+121 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 464.3898
- 273.5936
- 503.8498
- 285.5936 ]
+ /Rect [ 329.8034
+ 448.1936
+ 352.1804
+ 460.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER103': class PDFDictionary
-121 0 obj
+122 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 305.0429
- 261.5936
- 323.3829
- 273.5936 ]
+ /Rect [ 109.0098
+ 385.1936
+ 131.9967
+ 397.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER104': class PDFDictionary
-122 0 obj
+123 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/Clap/0.7) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 455.0104
- 243.5936
- 479.9015
- 255.5936 ]
+ /Rect [ 397.2929
+ 361.1936
+ 415.6329
+ 373.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER105': class PDFDictionary
-123 0 obj
+124 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://pypi.python.org/pypi/opterator) >>
/Border [ 0
0
0 ]
- /Rect [ 303.707
- 231.5936
- 322.047
- 243.5936 ]
+ /Rect [ 85.69291
+ 340.1936
+ 128.4929
+ 352.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER106': class PDFDictionary
-124 0 obj
+125 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/Clap/0.7) >>
+ /URI (http://pypi.python.org/pypi/CLIArgs) >>
/Border [ 0
0
0 ]
- /Rect [ 328.8186
- 231.5936
- 353.3701
- 243.5936 ]
+ /Rect [ 85.69291
+ 322.1936
+ 124.5929
+ 334.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER107': class PDFDictionary
-125 0 obj
+126 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 219.5936
- 81.03291
- 231.5936 ]
+ /Rect [ 464.3898
+ 301.1936
+ 503.8498
+ 313.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page13': class PDFPage
-126 0 obj
-% Page dictionary
-<< /Annots [ 98 0 R
- 99 0 R
- 100 0 R
- 101 0 R
- 102 0 R
- 103 0 R
- 104 0 R
- 105 0 R
- 106 0 R
- 107 0 R
- 108 0 R
- 109 0 R
- 110 0 R
- 111 0 R
- 112 0 R
- 113 0 R
- 114 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
- 125 0 R ]
- /Contents 176 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 163 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
% 'Annot.NUMBER108': class PDFDictionary
127 0 obj
<< /A << /S /URI
@@ -2099,10 +2089,10 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 106.199
- 729.5936
- 127.4851
- 741.5936 ]
+ /Rect [ 305.0429
+ 289.1936
+ 323.3829
+ 301.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2110,14 +2100,14 @@ endobj
128 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/Clap/0.7) >>
/Border [ 0
0
0 ]
- /Rect [ 128.5202
- 705.5936
- 167.9802
- 717.5936 ]
+ /Rect [ 455.0104
+ 271.1936
+ 479.9015
+ 283.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2125,14 +2115,14 @@ endobj
129 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 459.5254
- 705.5936
- 504.114
- 717.5936 ]
+ /Rect [ 303.707
+ 259.1936
+ 322.047
+ 271.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2140,14 +2130,14 @@ endobj
130 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://pypi.python.org/pypi/Clap/0.7) >>
/Border [ 0
0
0 ]
- /Rect [ 514.2427
- 705.5936
- 530.234
- 717.5936 ]
+ /Rect [ 328.8186
+ 259.1936
+ 353.3701
+ 271.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2155,14 +2145,14 @@ endobj
131 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 139.0773
- 669.5936
- 178.5373
- 681.5936 ]
+ /Rect [ 62.69291
+ 247.1936
+ 81.03291
+ 259.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2174,10 +2164,10 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 488.2705
- 669.5936
- 509.8666
- 681.5936 ]
+ /Rect [ 156.6051
+ 196.1936
+ 177.8606
+ 208.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2185,14 +2175,14 @@ endobj
133 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/md/gcodedev/plac/in-writing) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 333.3598
- 657.5936
- 458.0115
- 669.5936 ]
+ /Rect [ 186.6535
+ 172.1936
+ 226.1135
+ 184.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2200,14 +2190,14 @@ endobj
134 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 194.4129
- 645.5936
- 215.5329
- 657.5936 ]
+ /Rect [ 493.1227
+ 172.1936
+ 532.4646
+ 184.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2219,10 +2209,10 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 83.64556
- 594.5936
- 105.7082
- 606.5936 ]
+ /Rect [ 72.7804
+ 160.1936
+ 96.20788
+ 172.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2230,19 +2220,104 @@ endobj
136 0 obj
<< /A << /S /URI
/Type /Action
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 457.957
+ 148.1936
+ 531.337
+ 160.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER118': class PDFDictionary
+137 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 136.1936
+ 106.6029
+ 148.1936 ]
+ /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
+ 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 >>
+endobj
+% 'Annot.NUMBER119': class PDFDictionary
+139 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.64556
+ 729.5936
+ 105.7082
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER120': class PDFDictionary
+140 0 obj
+<< /A << /S /URI
+ /Type /Action
/URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
/Border [ 0
0
0 ]
/Rect [ 446.6
- 594.5936
+ 729.5936
502.5727
- 606.5936 ]
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER118': class PDFDictionary
-137 0 obj
+% 'Annot.NUMBER121': class PDFDictionary
+141 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2250,14 +2325,14 @@ endobj
0
0 ]
/Rect [ 275.6828
- 582.5936
+ 717.5936
297.3688
- 594.5936 ]
+ 729.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER119': class PDFDictionary
-138 0 obj
+% 'Annot.NUMBER122': class PDFDictionary
+142 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser) >>
@@ -2265,14 +2340,14 @@ endobj
0
0 ]
/Rect [ 77.19665
- 570.5936
+ 705.5936
139.4904
- 582.5936 ]
+ 717.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER120': class PDFDictionary
-139 0 obj
+% 'Annot.NUMBER123': class PDFDictionary
+143 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2280,14 +2355,14 @@ endobj
0
0 ]
/Rect [ 96.54131
- 558.5936
+ 693.5936
139.0255
- 570.5936 ]
+ 705.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER121': class PDFDictionary
-140 0 obj
+% 'Annot.NUMBER124': class PDFDictionary
+144 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2295,14 +2370,14 @@ endobj
0
0 ]
/Rect [ 203.5016
- 525.5936
+ 660.5936
245.8453
- 537.5936 ]
+ 672.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER122': class PDFDictionary
-141 0 obj
+% 'Annot.NUMBER125': class PDFDictionary
+145 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -2310,14 +2385,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 450.5936
+ 585.5936
138.7898
- 462.5936 ]
+ 597.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER123': class PDFDictionary
-142 0 obj
+% 'Annot.NUMBER126': class PDFDictionary
+146 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2325,14 +2400,14 @@ endobj
0
0 ]
/Rect [ 114.6649
- 438.5936
+ 573.5936
154.1249
- 450.5936 ]
+ 585.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER124': class PDFDictionary
-143 0 obj
+% 'Annot.NUMBER127': class PDFDictionary
+147 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2340,14 +2415,14 @@ endobj
0
0 ]
/Rect [ 191.6329
- 426.5936
+ 561.5936
233.8729
- 438.5936 ]
+ 573.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER125': class PDFDictionary
-144 0 obj
+% 'Annot.NUMBER128': class PDFDictionary
+148 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2355,14 +2430,44 @@ endobj
0
0 ]
/Rect [ 263.3429
- 396.5936
+ 531.5936
286.6829
- 408.5936 ]
+ 543.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER126': class PDFDictionary
-145 0 obj
+% '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
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2370,40 +2475,34 @@ endobj
0
0 ]
/Rect [ 258.5629
- 348.5936
+ 441.5936
276.9029
- 360.5936 ]
+ 453.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page14': class PDFPage
-146 0 obj
+% 'Page15': class PDFPage
+152 0 obj
% Page dictionary
-<< /Annots [ 127 0 R
- 128 0 R
- 129 0 R
- 130 0 R
- 131 0 R
- 132 0 R
- 133 0 R
- 134 0 R
- 135 0 R
- 136 0 R
- 137 0 R
- 138 0 R
- 139 0 R
+<< /Annots [ 139 0 R
140 0 R
141 0 R
142 0 R
143 0 R
144 0 R
- 145 0 R ]
- /Contents 177 0 R
+ 145 0 R
+ 146 0 R
+ 147 0 R
+ 148 0 R
+ 149 0 R
+ 150 0 R
+ 151 0 R ]
+ /Contents 184 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 163 0 R
+ /Parent 169 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -2414,189 +2513,189 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R147': class PDFCatalog
-147 0 obj
+% 'R153': class PDFCatalog
+153 0 obj
% Document Root
-<< /Outlines 149 0 R
- /PageLabels 178 0 R
+<< /Outlines 155 0 R
+ /PageLabels 185 0 R
/PageMode /UseNone
- /Pages 163 0 R
+ /Pages 169 0 R
/Type /Catalog >>
endobj
-% 'R148': class PDFInfo
-148 0 obj
+% 'R154': class PDFInfo
+154 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100611140636-01'00')
+ /CreationDate (D:20100615054641-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Plac: Parsing the Command Line the Easy Way) >>
endobj
-% 'R149': class PDFOutlines
-149 0 obj
+% 'R155': class PDFOutlines
+155 0 obj
<< /Count 13
- /First 150 0 R
- /Last 162 0 R
+ /First 156 0 R
+ /Last 168 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-150 0 obj
+156 0 obj
<< /Dest [ 46 0 R
/XYZ
62.69291
329.0236
0 ]
- /Next 151 0 R
- /Parent 149 0 R
+ /Next 157 0 R
+ /Parent 155 0 R
/Title (The importance of scaling down) >>
endobj
% 'Outline.1': class OutlineEntryObject
-151 0 obj
+157 0 obj
<< /Dest [ 54 0 R
/XYZ
62.69291
627.0236
0 ]
- /Next 152 0 R
- /Parent 149 0 R
- /Prev 150 0 R
+ /Next 158 0 R
+ /Parent 155 0 R
+ /Prev 156 0 R
/Title (Scripts with required arguments) >>
endobj
% 'Outline.2': class OutlineEntryObject
-152 0 obj
+158 0 obj
<< /Dest [ 60 0 R
/XYZ
62.69291
298.2236
0 ]
- /Next 153 0 R
- /Parent 149 0 R
- /Prev 151 0 R
+ /Next 159 0 R
+ /Parent 155 0 R
+ /Prev 157 0 R
/Title (Scripts with default arguments) >>
endobj
% 'Outline.3': class OutlineEntryObject
-153 0 obj
+159 0 obj
<< /Dest [ 72 0 R
/XYZ
62.69291
253.4236
0 ]
- /Next 154 0 R
- /Parent 149 0 R
- /Prev 152 0 R
+ /Next 160 0 R
+ /Parent 155 0 R
+ /Prev 158 0 R
/Title (Scripts with options \(and smart options\)) >>
endobj
% 'Outline.4': class OutlineEntryObject
-154 0 obj
+160 0 obj
<< /Dest [ 76 0 R
/XYZ
62.69291
251.0236
0 ]
- /Next 155 0 R
- /Parent 149 0 R
- /Prev 153 0 R
+ /Next 161 0 R
+ /Parent 155 0 R
+ /Prev 159 0 R
/Title (Scripts with flags) >>
endobj
% 'Outline.5': class OutlineEntryObject
-155 0 obj
+161 0 obj
<< /Dest [ 78 0 R
/XYZ
62.69291
- 443.4236
+ 401.4236
0 ]
- /Next 156 0 R
- /Parent 149 0 R
- /Prev 154 0 R
+ /Next 162 0 R
+ /Parent 155 0 R
+ /Prev 160 0 R
/Title (plac for Python 2.X users) >>
endobj
% 'Outline.6': class OutlineEntryObject
-156 0 obj
+162 0 obj
<< /Dest [ 86 0 R
/XYZ
62.69291
- 695.8236
+ 647.8236
0 ]
- /Next 157 0 R
- /Parent 149 0 R
- /Prev 155 0 R
+ /Next 163 0 R
+ /Parent 155 0 R
+ /Prev 161 0 R
/Title (More features) >>
endobj
% 'Outline.7': class OutlineEntryObject
-157 0 obj
+163 0 obj
<< /Dest [ 90 0 R
/XYZ
62.69291
- 254.0849
+ 218.0849
0 ]
- /Next 158 0 R
- /Parent 149 0 R
- /Prev 156 0 R
+ /Next 164 0 R
+ /Parent 155 0 R
+ /Prev 162 0 R
/Title (A realistic example) >>
endobj
% 'Outline.8': class OutlineEntryObject
-158 0 obj
-<< /Dest [ 93 0 R
+164 0 obj
+<< /Dest [ 94 0 R
/XYZ
62.69291
- 180.6236
+ 765.0236
0 ]
- /Next 159 0 R
- /Parent 149 0 R
- /Prev 157 0 R
+ /Next 165 0 R
+ /Parent 155 0 R
+ /Prev 163 0 R
/Title (Keyword arguments) >>
endobj
% 'Outline.9': class OutlineEntryObject
-159 0 obj
-<< /Dest [ 97 0 R
+165 0 obj
+<< /Dest [ 116 0 R
/XYZ
62.69291
- 152.2236
+ 765.0236
0 ]
- /Next 160 0 R
- /Parent 149 0 R
- /Prev 158 0 R
+ /Next 166 0 R
+ /Parent 155 0 R
+ /Prev 164 0 R
/Title (plac vs argparse) >>
endobj
% 'Outline.10': class OutlineEntryObject
-160 0 obj
-<< /Dest [ 126 0 R
+166 0 obj
+<< /Dest [ 138 0 R
/XYZ
62.69291
- 393.0236
+ 420.6236
0 ]
- /Next 161 0 R
- /Parent 149 0 R
- /Prev 159 0 R
+ /Next 167 0 R
+ /Parent 155 0 R
+ /Prev 165 0 R
/Title (plac vs the rest of the world) >>
endobj
% 'Outline.11': class OutlineEntryObject
-161 0 obj
-<< /Dest [ 146 0 R
+167 0 obj
+<< /Dest [ 138 0 R
/XYZ
62.69291
- 765.0236
+ 231.6236
0 ]
- /Next 162 0 R
- /Parent 149 0 R
- /Prev 160 0 R
+ /Next 168 0 R
+ /Parent 155 0 R
+ /Prev 166 0 R
/Title (The future) >>
endobj
% 'Outline.12': class OutlineEntryObject
-162 0 obj
-<< /Dest [ 146 0 R
+168 0 obj
+<< /Dest [ 152 0 R
/XYZ
62.69291
- 630.0236
+ 765.0236
0 ]
- /Parent 149 0 R
- /Prev 161 0 R
+ /Parent 155 0 R
+ /Prev 167 0 R
/Title (Trivia: the story behind the name) >>
endobj
-% 'R163': class PDFPages
-163 0 obj
+% 'R169': class PDFPages
+169 0 obj
% page tree
-<< /Count 14
+<< /Count 15
/Kids [ 46 0 R
54 0 R
60 0 R
@@ -2607,14 +2706,15 @@ endobj
78 0 R
86 0 R
90 0 R
- 93 0 R
- 97 0 R
- 126 0 R
- 146 0 R ]
+ 91 0 R
+ 94 0 R
+ 116 0 R
+ 138 0 R
+ 152 0 R ]
/Type /Pages >>
endobj
-% 'R164': class PDFStream
-164 0 obj
+% 'R170': class PDFStream
+170 0 obj
% page stream
<< /Length 8958 >>
stream
@@ -2908,7 +3008,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 (11) Tj T* -60.88 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (12) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2922,7 +3022,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
@@ -2936,7 +3036,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 (14) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2964,7 +3064,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 (15) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -3005,8 +3105,8 @@ Q
endstream
endobj
-% 'R165': class PDFStream
-165 0 obj
+% 'R171': class PDFStream
+171 0 obj
% page stream
<< /Length 4830 >>
stream
@@ -3094,8 +3194,8 @@ Q
endstream
endobj
-% 'R166': class PDFStream
-166 0 obj
+% 'R172': class PDFStream
+172 0 obj
% page stream
<< /Length 4076 >>
stream
@@ -3253,8 +3353,8 @@ Q
endstream
endobj
-% 'R167': class PDFStream
-167 0 obj
+% 'R173': class PDFStream
+173 0 obj
% page stream
<< /Length 3963 >>
stream
@@ -3400,8 +3500,8 @@ Q
endstream
endobj
-% 'R168': class PDFStream
-168 0 obj
+% 'R174': class PDFStream
+174 0 obj
% page stream
<< /Length 4987 >>
stream
@@ -3543,8 +3643,8 @@ Q
endstream
endobj
-% 'R169': class PDFStream
-169 0 obj
+% 'R175': class PDFStream
+175 0 obj
% page stream
<< /Length 4202 >>
stream
@@ -3691,8 +3791,8 @@ Q
endstream
endobj
-% 'R170': class PDFStream
-170 0 obj
+% 'R176': class PDFStream
+176 0 obj
% page stream
<< /Length 4357 >>
stream
@@ -3871,10 +3971,10 @@ Q
endstream
endobj
-% 'R171': class PDFStream
-171 0 obj
+% 'R177': class PDFStream
+177 0 obj
% page stream
-<< /Length 4910 >>
+<< /Length 5041 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -3953,19 +4053,25 @@ BT 1 0 0 1 0 52.82 Tm 5.832651 Tw 12 TL /F1 10 Tf 0 0 0 rg (For consistency with
Q
Q
q
-1 0 0 1 62.69291 422.4236 cm
+1 0 0 1 62.69291 413.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
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 356.4236 cm
+1 0 0 1 62.69291 314.4236 cm
q
BT 1 0 0 1 0 52.82 Tm .211807 Tw 12 TL /F1 10 Tf 0 0 0 rg (I do not use Python 3. At work we are just starting to think about migrating to Python 2.6. It will take years) Tj T* 0 Tw .304724 Tw (before we think to migrate to Python 3. I am pretty much sure most Pythonistas are in the same situation.) Tj T* 0 Tw 1.459984 Tw (Therefore ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides a way to work with function annotations even in Python 2.X \(including Python) Tj T* 0 Tw .226098 Tw (2.3\). There is no magic involved; you just need to add the annotations by hand. For instance the annotate) Tj T* 0 Tw (function declaration) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 311.2236 cm
+1 0 0 1 62.69291 269.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3986,14 +4092,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 291.2236 cm
+1 0 0 1 62.69291 249.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 210.0236 cm
+1 0 0 1 62.69291 168.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -4014,33 +4120,12 @@ Q
Q
Q
q
-1 0 0 1 62.69291 166.0236 cm
+1 0 0 1 62.69291 124.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
Q
q
-1 0 0 1 62.69291 96.82362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (@plac.annotations\() Tj T* ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -4051,14 +4136,14 @@ Q
endstream
endobj
-% 'R172': class PDFStream
-172 0 obj
+% 'R178': class PDFStream
+178 0 obj
% page stream
-<< /Length 6179 >>
+<< /Length 5962 >>
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 691.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4068,39 +4153,39 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
+n -6 -6 468.6898 72 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ( ...) Tj T* ET
+BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL (@plac.annotations\() Tj T* ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 707.8236 cm
+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
Q
Q
q
-1 0 0 1 62.69291 674.8236 cm
+1 0 0 1 62.69291 626.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (More features) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 620.8236 cm
+1 0 0 1 62.69291 572.8236 cm
q
BT 1 0 0 1 0 40.82 Tm 1.483488 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the goals of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is to have a learning curve of ) Tj /F5 10 Tf (minutes ) Tj /F1 10 Tf (for its core features, compared to the) Tj T* 0 Tw 1.152093 Tw (learning curve of ) Tj /F5 10 Tf (hours ) Tj /F1 10 Tf (of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. In order to reach this goal, I have ) Tj /F5 10 Tf (not ) Tj /F1 10 Tf (sacrificed all the features of) Tj T* 0 Tw 2.89936 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually a lot of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (power persists in ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Until now, I have only showed simple) Tj T* 0 Tw (annotations, but in general an annotation is a 6-tuple of the form) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 614.8236 cm
+1 0 0 1 62.69291 566.8236 cm
Q
q
-1 0 0 1 62.69291 602.8236 cm
+1 0 0 1 62.69291 554.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -4115,40 +4200,40 @@ q
Q
Q
q
-1 0 0 1 62.69291 602.8236 cm
+1 0 0 1 62.69291 554.8236 cm
Q
q
-1 0 0 1 62.69291 560.8236 cm
+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
Q
Q
q
-1 0 0 1 62.69291 530.8236 cm
+1 0 0 1 62.69291 482.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.05061 Tw 12 TL /F4 10 Tf 0 0 0 rg (type ) Tj /F1 10 Tf (is used to automagically convert the command line arguments from the string type to any Python) Tj T* 0 Tw (type; by default there is no conversion and ) Tj /F4 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 500.8236 cm
+1 0 0 1 62.69291 452.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.904692 Tw 12 TL /F4 10 Tf 0 0 0 rg (choices ) Tj /F1 10 Tf (is used to restrict the number of the valid options; by default there is no restriction i.e.) Tj T* 0 Tw /F4 10 Tf (choices=None) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 458.8236 cm
+1 0 0 1 62.69291 410.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.071751 Tw 12 TL /F4 10 Tf 0 0 0 rg (metavar ) Tj /F1 10 Tf (is used to change the argument name in the usage message \(and only there\); by default the) Tj T* 0 Tw 1.056654 Tw (metavar is ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (: this means that the name in the usage message is the same as the argument name,) Tj T* 0 Tw (unless the argument has a default and in such a case is equal to the stringified form of the default.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 440.8236 cm
+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
Q
Q
q
-1 0 0 1 62.69291 232.4467 cm
+1 0 0 1 62.69291 184.4467 cm
q
q
.976496 0 0 .976496 0 0 cm
@@ -4169,46 +4254,25 @@ Q
Q
Q
q
-1 0 0 1 62.69291 200.4467 cm
+1 0 0 1 62.69291 152.4467 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.304987 Tw 12 TL /F1 10 Tf 0 0 0 rg (Usually the main function of a script works by side effects and returns ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (; in this example instead I) Tj T* 0 Tw (choose to return the number and to print it in the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block.) Tj T* ET
+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 170.4467 cm
+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 152.4467 cm
+1 0 0 1 62.69291 104.4467 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 95.24669 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* T* (A script to add and multiply numbers) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -4219,14 +4283,14 @@ Q
endstream
endobj
-% 'R173': class PDFStream
-173 0 obj
+% 'R179': class PDFStream
+179 0 obj
% page stream
-<< /Length 4996 >>
+<< /Length 5001 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 667.8236 cm
+1 0 0 1 62.69291 631.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4236,24 +4300,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 96 re B*
+n -6 -6 468.6898 132 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL T* (positional arguments:) Tj T* ( {add,mul} The name of an operator) Tj T* ( n A number) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+BT 1 0 0 1 0 113.71 Tm /F4 10 Tf 12 TL (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* T* (A script to add and multiply numbers) Tj T* T* (positional arguments:) Tj T* ( {add,mul} The name of an operator) Tj T* ( n A number) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 635.8236 cm
+1 0 0 1 62.69291 599.8236 cm
q
BT 1 0 0 1 0 16.82 Tm .15186 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function has been automatically added to the usage message. Here) Tj T* 0 Tw (are a couple of examples of use:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 542.4849 cm
+1 0 0 1 62.69291 506.4849 cm
q
q
.87797 0 0 .87797 0 0 cm
@@ -4274,13 +4338,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 486.4849 cm
+1 0 0 1 62.69291 450.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
Q
Q
q
-1 0 0 1 62.69291 429.2849 cm
+1 0 0 1 62.69291 393.2849 cm
q
q
1 0 0 1 0 0 cm
@@ -4300,13 +4364,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 409.2849 cm
+1 0 0 1 62.69291 373.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 328.0849 cm
+1 0 0 1 62.69291 292.0849 cm
q
q
1 0 0 1 0 0 cm
@@ -4326,25 +4390,25 @@ Q
Q
Q
q
-1 0 0 1 62.69291 296.0849 cm
+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
q
-1 0 0 1 62.69291 266.0849 cm
+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 233.0849 cm
+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
Q
Q
q
-1 0 0 1 62.69291 191.0849 cm
+1 0 0 1 62.69291 155.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
Q
@@ -4360,11 +4424,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# dbcli.py) Tj T* (import plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (@plac.annotations\() Tj T* ( db=\("Connection string", 'positional', None, SqlSoup\),) Tj T* ET
+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
Q
Q
Q
@@ -4381,14 +4445,14 @@ Q
endstream
endobj
-% 'R174': class PDFStream
-174 0 obj
+% 'R180': class PDFStream
+180 0 obj
% page stream
-<< /Length 3892 >>
+<< /Length 3386 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 475.8236 cm
+1 0 0 1 62.69291 439.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4398,31 +4462,31 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 288 re B*
+n -6 -6 468.6898 324 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 269.71 Tm /F4 10 Tf 12 TL ( header=\("Header", 'flag', 'H'\),) Tj T* ( sqlcmd=\("SQL command", 'option', 'c', str, None, "SQL"\),) Tj T* ( delimiter=\("Column separator", 'option', 'd'\),) Tj T* ( scripts="SQL scripts",) Tj T* ( \)) Tj T* (def main\(db, header, sqlcmd, delimiter="|", *scripts\):) Tj T* ( "A script to run queries and SQL scripts on a database") Tj T* ( yield 'Working on %s' % db.bind.url) Tj T* T* ( if sqlcmd:) Tj T* ( result = db.bind.execute\(sqlcmd\)) Tj T* ( if header: # print the header) Tj T* ( yield delimiter.join\(result.keys\(\)\)) Tj T* ( for row in result: # print the rows) Tj T* ( yield delimiter.join\(map\(str, row\)\)) Tj T* T* ( for script in scripts:) Tj T* ( db.bind.execute\(file\(script\).read\(\)\)) Tj T* ( yield 'executed %s' % script) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 407.8236 cm
+1 0 0 1 62.69291 371.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
Q
Q
q
-1 0 0 1 62.69291 389.8236 cm
+1 0 0 1 62.69291 353.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 212.6236 cm
+1 0 0 1 62.69291 176.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4443,25 +4507,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 192.6236 cm
+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 62.69291 159.6236 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 117.6236 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 56.69291 56.69291 cm
q
0 0 0 rg
@@ -4472,14 +4524,26 @@ Q
endstream
endobj
-% 'R175': class PDFStream
-175 0 obj
+% 'R181': class PDFStream
+181 0 obj
% page stream
-<< /Length 3855 >>
+<< /Length 3859 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 535.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 (Keyword arguments) 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.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
q
q
1 0 0 1 0 0 cm
@@ -4500,14 +4564,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 515.8236 cm
+1 0 0 1 62.69291 444.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the generated usage message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 386.6236 cm
+1 0 0 1 62.69291 315.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4528,14 +4592,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 366.6236 cm
+1 0 0 1 62.69291 295.6236 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 297.4236 cm
+1 0 0 1 62.69291 226.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4556,13 +4620,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 265.4236 cm
+1 0 0 1 62.69291 194.4236 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 208.2236 cm
+1 0 0 1 62.69291 137.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4583,25 +4647,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 164.2236 cm
+1 0 0 1 62.69291 93.22362 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 131.2236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac vs argparse) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 89.22362 cm
-q
-BT 1 0 0 1 0 28.82 Tm 1.065988 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is opinionated and by design it does not try to make available all of the features of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (in an) Tj T* 0 Tw .177126 Tw (easy way. In particular you should be aware of the following limitations/differences \(the following assumes) Tj T* 0 Tw (knowledge of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (\):) Tj T* ET
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -4612,20 +4664,32 @@ Q
endstream
endobj
-% 'R176': class PDFStream
-176 0 obj
+% 'R182': class PDFStream
+182 0 obj
% page stream
-<< /Length 8408 >>
+<< /Length 8875 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 759.0236 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 (plac vs argparse) Tj T* ET
Q
+Q
+q
+1 0 0 1 62.69291 702.0236 cm
q
-1 0 0 1 62.69291 759.0236 cm
+BT 1 0 0 1 0 28.82 Tm 1.065988 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is opinionated and by design it does not try to make available all of the features of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (in an) Tj T* 0 Tw .177126 Tw (easy way. In particular you should be aware of the following limitations/differences \(the following assumes) Tj T* 0 Tw (knowledge of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (\):) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 696.0236 cm
Q
q
-1 0 0 1 62.69291 681.0236 cm
+1 0 0 1 62.69291 696.0236 cm
+Q
+q
+1 0 0 1 62.69291 618.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4645,13 +4709,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 681.0236 cm
+1 0 0 1 62.69291 618.0236 cm
Q
q
-1 0 0 1 62.69291 681.0236 cm
+1 0 0 1 62.69291 618.0236 cm
Q
q
-1 0 0 1 62.69291 639.0236 cm
+1 0 0 1 62.69291 576.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4671,13 +4735,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 639.0236 cm
+1 0 0 1 62.69291 576.0236 cm
Q
q
-1 0 0 1 62.69291 639.0236 cm
+1 0 0 1 62.69291 576.0236 cm
Q
q
-1 0 0 1 62.69291 573.0236 cm
+1 0 0 1 62.69291 510.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4697,13 +4761,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 573.0236 cm
+1 0 0 1 62.69291 510.0236 cm
Q
q
-1 0 0 1 62.69291 573.0236 cm
+1 0 0 1 62.69291 510.0236 cm
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 468.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4723,13 +4787,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 468.0236 cm
Q
q
-1 0 0 1 62.69291 531.0236 cm
+1 0 0 1 62.69291 468.0236 cm
Q
q
-1 0 0 1 62.69291 501.0236 cm
+1 0 0 1 62.69291 438.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4749,13 +4813,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 501.0236 cm
+1 0 0 1 62.69291 438.0236 cm
Q
q
-1 0 0 1 62.69291 501.0236 cm
+1 0 0 1 62.69291 438.0236 cm
Q
q
-1 0 0 1 62.69291 459.0236 cm
+1 0 0 1 62.69291 396.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4775,37 +4839,177 @@ q
Q
Q
q
-1 0 0 1 62.69291 459.0236 cm
+1 0 0 1 62.69291 396.0236 cm
+Q
+q
+1 0 0 1 62.69291 396.0236 cm
+Q
+q
+1 0 0 1 62.69291 378.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 (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
+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
+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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 60 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (def main\(...\):) Tj T* ( pass) Tj T* T* (main.add_help = False) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 168.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 459.0236 cm
+1 0 0 1 62.69291 138.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 405.0236 cm
+1 0 0 1 62.69291 108.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm 3.779431 Tw 12 TL /F1 10 Tf 0 0 0 rg (I should stress that if you want to access all of the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features from ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (you can use) Tj T* 0 Tw 2.723059 Tw /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and you will get the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object. The the full power of) Tj T* 0 Tw 2.44152 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (is then available to you: you can use ) Tj /F4 10 Tf (add_argument) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (add_subparsers\(\)) Tj /F1 10 Tf (, etc. In other) Tj T* 0 Tw (words, while some features are not supported directly, ) Tj /F5 10 Tf (all ) Tj /F1 10 Tf (features are supported indirectly.) Tj T* ET
+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 62.69291 372.0236 cm
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 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
+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 (main.prefix_chars='/-') Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 695.8236 cm
+q
+BT 1 0 0 1 0 28.82 Tm .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
+Q
+Q
+q
+1 0 0 1 62.69291 665.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 7.709147 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is possible to access directly the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, by invoking the) Tj T* 0 Tw /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 548.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+BT 1 0 0 1 0 89.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print plac.parser_from\(main\)) Tj T* (ArgumentParser\(prog='', usage=None, description=None, version=None,) Tj T* (formatter_class=) Tj (<) Tj (class 'argparse.HelpFormatter') Tj (>) Tj (, conflict_handler='error',) Tj T* (add_help=True\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 516.6236 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
+Q
+Q
+q
+1 0 0 1 62.69291 486.6236 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
+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
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 330.0236 cm
+1 0 0 1 62.69291 357.6236 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 324.0236 cm
+1 0 0 1 62.69291 351.6236 cm
Q
q
-1 0 0 1 62.69291 324.0236 cm
+1 0 0 1 62.69291 351.6236 cm
Q
q
-1 0 0 1 62.69291 306.0236 cm
+1 0 0 1 62.69291 333.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4825,13 +5029,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 306.0236 cm
+1 0 0 1 62.69291 333.6236 cm
Q
q
-1 0 0 1 62.69291 306.0236 cm
+1 0 0 1 62.69291 333.6236 cm
Q
q
-1 0 0 1 62.69291 288.0236 cm
+1 0 0 1 62.69291 315.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4851,72 +5055,72 @@ q
Q
Q
q
-1 0 0 1 62.69291 288.0236 cm
+1 0 0 1 62.69291 315.6236 cm
Q
q
-1 0 0 1 62.69291 288.0236 cm
+1 0 0 1 62.69291 315.6236 cm
Q
q
-1 0 0 1 62.69291 258.0236 cm
+1 0 0 1 62.69291 285.6236 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 216.0236 cm
+1 0 0 1 62.69291 243.6236 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
+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
+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
-% 'R177': class PDFStream
-177 0 obj
+% 'R184': class PDFStream
+184 0 obj
% page stream
-<< /Length 5266 >>
+<< /Length 4265 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
1 0 0 1 62.69291 744.0236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The future) 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 .166098 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is around 170 lines of code, not counting blanks, comments and docstrings. I do not plan to) Tj T* 0 Tw .388626 Tw (extend the core much in the future. The idea is to keep the module short: it is and it should remain a little) Tj T* 0 Tw 2.348651 Tw (wrapper over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually I have thought about contributing the core back to ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac) Tj T* 0 Tw .22311 Tw 0 0 0 rg (becomes successfull and gains a reasonable number of users. For the moment it should be considered in) Tj T* 0 Tw .127882 Tw (alpha status: after all I wrote the first version of it in three days, including the tests, the documentation and) Tj T* 0 Tw .476098 Tw (the time to learn ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. At the moment I have already written some powerful extensions for ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and I) Tj T* 0 Tw .600574 Tw (plan to release them in the 0.5 release, once I finish writing ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (. The extensions) Tj T* 0 Tw (will be released together with ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (but in a separated module.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 609.0236 cm
-q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Trivia: the story behind the name) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 543.0236 cm
+1 0 0 1 62.69291 678.0236 cm
q
BT 1 0 0 1 0 52.82 Tm .942651 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (project started very humble: I just wanted to make easy_installable my old ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw .565988 Tw (and to publish it on PyPI. The original name of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was optionparser and the idea behind it was to build) Tj T* 0 Tw .603735 Tw (an ) Tj 0 0 .501961 rg (OptionParser ) Tj 0 0 0 rg (object from the docstring of the module. However, before doing that, I decided to check) Tj T* 0 Tw .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 537.0236 cm
+1 0 0 1 62.69291 672.0236 cm
Q
q
-1 0 0 1 62.69291 537.0236 cm
+1 0 0 1 62.69291 672.0236 cm
Q
q
-1 0 0 1 62.69291 507.0236 cm
+1 0 0 1 62.69291 642.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4936,13 +5140,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 507.0236 cm
+1 0 0 1 62.69291 642.0236 cm
Q
q
-1 0 0 1 62.69291 507.0236 cm
+1 0 0 1 62.69291 642.0236 cm
Q
q
-1 0 0 1 62.69291 477.0236 cm
+1 0 0 1 62.69291 612.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4963,32 +5167,38 @@ q
Q
Q
q
-1 0 0 1 62.69291 477.0236 cm
+1 0 0 1 62.69291 612.0236 cm
Q
q
-1 0 0 1 62.69291 477.0236 cm
+1 0 0 1 62.69291 612.0236 cm
Q
q
-1 0 0 1 62.69291 423.0236 cm
+1 0 0 1 62.69291 558.0236 cm
q
BT 1 0 0 1 0 40.82 Tm .600574 Tw 12 TL /F1 10 Tf 0 0 0 rg (Putting together these two observations with the original idea of inferring the parser I decided to build an) Tj T* 0 Tw .516905 Tw 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object from function annotations. The ) Tj /F4 10 Tf (optionparser ) Tj /F1 10 Tf (name was ruled out, since I was) Tj T* 0 Tw 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 393.0236 cm
+1 0 0 1 62.69291 528.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.093876 Tw 12 TL /F1 10 Tf 0 0 0 rg (I made a research on PyPI and the name ) 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 363.0236 cm
+1 0 0 1 62.69291 498.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .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 345.0236 cm
+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
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
@@ -4997,131 +5207,139 @@ 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 (15) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R178': class PDFPageLabels
-178 0 obj
+% 'R185': class PDFPageLabels
+185 0 obj
% Document Root
<< /Nums [ 0
- 179 0 R
+ 186 0 R
1
- 180 0 R
+ 187 0 R
2
- 181 0 R
+ 188 0 R
3
- 182 0 R
+ 189 0 R
4
- 183 0 R
+ 190 0 R
5
- 184 0 R
+ 191 0 R
6
- 185 0 R
+ 192 0 R
7
- 186 0 R
+ 193 0 R
8
- 187 0 R
+ 194 0 R
9
- 188 0 R
+ 195 0 R
10
- 189 0 R
+ 196 0 R
11
- 190 0 R
+ 197 0 R
12
- 191 0 R
+ 198 0 R
13
- 192 0 R ] >>
+ 199 0 R
+ 14
+ 200 0 R ] >>
endobj
-% 'R179': class PDFPageLabel
-179 0 obj
+% 'R186': class PDFPageLabel
+186 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R180': class PDFPageLabel
-180 0 obj
+% 'R187': class PDFPageLabel
+187 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R181': class PDFPageLabel
-181 0 obj
+% 'R188': class PDFPageLabel
+188 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R182': class PDFPageLabel
-182 0 obj
+% 'R189': class PDFPageLabel
+189 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R183': class PDFPageLabel
-183 0 obj
+% 'R190': class PDFPageLabel
+190 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R184': class PDFPageLabel
-184 0 obj
+% 'R191': class PDFPageLabel
+191 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R185': class PDFPageLabel
-185 0 obj
+% 'R192': class PDFPageLabel
+192 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R186': class PDFPageLabel
-186 0 obj
+% 'R193': class PDFPageLabel
+193 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R187': class PDFPageLabel
-187 0 obj
+% 'R194': class PDFPageLabel
+194 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R188': class PDFPageLabel
-188 0 obj
+% 'R195': class PDFPageLabel
+195 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R189': class PDFPageLabel
-189 0 obj
+% 'R196': class PDFPageLabel
+196 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R190': class PDFPageLabel
-190 0 obj
+% 'R197': class PDFPageLabel
+197 0 obj
% None
<< /S /D
/St 12 >>
endobj
-% 'R191': class PDFPageLabel
-191 0 obj
+% 'R198': class PDFPageLabel
+198 0 obj
% None
<< /S /D
/St 13 >>
endobj
-% 'R192': class PDFPageLabel
-192 0 obj
+% 'R199': class PDFPageLabel
+199 0 obj
% None
<< /S /D
/St 14 >>
endobj
+% 'R200': class PDFPageLabel
+200 0 obj
+% None
+<< /S /D
+ /St 15 >>
+endobj
xref
-0 193
+0 201
0000000000 65535 f
0000000113 00000 n
0000000258 00000 n
@@ -5150,179 +5368,187 @@ xref
0000005595 00000 n
0000005838 00000 n
0000006081 00000 n
-0000006324 00000 n
-0000006567 00000 n
-0000006811 00000 n
-0000007055 00000 n
-0000007299 00000 n
-0000007543 00000 n
-0000007787 00000 n
-0000008030 00000 n
-0000008292 00000 n
-0000008555 00000 n
-0000008805 00000 n
-0000009054 00000 n
-0000009320 00000 n
-0000009572 00000 n
-0000009822 00000 n
-0000010074 00000 n
-0000010324 00000 n
-0000010576 00000 n
-0000010811 00000 n
-0000011476 00000 n
-0000011728 00000 n
-0000011979 00000 n
-0000012218 00000 n
-0000012413 00000 n
-0000012674 00000 n
-0000012938 00000 n
-0000013172 00000 n
-0000013535 00000 n
-0000013787 00000 n
-0000014039 00000 n
-0000014290 00000 n
-0000014540 00000 n
-0000014777 00000 n
-0000015131 00000 n
-0000015380 00000 n
-0000015632 00000 n
-0000015869 00000 n
-0000016205 00000 n
-0000016457 00000 n
-0000016709 00000 n
-0000016997 00000 n
-0000017249 00000 n
-0000017537 00000 n
-0000017825 00000 n
-0000018062 00000 n
-0000018434 00000 n
-0000018671 00000 n
-0000018989 00000 n
-0000019226 00000 n
-0000019544 00000 n
-0000019781 00000 n
-0000020099 00000 n
-0000020351 00000 n
-0000020603 00000 n
-0000020853 00000 n
-0000021103 00000 n
-0000021353 00000 n
-0000021605 00000 n
-0000021840 00000 n
-0000022212 00000 n
-0000022464 00000 n
-0000022710 00000 n
-0000022973 00000 n
-0000023309 00000 n
-0000023561 00000 n
-0000023798 00000 n
-0000024125 00000 n
-0000024376 00000 n
-0000024626 00000 n
-0000024862 00000 n
-0000025198 00000 n
-0000025448 00000 n
-0000025700 00000 n
-0000025951 00000 n
-0000026204 00000 n
-0000026455 00000 n
-0000026708 00000 n
-0000026959 00000 n
-0000027211 00000 n
-0000027464 00000 n
-0000027744 00000 n
-0000027997 00000 n
-0000028250 00000 n
-0000028529 00000 n
-0000028809 00000 n
-0000029060 00000 n
-0000029313 00000 n
-0000029601 00000 n
-0000029852 00000 n
-0000030105 00000 n
-0000030359 00000 n
-0000030618 00000 n
-0000030875 00000 n
-0000031127 00000 n
-0000031381 00000 n
-0000031639 00000 n
-0000031891 00000 n
-0000032149 00000 n
-0000032388 00000 n
-0000032977 00000 n
-0000033230 00000 n
-0000033482 00000 n
-0000033733 00000 n
-0000033986 00000 n
-0000034238 00000 n
-0000034492 00000 n
-0000034773 00000 n
-0000035027 00000 n
-0000035281 00000 n
-0000035568 00000 n
-0000035822 00000 n
-0000036133 00000 n
-0000036385 00000 n
-0000036637 00000 n
-0000036926 00000 n
-0000037178 00000 n
-0000037430 00000 n
-0000037688 00000 n
-0000037927 00000 n
-0000038414 00000 n
-0000038578 00000 n
-0000038852 00000 n
-0000038981 00000 n
-0000039175 00000 n
-0000039386 00000 n
-0000039596 00000 n
-0000039818 00000 n
-0000040016 00000 n
-0000040221 00000 n
-0000040414 00000 n
-0000040613 00000 n
-0000040810 00000 n
-0000041007 00000 n
-0000041218 00000 n
-0000041410 00000 n
-0000041593 00000 n
-0000041823 00000 n
-0000050884 00000 n
-0000055817 00000 n
-0000059996 00000 n
-0000064062 00000 n
-0000069152 00000 n
-0000073457 00000 n
-0000077917 00000 n
-0000082930 00000 n
-0000089212 00000 n
-0000094311 00000 n
-0000098306 00000 n
-0000102264 00000 n
-0000110775 00000 n
-0000116148 00000 n
-0000116430 00000 n
-0000116509 00000 n
-0000116588 00000 n
-0000116667 00000 n
-0000116746 00000 n
-0000116825 00000 n
-0000116904 00000 n
-0000116983 00000 n
-0000117062 00000 n
-0000117141 00000 n
-0000117221 00000 n
-0000117301 00000 n
-0000117381 00000 n
-0000117461 00000 n
+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
+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
+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
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\2315\367\261S5[w\201\312b\3121\246-\033) (\2315\367\261S5[w\201\312b\3121\246-\033)]
+ [(\312\016\325\031\026V\207X\350\364\303&\322-:\354) (\312\016\325\031\026V\207X\350\364\303&\322-:\354)]
- /Info 148 0 R
- /Root 147 0 R
- /Size 193 >>
+ /Info 154 0 R
+ /Root 153 0 R
+ /Size 201 >>
startxref
-117510
+125290
%%EOF
diff --git a/plac/doc/plac.txt b/plac/doc/plac.txt
index 88e85a2..c0e1252 100644
--- a/plac/doc/plac.txt
+++ b/plac/doc/plac.txt
@@ -291,6 +291,12 @@ arguments. This is just a convention and you are not forced to use it,
except for the default arguments (including the varargs) which must
stay at the end as it is required by the Python syntax.
+I also suggests to specify a one-character abbreviation for flags: in
+this way you can use the GNU-style composition of flags (i.e. ``-zxvf``
+is an abbreviation of ``-z -x -v -f``). I usually do not provide
+the one-character abbreviation for options, since it does not make sense
+to compose them.
+
plac for Python 2.X users
--------------------------------------------------
@@ -367,7 +373,7 @@ argparse_ documentation):
:literal:
-Usually the main function of a script works by side effects and returns
+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.
@@ -526,12 +532,76 @@ following assumes knowledge of argparse_):
that the ability to define your own annotation objects (again, see
the `advanced usage document`_) may mitigate the need for custom actions.
-I should stress that if you want to access all of the argparse_ features
-from plac_ you can use ``plac.parser_from`` and you will get
-the underlying ArgumentParser_ object. The the full power of argparse_
-is then available to you: you can use ``add_argument``, ``add_subparsers()``,
-etc. In other words, while some features are not supported directly,
-*all* features are supported indirectly.
+plac_ can leverage directly on many argparse_ features.
+
+For instance, you can make invisible an argument in the usage message
+simply by using ``'==SUPPRESS=='`` as help string (or
+``argparse.SUPPRESS``). Similarly, you can use argparse.FileType_
+directly.
+
+It is also possible to pass options to the underlying
+``argparse.ArgumentParser`` object (currently it accepts the default
+arguments ``description``, ``epilog``, ``prog``, ``usage``,
+``add_help``, ``argument_default``, ``parents``, ``prefix_chars``,
+``fromfile_prefix_chars``, ``conflict_handler``, ``formatter_class``).
+It is enough to set such attributes on the ``main`` function. For
+instance
+
+::
+
+ def main(...):
+ pass
+
+ main.add_help = False
+
+disables the recognition of the help flag ``-h, --help``. This
+mechanism does not look particularly elegant, but it works well
+enough. I assume that the typical user of plac_ will be happy with
+the defaults and would not want to change them; still it is possible
+if she wants to.
+
+For instance, by setting the ``description`` attribute, it is possible
+to add a comment to the usage message (by default the docstring of the
+``main`` function is used as description).
+
+It is also possible to change the option prefix; for
+instance if your script must run under Windows and you want to use "/"
+as option prefix you can add the line::
+
+ main.prefix_chars='/-'
+
+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`_ .
+
+It is possible to access directly the underlying ArgumentParser_ object, by
+invoking the ``plac.parser_from`` utility function:
+
+>>> import plac
+>>> def main(arg):
+... pass
+...
+>>> print plac.parser_from(main)
+ArgumentParser(prog='', usage=None, description=None, version=None,
+formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error',
+add_help=True)
+
+Internally ``plac.call`` uses ``plac.parser_from`` and adds the parser
+as an attribute ``.p``. 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
+users should not need to use it, unless they want to access *all*
+of the features of argparse_ directly without calling the main function.
+
+Interested readers should read the documentation of argparse_ to
+understand the meaning of the other options. If there is a set of
+options that you use very often, you may consider writing a decorator
+adding such options to the ``main`` function for you. For simplicity,
+plac_ does not perform any magic except the addition of the ``.p``
+attribute.
plac vs the rest of the world
------------------------------------------
@@ -557,19 +627,15 @@ easier than plac_.
The future
-------------------------------
-Currently plac_ is around 170 lines of code, not counting blanks,
+Currently the core of plac_ 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 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: after all I wrote the first version of it in
-three days, including the tests, the documentation and the time to
-learn argparse_. At the moment I have already written some powerful
-extensions for plac_ and I plan to release them in the 0.5 release, once
-I finish writing `advanced usage document`_ .
-The extensions will be released together with plac_ but in a separated
-module.
+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``).
Trivia: the story behind the name
-----------------------------------------
@@ -601,6 +667,10 @@ 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 26d6cdf..d9f73b9 100644
--- a/plac/doc/plac_adv.html
+++ b/plac/doc/plac_adv.html
@@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" />
+<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title>Testing and scripting your applications with plac</title>
<style type="text/css">
@@ -414,48 +414,87 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<div class="document" id="testing-and-scripting-your-applications-with-plac">
<h1 class="title">Testing and scripting your applications with plac</h1>
+<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>
+</ul>
+</div>
<div class="section" id="introduction">
-<h1>Introduction</h1>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> has been designed to be simple to use for simple stuff, but in
-truth it is a quite advanced tool with a field of applicability
-which far outreaches the specific domain of command-line arguments parsers.
-In reality <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is a generic tool to write domain specific
-languages (DSL).
-This document explains how you can use plac to test your application, and
-how you can use it to provide a scripting interface to your application.
-Notice that your application does not need to be a command-line
-application: you can use <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> whenever you have an API with strings
-in input and strings in output.</p>
+<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>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
+can test your application interactively as well as with batch
+scripts, and even with the analogous of Python doctests for your
+defined language.</p>
+<p>You can easily replace the <tt class="docutils literal">cmd</tt> module of the standard library and
+you could easily write an application like <a class="reference external" href="http://twill.idyll.org/">twill</a> with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Or you
+could use it to script your building procedure. Or any other thing,
+your imagination is the only limit!</p>
</div>
-<div class="section" id="testing-applications-with-plac">
-<h1>Testing applications with plac</h1>
-<p>In the standard usage, <tt class="docutils literal"><span class="pre">plac.call</span></tt> is called only once on the main
-function; however in the tests it quite natural to invoke <tt class="docutils literal"><span class="pre">plac.call</span></tt>
-multiple times on the same function with different arguments.
-For instance, suppose you want to store the configuration of
-your application into a Python shelve; then, you may want to write
-a command-line tool to edit your configuration, i.e. a shelve
-interface. A possible implementation could be the following:</p>
-<pre class="literal-block">
-import shelve
+<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'),
- all=('show all parameters in the shelve', '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 ishelve(help, all, clear, delete, filename='conf.shelve',
+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 help:
- yield 'Special commands:'
- yield 'help, all, clear, delete'
- elif all:
+ 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:
@@ -479,72 +518,143 @@ def ishelve(help, all, clear, delete, filename='conf.shelve',
finally:
sh.close()
-ishelve.add_help = False # there is a custom help
+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(ishelve):
+ for output in plac.call(main):
print(output)
</pre>
-<p>You can write the tests for such implementation as follows:</p>
+<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">
-import plac
-from ishelve import ishelve
-
-def test():
- assert plac.call(ishelve, []) == []
- assert plac.call(ishelve, ['--clear']) == ['cleared the shelve']
- assert plac.call(ishelve, ['a=1']) == ['setting a=1']
- assert plac.call(ishelve, ['a']) == ['1']
- assert plac.call(ishelve, ['--delete=a']) == ['deleted a']
- assert plac.call(ishelve, ['a']) == ['a: not found']
-
-
+$ 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>
-<p>There is a small optimization here: once <tt class="docutils literal"><span class="pre">plac.call(func)</span></tt>
-has been called, a <tt class="docutils literal"><span class="pre">.p</span></tt> attribute is attached to <tt class="docutils literal"><span class="pre">func</span></tt>, containing
-the parser associated to the function annotations. The second time
-<tt class="docutils literal"><span class="pre">plac.call(func)</span></tt> is invoked, the parser is re-used.</p>
+<p>Command-line scripts have many advantages, but are no substitute
+for a real interactive application.</p>
</div>
-<div class="section" id="writing-command-line-interpreters-with-plac">
-<h1>Writing command-line interpreters with plac</h1>
-<p>Apart from testing, there is another typical use case where <tt class="docutils literal"><span class="pre">plac.call</span></tt>
-is invoked multiple times, in the implementation of command interpreters.
-For instance, you could define an interative interpreter on top
-of <tt class="docutils literal"><span class="pre">ishelve</span></tt> as follows:</p>
+<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
+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
+follows:</p>
<pre class="literal-block">
-import plac
-from ishelve import ishelve
-
-ishelve.prefix_chars = '.'
-ishelve.add_help = False
+import plac, ishelve
&#64;plac.annotations(
- interactive=('start interactive interface', 'flag'))
-def main(interactive, *args):
+ interactive=('start interactive interface', 'flag'),
+ subcommands='the commands of the underlying ishelve interpreter')
+def main(interactive, *subcommands):
+ &quot;&quot;&quot;
+ This script works both interactively and non-interactively.
+ Use .help to see the internal commands.
+ &quot;&quot;&quot;
if interactive:
- import shlex
- while True:
- try:
- line = raw_input('i&gt; ')
- except EOFError:
- break
- cmd = shlex.split(line)
- for out in plac.call(ishelve, cmd):
- print(out)
+ plac.Interpreter(ishelve.main).interact()
else:
- plac.call(ishelve, args)
+ for out in plac.call(ishelve.main, subcommands):
+ print(out)
if __name__ == '__main__':
plac.call(main)
</pre>
-<p>Here is an usage session, usinng <tt class="docutils literal"><span class="pre">rlwrap</span></tt> to enable readline features:</p>
+<p>A trick has been used here: the ishelve command-line interface has been
+hidden inside and 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">
-$ rlwrap python shelve_interpreter.py -i
+usage: shelve_interpreter.py [-h] [-interactive]
+ [subcommands [subcommands ...]]
+
+This script works both interactively and non-interactively. Use .help to see
+the internal commands.
-i&gt; ..clear
+positional arguments:
+ subcommands the commands of the underlying ishelve interpreter
+
+optional arguments:
+ -h, --help show this help message and exit
+ -interactive start interactive interface
+
+</pre>
+<p>Thanks to this ingenuous trick, the script can be run both interactively
+and non-interactively:</p>
+<pre class="literal-block">
+$ 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>
+<pre class="literal-block">
+$ rlwrap python shelve_interpreter.py -i # interactive use
+usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]
+ [.filename /home/micheles/conf.shelve]
+ [params [params ...]] [setters [setters ...]]
i&gt; a=1
setting a=1
i&gt; a
@@ -554,114 +664,315 @@ setting b=2
i&gt; a b
1
2
-i&gt; ..delete a
+i&gt; .del a
deleted a
i&gt; a
a: not found
-i&gt; ..all
+i&gt; .show
b=2
i&gt; [CTRL-D]
</pre>
-<p>As you see, it is possibly to write command interpreters directly on top of
-<tt class="docutils literal"><span class="pre">plac.call</span></tt> and it is not particularly difficult. However, the devil
-is in the details (I mean error management) and my recommendation, if
-you want to implement an interpreter of commands, is to use the
-class <tt class="docutils literal"><span class="pre">plac.Interpreter</span></tt> which is especially suited for this
-task. <tt class="docutils literal"><span class="pre">plac.Interpreter</span></tt> is available only if you are using a recent
+<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>
+<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
-to be used with the <tt class="docutils literal"><span class="pre">with</span></tt> statement. The only important method
-of <tt class="docutils literal"><span class="pre">plac.Interpreter</span></tt> is the <tt class="docutils literal"><span class="pre">.send</span></tt> method, which takes a
-string in input and returns a string in output. Internally the input string
-is splitted with <tt class="docutils literal"><span class="pre">shlex.split</span></tt> and passed to <tt class="docutils literal"><span class="pre">plac.call</span></tt>,
-with some trick to manage exceptions correctly. Moreover long options
-are managed with a single prefix character.</p>
-<pre class="literal-block">
-&quot;&quot;&quot;Call this script with rlwrap and you will be happy&quot;&quot;&quot;
-from __future__ import with_statement
-from plac_shell import Interpreter
-from shelve_interface import interpreter
-
-if __name__ == '__main__':
- with Interpreter(interpreter) as i:
- while True:
- try:
- line = raw_input('i&gt; ')
- except EOFError:
- break
- print(i.send(line))
-
+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="multi-parsers">
-<h1>Multi-parsers</h1>
-<p>As we saw, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to infer an arguments parser from the
-signature of a function. In addition, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is also able to infer a
-multi-parser from a container of commands, by inferring the subparsers
-from the commands. That is useful if you want to implement
-<em>subcommands</em> (a familiar example of a command-line application
-featuring subcommands is subversion).</p>
-<p>A container of commands is any object with a <tt class="docutils literal"><span class="pre">.commands</span></tt> attribute
-listing a set of functions or methods which are valid commands. In
-particular, a Python module is a perfect container of commands. As an
-example, consider the following module implementing a fake Version
-Control System:</p>
+<div class="section" id="testing-a-plac-application">
+<h1><a class="toc-backref" href="#id4">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">
-&quot;A Fake Version Control System&quot;
-
+# test_ishelve.py
import plac
+from ishelve import ishelve
-commands = 'checkout', 'commit', 'status'
-
-&#64;plac.annotations(
- url=('url of the source code', 'positional'))
-def checkout(url):
- return ('checkout ', url)
+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']
-&#64;plac.annotations(
- message=('commit message', 'option'))
-def commit(message):
- return ('commit ', message)
+if __name__ == '__main__':
+ test()
-&#64;plac.annotations(quiet=('summary information', 'flag'))
-def status(quiet):
- return ('status ', quiet)
+</pre>
+<p>However, using <tt class="docutils literal">plac.call</tt> is not especially nice. The big
+issue is that <tt class="docutils literal">plac.call</tt> responds to invalid input by printing an
+error message on stderr and by raising a <tt class="docutils literal">SystemExit</tt>: this is
+certainly not a nice thing to do in a test.</p>
+<p>As a consequence of this behavior it is impossible to test for invalid
+commands, unless you wrap the <tt class="docutils literal">SystemExit</tt> exception by
+hand each time (a possibly you do something with the error message in
+stderr too). Luckily, <tt class="docutils literal">plac</tt> offers a better testing support through
+the <tt class="docutils literal">check</tt> method of <tt class="docutils literal">Interpreter</tt> objects:</p>
+<pre class="literal-block">
+# test_ishelve2.py
+from __future__ import with_statement
+import plac, ishelve
-if __name__ == '__main__':
- import __main__
- print(plac.call(__main__))
+def test():
+ with plac.Interpreter(ishelve.main) as i:
+ i.check('.clear', 'cleared the shelve')
+ i.check('a=1', 'setting a=1')
+ i.check('a', '1')
+ i.check('.delete=a', 'deleted a')
+ i.check('a', 'a: not found')
</pre>
-<p>Here is the usage message:</p>
+<p>The method <tt class="docutils literal">.check(given_input, expected_output)</tt> works on strings
+and raises an <tt class="docutils literal">AssertionError</tt> if the output produced by the
+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>
+<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
+something like <tt class="docutils literal">plac.call</tt>, but it wraps all exceptions, so that <tt class="docutils literal">i.check</tt>
+is guaranteed not to raise any exception except <tt class="docutils literal">AssertionError</tt>.</p>
+<p>Even the <tt class="docutils literal">SystemExit</tt> exception is captured and you can write your test as</p>
+<blockquote>
+<tt class="docutils literal"><span class="pre">i.check('-cler',</span> 'SystemExit: unrecognized arguments: <span class="pre">-cler')</span></tt></blockquote>
+<p>without risk of exiting from the Python interpreter.</p>
+<p>There is a second advantage of interpreters: if the main function contains some
+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>
+</div>
+<div class="section" id="plac-easytests">
+<h1><a class="toc-backref" href="#id5">Plac easytests</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>
+<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>
<pre class="literal-block">
-usage: vcs.py [-h] {status,commit,checkout} ...
+#!ishelve.py
+i&gt; .clear # start from a clean state
+cleared the shelve
+i&gt; a=1
+setting a=1
+i&gt; a
+1
+i&gt; .del a
+deleted a
+i&gt; a
+a: not found
+i&gt; .cler # spelling error
+SystemExit: unrecognized arguments: .cler
-A Fake Version Control System
+</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>
+<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
-optional arguments:
- -h, --help show this help message and exit
+</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
+easy tests supported by <tt class="docutils literal">plac</tt> are <em>not</em> unittests: they should be
+used to model user interaction when the order of the operations
+matters. Since the single subtests in a <tt class="docutils literal">.placet</tt> file are not
+independent, it makes sense to exit immediately at the first failure.</p>
+<p>The support for doctests in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> comes nearly for free, thanks to the <a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a>
+module in the standard library, which is able to parse simple
+languages as the ones you can implement with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. In particular,
+thanks to <a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a>, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize comments (the default
+comment character is <tt class="docutils literal">#</tt>), continuation lines, escape sequences and
+more. Look at the <a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a> documentation if you need to customize how
+the language is interpreted.</p>
+<p>In addition, I have implemented from scratch some support for line number
+recognition, so that if a test fail you get the line number of the
+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
+testing tools. For instance, you can integrate your doctests with nose
+or py.test as follow:</p>
+<pre class="literal-block">
+import os, 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:]
-subcommands:
- {status,commit,checkout}
- -h to get additional help
+</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
+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
+a string containing directory names separated by colons). If the variable
+<tt class="docutils literal">PLACPATH</tt> is not defined, it just looks in the current directory.
+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>
+<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>
+<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">
+#!ishelve.py
+.clear
+a=1 b=2
+.show
+.del a
+.dl b
+.show
</pre>
-<p>If the commands are completely independent, a module is a good fit for
-a method container. In other situations, it is best to use a custom
-class. For instance, suppose you want to store the configuration of
-your application into a Python shelve; then, you may want to write
-a command-line tool to edit your configuration, i.e. a shelve
-interface:</p>
+<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>
<pre class="literal-block">
-import shelve
+$ plac --batch --verbose ishelve.batch
+i&gt; .clear
+cleared the shelve
+i&gt; a=1 b=2
+setting a=1
+setting b=2
+i&gt; .show
+b=2
+a=1
+i&gt; .del a
+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
+(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>
+<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
+application featuring subcommands is subversion).</p>
+<p>Technically a container of commands is any object with a <tt class="docutils literal">.commands</tt> attribute
+listing a set of functions or methods which are valid commands. A command
+container may have initialization/finalization hooks (<tt class="docutils literal">__enter__/__exit__</tt>)
+and dispatch hooks (<tt class="docutils literal">__missing__</tt>, invoked for invalid command names).</p>
+<p>Using this feature the shelve interface can be rewritten in a more
+object-oriented way as follows:</p>
+<pre class="literal-block">
+# ishelve2.py
+import shelve, os, sys
import plac
# error checking is missing: this is left to the reader
class ShelveInterface(object):
&quot;A minimal interface over a shelve object&quot;
- commands = 'set', 'show', 'show_all', 'delete'
+ commands = 'set', 'show', 'showall', 'delete'
def __init__(self, fname):
self.fname = fname
- self.sh = shelve.open(fname)
+ def __enter__(self):
+ self.sh = shelve.open(self.fname)
+ return self
def set(self, name, value):
&quot;set name value&quot;
yield 'setting %s=%s' % (name, value)
@@ -669,11 +980,11 @@ class ShelveInterface(object):
def show(self, *names):
&quot;show given parameters&quot;
for name in names:
- yield '%s = %s\n' % (name, self.sh[name])
- def show_all(self):
+ yield '%s = %s' % (name, self.sh[name])
+ def showall(self):
&quot;show all parameters&quot;
for name in self.sh:
- yield '%s = %s\n' % (name, self.sh[name])
+ yield '%s = %s' % (name, self.sh[name])
def delete(self, name=None):
&quot;delete given parameter (or everything)&quot;
if name is None:
@@ -682,73 +993,130 @@ class ShelveInterface(object):
else:
yield 'deleting %s' % name
del self.sh[name]
+ def __exit__(self, etype, exc, tb):
+ self.sh.close()
+
+main = ShelveInterface(os.path.expanduser('~/conf.shelve'))
if __name__ == '__main__':
- interface = ShelveInterface('conf.shelve')
- try:
- for output in plac.call(interface):
- print(output)
- finally:
- interface.sh.close()
+ for output in plac.call(main):
+ print(output)
</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>Here is a session of usage on an Unix-like operating system:</p>
<pre class="literal-block">
-$ alias conf=&quot;python shelve_interface.py&quot;
+$ alias conf=&quot;python ishelve2.py&quot;
$ conf set a pippo
setting a=pippo
$ conf set b lippo
setting b=lippo
-$ conf show_all
+$ conf showall
b = lippo
a = pippo
$ conf show a b
a = pippo
b = lippo
-$ conf delete a
+$ conf del a # an abbreviation
deleting a
-$ conf show_all
+$ conf 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>Technically a multi-parser is a parser object with an attribute <tt class="docutils literal"><span class="pre">.subp</span></tt>
-which is a dictionary of subparsers; each of the methods listed in
-the attribute <tt class="docutils literal"><span class="pre">.commands</span></tt> corresponds to a subparser inferred from
-the method signature. The original object gets a <tt class="docutils literal"><span class="pre">.p</span></tt> attribute
-containing the main parser which is associated to an internal function
-which dispatches on the right method depending on the method name.</p>
-<p>Here is the usage message:</p>
+<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
+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>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>
+(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>
+<pre class="literal-block">
+$ plac ishelve2.py -i
+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="a-non-class-based-example">
+<h1><a class="toc-backref" href="#id8">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
+example, consider the following module implementing a fake Version
+Control System:</p>
<pre class="literal-block">
+&quot;A Fake Version Control System&quot;
+
import plac
-class FVCS(object):
- &quot;A Fake Version Control System&quot;
- commands = 'checkout', 'commit', 'status', 'help'
+commands = 'checkout', 'commit', 'status'
- &#64;plac.annotations(
- name=('a recognized command', 'positional', None, str, commands))
- def help(self, name):
- self.p.subp[name].print_help()
+&#64;plac.annotations(url='url of the source code')
+def checkout(url):
+ &quot;A fake checkout command&quot;
+ return ('checkout ', url)
- &#64;plac.annotations(
- url=('url of the source code', 'positional'))
- def checkout(self, url):
- print('checkout', url)
+&#64;plac.annotations(message=('commit message', 'option'))
+def commit(message):
+ &quot;A fake commit command&quot;
+ return ('commit ', message)
- def commit(self):
- print('commit')
+&#64;plac.annotations(quiet=('summary information', 'flag', 'q'))
+def status(quiet):
+ &quot;A fake status command&quot;
+ return ('status ', quiet)
- &#64;plac.annotations(quiet=('summary information', 'flag'))
- def status(self, quiet):
- print('status', quiet)
+def __missing__(name):
+ return 'Command %r does not exist' % name
-main = FVCS()
+def __exit__(etype, exc, tb):
+ &quot;Will be called automatically at the end of the call/cmdloop&quot;
+ if etype in (None, GeneratorExit): # success
+ print('ok')
-if __name__ == '__main__':
- plac.call(main)
+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>
+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>
+<p>The <tt class="docutils literal">vcs</tt> module does not contain an <tt class="docutils literal">if __name__ == '__main__'</tt>
+block, but you can still run it through the plac runner
+(try <tt class="docutils literal">plac vcs.py <span class="pre">-h</span></tt>):</p>
<pre class="literal-block">
-usage: example13.py [-h] {status,commit,checkout,help} ...
+usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
A Fake Version Control System
@@ -756,98 +1124,171 @@ optional arguments:
-h, --help show this help message and exit
subcommands:
- {status,commit,checkout,help}
+ {status,commit,checkout}
-h to get additional help
</pre>
-</div>
-<div class="section" id="advanced-usage">
-<h1>Advanced usage</h1>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> relies on a <a class="reference external" href="http://argparse.googlecode.com">argparse</a> for all of the heavy lifting work and it is
-possible to leverage on <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features directly or indirectly.</p>
-<p>For instance, you can make invisible an argument in the usage message
-simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or
-<tt class="docutils literal"><span class="pre">argparse.SUPPRESS</span></tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a>
-directly.</p>
-<p>It is also possible to pass options to the underlying
-<tt class="docutils literal"><span class="pre">argparse.ArgumentParser</span></tt> object (currently it accepts the default
-arguments <tt class="docutils literal"><span class="pre">description</span></tt>, <tt class="docutils literal"><span class="pre">epilog</span></tt>, <tt class="docutils literal"><span class="pre">prog</span></tt>, <tt class="docutils literal"><span class="pre">usage</span></tt>,
-<tt class="docutils literal"><span class="pre">add_help</span></tt>, <tt class="docutils literal"><span class="pre">argument_default</span></tt>, <tt class="docutils literal"><span class="pre">parents</span></tt>, <tt class="docutils literal"><span class="pre">prefix_chars</span></tt>,
-<tt class="docutils literal"><span class="pre">fromfile_prefix_chars</span></tt>, <tt class="docutils literal"><span class="pre">conflict_handler</span></tt>, <tt class="docutils literal"><span class="pre">formatter_class</span></tt>).
-It is enough to set such attributes on the <tt class="docutils literal"><span class="pre">main</span></tt> function. For
-instance</p>
-<pre class="literal-block">
-def main(...):
- pass
-
-main.add_help = False
-</pre>
-<p>disable the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This is not
-particularly elegant, but I assume the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be
-happy with the defaults and would not want to change them; still it is
-possible if she wants to. For instance, by setting the <tt class="docutils literal"><span class="pre">description</span></tt>
-attribute, it is possible to add a comment to the usage message (by
-default the docstring of the <tt class="docutils literal"><span class="pre">main</span></tt> function is used as
-description). It is also possible to change the option prefix; for
-instance if your script must run under Windows and you want to use &quot;/&quot;
-as option prefix you can add the line:</p>
-<pre class="literal-block">
-main.prefix_chars='/-'
-</pre>
-<p><tt class="docutils literal"><span class="pre">prefix_chars</span></tt> is an <a class="reference external" href="http://argparse.googlecode.com">argparse</a> feature. The first prefix char (<tt class="docutils literal"><span class="pre">/</span></tt>) is used
-as the default in the construction of both short and long options;
-the second prefix char (<tt class="docutils literal"><span class="pre">-</span></tt>) is kept to keep the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> option
-working: however you can disable it and reimplement it if you like.
-For instance, here is how you could reimplement the <tt class="docutils literal"><span class="pre">help</span></tt> command
-in the Fake VCS example:</p>
+<p>You can get help for the subcommands by postponing <tt class="docutils literal"><span class="pre">-h</span></tt> after the
+name of the command:</p>
<pre class="literal-block">
-import plac
-from example13 import FVCS
-
-class VCS_with_help(FVCS):
- commands = FVCS.commands + ('help',)
-
- &#64;plac.annotations(
- name=('a recognized command', 'positional', None, str, commands))
- def help(self, name):
- self.p.subp[name].print_help()
+$ plac vcs.py status -h
+usage: vcs.py status [-h] [-q]
-main = VCS_with_help()
-
-if __name__ == '__main__':
- plac.call(main)
+A fake status command
+optional arguments:
+ -h, --help show this help message and exit
+ -q, --quiet summary information
</pre>
-<p>Internally <tt class="docutils literal"><span class="pre">plac.call</span></tt> uses
-<tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> and adds the parser as an attribute <tt class="docutils literal"><span class="pre">.p</span></tt>.
-This also happers for multiparsers and you can take advantage of
-the <tt class="docutils literal"><span class="pre">.p</span></tt> attribute to invoke <tt class="docutils literal"><span class="pre">argparse.ArgumentParser</span></tt> methods.</p>
-<p>Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> to
-understand the meaning of the other options. If there is a set of
-options that you use very often, you may consider writing a decorator
-adding such options to the <tt class="docutils literal"><span class="pre">main</span></tt> function for you. For simplicity,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic of that kind.</p>
-<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
-invoking the <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> utility function:</p>
+<p>Notice how the docstring of the command is automatically shown in
+usage message, as well as the documentation for the sub flag <tt class="docutils literal"><span class="pre">-q</span></tt>.</p>
+<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>
+<p>and here is an interactive session:</p>
+<pre class="literal-block">
+$ plac -i vcs.py
+usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
+i&gt; check url
+checkout
+url
+i&gt; st -q
+status
+True
+i&gt; co
+commit
+None
+i&gt; sto
+Command 'sto' does not exist
+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>
+<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>
+<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
+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
+strings in input and returns a four-tuple with attributes
+<tt class="docutils literal">.str</tt>, <tt class="docutils literal">.etype</tt>, <tt class="docutils literal">.exc</tt> and <tt class="docutils literal">.tb</tt>:</p>
+<ul class="simple">
+<li><tt class="docutils literal">.str</tt> is the output of the command, if successful (a string);</li>
+<li><tt class="docutils literal">.etype</tt> is the class of the exception, if the command fail;</li>
+<li><tt class="docutils literal">.exc</tt> is the exception instance;</li>
+<li><tt class="docutils literal">.tb</tt> is the traceback.</li>
+</ul>
+<p>Moreover the <tt class="docutils literal">__str__</tt> representation of the output object is redefined
+to return the output string if the command was successful or the error
+message if the command failed (actually it returns the error message
+preceded by the name of the exception class).</p>
+<p>For instance, if you send a mispelled option to
+the interpreter a <tt class="docutils literal">SystemExit</tt> will be trapped:</p>
<pre class="doctest-block">
&gt;&gt;&gt; import plac
-&gt;&gt;&gt; def main(arg):
-... pass
+&gt;&gt;&gt; from ishelve import ishelve
+&gt;&gt;&gt; with plac.Interpreter(ishelve) as i:
+... print(i.send('.cler'))
...
-&gt;&gt;&gt; print plac.parser_from(main)
-ArgumentParser(prog='', usage=None, description=None, version=None,
-formatter_class=&lt;class 'argparse.HelpFormatter'&gt;, conflict_handler='error',
-add_help=True)
-</pre>
-<p>I use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> in the unit tests of the module, but regular
-users should never need to use it, since the parser is also available
-as an attribute of the main function.</p>
+SystemExit: unrecognized arguments: .cler
+</pre>
+<p>It is important to invoke the <tt class="docutils literal">.send</tt> method inside the context manager,
+otherwise you will get a <tt class="docutils literal">RuntimeError</tt>.</p>
+<p>For instance, suppose you want to implement a graphical runner for a
+plac-based interpreter with two text widgets: one to enter the commands
+and one to display the results. Suppose you want to display the errors
+with tracebacks in red. You will need to code something like that
+(pseudocode follows):</p>
+<pre class="literal-block">
+input_widget = WidgetReadingInput()
+output_widget = WidgetDisplayingOutput()
+
+def send(interpreter, line):
+ out = interpreter.send(line)
+ if out.tb: # there was an error
+ output_widget.display(out.tb, color='red')
+ else:
+ output_widget.display(out.str)
+
+main = plac.import_main(tool_path) # get the main object
+
+with plac.Interpreter(main) as i:
+ def callback(event):
+ if event.user_pressed_ENTER():
+ send(i, input_widget.last_line)
+ input_widget.addcallback(callback)
+ gui_mainloop.start()
+</pre>
+<p>You can adapt the pseudocode to your GUI toolkit of choice and you can
+also change the file associations in such a way that clicking on a
+plac tool file the graphical user interface starts.</p>
+<p>There is a final <em>caveat</em>: since the plac interpreter loop is
+implemented via extended generators, plac interpreters are single threaded: you
+will get an error if you <tt class="docutils literal">.send</tt> commands from separated threads.
+You can circumvent the problem by using a queue. If EXIT is a sentinel
+value to signal exiting from the interpreter look, you can write code
+like this:</p>
+<pre class="literal-block">
+with interpreter:
+ for input_value in iter(input_queue.get, EXIT):
+ output_queue.put(interpreter.send(input_value))
+</pre>
+<p>The same trick also work for processes; you could run the interpreter
+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>
+<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
+rules are quite simple:</p>
+<ol class="arabic simple">
+<li>if you want to implement a command-line script, use <tt class="docutils literal">plac.call</tt>;</li>
+<li>if you want to implement a command interpreter, use <tt class="docutils literal">plac.Interpreter</tt>:<ul>
+<li>for an interactive interpreter, call the <tt class="docutils literal">.interact</tt> method;</li>
+<li>for an batch interpreter, call the <tt class="docutils literal">.execute</tt> method;</li>
+</ul>
+</li>
+<li>for testing call the <tt class="docutils literal">Interpreter.check</tt> method in the appropriate context
+or use the <tt class="docutils literal">Interpreter.doctest</tt> feature;</li>
+<li>if you need to go at a lower level, you may need to call the
+<tt class="docutils literal">Interpreter.send</tt> method.</li>
+</ol>
+<p>Moreover, remember that <tt class="docutils literal">plac_runner.py</tt> is your friend.</p>
</div>
-<div class="section" id="custom-annotation-objects">
-<h1>Custom annotation objects</h1>
-<p>Internally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses an <tt class="docutils literal"><span class="pre">Annotation</span></tt> class to convert the tuples
+<div class="section" id="appendix-custom-annotation-objects">
+<h1><a class="toc-backref" href="#id11">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"><span class="pre">help,</span> <span class="pre">kind,</span> <span class="pre">short,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar</span></tt>.</p>
+six attributes <tt class="docutils literal">help, kind, short, type, choices, metavar</tt>.</p>
<p>Advanced users can implement their own annotation objects.
For instance, here is an example of how you could implement annotations for
positional arguments:</p>
@@ -893,7 +1334,7 @@ optional arguments:
-h, --help show this help message and exit
</pre>
-<p>You can go on and define <tt class="docutils literal"><span class="pre">Option</span></tt> and <tt class="docutils literal"><span class="pre">Flag</span></tt> classes, if you like.
+<p>You can go on and define <tt class="docutils literal">Option</tt> and <tt class="docutils literal">Flag</tt> classes, if you like.
Using custom annotation objects you could do advanced things like extracting the
annotations from a configuration file or from a database, but I expect such
use cases to be quite rare: the default mechanism should work
diff --git a/plac/doc/plac_adv.pdf b/plac/doc/plac_adv.pdf
index d0b207f..1eb01a8 100644
--- a/plac/doc/plac_adv.pdf
+++ b/plac/doc/plac_adv.pdf
@@ -5,8 +5,9 @@
% The standard fonts dictionary
<< /F1 2 0 R
/F2 3 0 R
- /F3 7 0 R
- /F4 13 0 R >>
+ /F3 4 0 R
+ /F4 32 0 R
+ /F5 37 0 R >>
endobj
% 'F1': class PDFType1Font
2 0 obj
@@ -26,8 +27,413 @@ endobj
/Subtype /Type1
/Type /Font >>
endobj
-% 'Annot.NUMBER1': class PDFDictionary
+% 'F3': class PDFType1Font
4 0 obj
+% Font Times-Roman
+<< /BaseFont /Times-Roman
+ /Encoding /WinAnsiEncoding
+ /Name /F3
+ /Subtype /Type1
+ /Type /Font >>
+endobj
+% 'Annot.NUMBER1': class LinkAnnotation
+5 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 492.0236
+ 0 ]
+ /Rect [ 62.69291
+ 690.5936
+ 121.0229
+ 702.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER2': class LinkAnnotation
+6 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 492.0236
+ 0 ]
+ /Rect [ 527.0227
+ 690.5936
+ 532.5827
+ 702.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER3': class LinkAnnotation
+7 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 207.0236
+ 0 ]
+ /Rect [ 62.69291
+ 672.5936
+ 237.2129
+ 684.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER4': class LinkAnnotation
+8 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 41 0 R
+ /XYZ
+ 62.69291
+ 207.0236
+ 0 ]
+ /Rect [ 527.0227
+ 672.5936
+ 532.5827
+ 684.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER5': class LinkAnnotation
+9 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 46 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Rect [ 62.69291
+ 654.5936
+ 282.1929
+ 666.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER6': class LinkAnnotation
+10 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 46 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Rect [ 527.0227
+ 654.5936
+ 532.5827
+ 666.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER7': class LinkAnnotation
+11 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 47 0 R
+ /XYZ
+ 62.69291
+ 168.6236
+ 0 ]
+ /Rect [ 62.69291
+ 636.5936
+ 184.9529
+ 648.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER8': class LinkAnnotation
+12 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 47 0 R
+ /XYZ
+ 62.69291
+ 168.6236
+ 0 ]
+ /Rect [ 527.0227
+ 636.5936
+ 532.5827
+ 648.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER9': class LinkAnnotation
+13 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 52 0 R
+ /XYZ
+ 62.69291
+ 741.0236
+ 0 ]
+ /Rect [ 62.69291
+ 618.5936
+ 131.6229
+ 630.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER10': class LinkAnnotation
+14 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 52 0 R
+ /XYZ
+ 62.69291
+ 741.0236
+ 0 ]
+ /Rect [ 527.0227
+ 618.5936
+ 532.5827
+ 630.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER11': class LinkAnnotation
+15 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 60 0 R
+ /XYZ
+ 62.69291
+ 170.8826
+ 0 ]
+ /Rect [ 62.69291
+ 600.5936
+ 148.2829
+ 612.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER12': class LinkAnnotation
+16 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 60 0 R
+ /XYZ
+ 62.69291
+ 170.8826
+ 0 ]
+ /Rect [ 527.0227
+ 600.5936
+ 532.5827
+ 612.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER13': class LinkAnnotation
+17 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 64 0 R
+ /XYZ
+ 62.69291
+ 271.4236
+ 0 ]
+ /Rect [ 62.69291
+ 582.5936
+ 164.9329
+ 594.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER14': class LinkAnnotation
+18 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 64 0 R
+ /XYZ
+ 62.69291
+ 271.4236
+ 0 ]
+ /Rect [ 527.0227
+ 582.5936
+ 532.5827
+ 594.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER15': class LinkAnnotation
+19 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 69 0 R
+ /XYZ
+ 62.69291
+ 240.8235
+ 0 ]
+ /Rect [ 62.69291
+ 564.5936
+ 193.8529
+ 576.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER16': class LinkAnnotation
+20 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 69 0 R
+ /XYZ
+ 62.69291
+ 240.8235
+ 0 ]
+ /Rect [ 521.4627
+ 564.5936
+ 532.5827
+ 576.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER17': class LinkAnnotation
+21 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 73 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Rect [ 62.69291
+ 546.5936
+ 201.6029
+ 558.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER18': class LinkAnnotation
+22 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 73 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Rect [ 521.4627
+ 546.5936
+ 532.5827
+ 558.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER19': class LinkAnnotation
+23 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 77 0 R
+ /XYZ
+ 62.69291
+ 615.8236
+ 0 ]
+ /Rect [ 62.69291
+ 528.5936
+ 108.2629
+ 540.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER20': class LinkAnnotation
+24 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 77 0 R
+ /XYZ
+ 62.69291
+ 615.8236
+ 0 ]
+ /Rect [ 521.4627
+ 528.5936
+ 532.5827
+ 540.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER21': class LinkAnnotation
+25 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 77 0 R
+ /XYZ
+ 62.69291
+ 390.8236
+ 0 ]
+ /Rect [ 62.69291
+ 510.5936
+ 241.6029
+ 522.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER22': class LinkAnnotation
+26 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 77 0 R
+ /XYZ
+ 62.69291
+ 390.8236
+ 0 ]
+ /Rect [ 521.4627
+ 510.5936
+ 532.5827
+ 522.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) >>
@@ -35,63 +441,238 @@ endobj
0
0 ]
/Rect [ 62.69291
- 693.5936
- 84.3779
- 705.5936 ]
+ 456.5936
+ 83.9408
+ 468.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER2': class PDFDictionary
-5 0 obj
+% 'Annot.NUMBER24': class PDFDictionary
+28 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 93.0279
- 669.5936
- 115.0329
- 681.5936 ]
+ /Rect [ 364.4907
+ 414.5936
+ 382.8307
+ 426.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER3': class PDFDictionary
-6 0 obj
+% '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
+ 0 ]
+ /Rect [ 146.0829
+ 402.5936
+ 237.7929
+ 414.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
0
0 ]
- /Rect [ 62.69291
- 633.5936
- 83.81291
- 645.5936 ]
+ /Rect [ 160.8629
+ 384.5936
+ 184.1608
+ 396.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'F3': class PDFType1Font
-7 0 obj
+% 'Annot.NUMBER27': class PDFDictionary
+31 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 121.3434
+ 354.5936
+ 142.5569
+ 366.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
+<< /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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER29': class PDFDictionary
+34 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER30': class PDFDictionary
+35 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER31': class PDFDictionary
+36 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 248.4095
+ 276.5936
+ 270.8808
+ 288.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'F5': class PDFType1Font
+37 0 obj
% Font Courier
<< /BaseFont /Courier
/Encoding /WinAnsiEncoding
- /Name /F3
+ /Name /F5
/Subtype /Type1
/Type /Font >>
endobj
+% 'Annot.NUMBER32': class PDFDictionary
+38 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://twill.idyll.org/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 83.20457
+ 234.5936
+ 105.3762
+ 246.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER33': class PDFDictionary
+39 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 128.6679
+ 234.5936
+ 147.0079
+ 246.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER34': class PDFDictionary
+40 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
% 'Page1': class PDFPage
-8 0 obj
+41 0 obj
% Page dictionary
-<< /Annots [ 4 0 R
- 5 0 R
- 6 0 R ]
- /Contents 40 0 R
+<< /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
+ 13 0 R
+ 14 0 R
+ 15 0 R
+ 16 0 R
+ 17 0 R
+ 18 0 R
+ 19 0 R
+ 20 0 R
+ 21 0 R
+ 22 0 R
+ 23 0 R
+ 24 0 R
+ 25 0 R
+ 26 0 R
+ 27 0 R
+ 28 0 R
+ 29 0 R
+ 30 0 R
+ 31 0 R
+ 33 0 R
+ 34 0 R
+ 35 0 R
+ 36 0 R
+ 38 0 R
+ 39 0 R
+ 40 0 R ]
+ /Contents 94 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -103,14 +684,14 @@ endobj
/Type /Page >>
endobj
% 'Page2': class PDFPage
-9 0 obj
+42 0 obj
% Page dictionary
-<< /Contents 41 0 R
+<< /Contents 95 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -121,15 +702,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
+% 'Annot.NUMBER35': class PDFDictionary
+43 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 304.0655
+ 753.5936
+ 348.3808
+ 765.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER36': class PDFDictionary
+44 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 291.4339
+ 567.5936
+ 313.6065
+ 579.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
% 'Page3': class PDFPage
-10 0 obj
+45 0 obj
% Page dictionary
-<< /Contents 42 0 R
+<< /Annots [ 43 0 R
+ 44 0 R ]
+ /Contents 96 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -140,56 +753,69 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER4': class PDFDictionary
-11 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+% 'Page4': class PDFPage
+46 0 obj
+% Page dictionary
+<< /Contents 97 0 R
+ /MediaBox [ 0
0
- 0 ]
- /Rect [ 117.8279
- 729.5936
- 139.5429
- 741.5936 ]
- /Subtype /Link
- /Type /Annot >>
+ 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.NUMBER5': class PDFDictionary
-12 0 obj
+% 'Page5': class PDFPage
+47 0 obj
+% Page dictionary
+<< /Contents 98 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.NUMBER37': class PDFDictionary
+48 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 503.6477
- 729.5936
- 525.3627
- 741.5936 ]
+ /Rect [ 327.915
+ 262.1936
+ 351.1161
+ 274.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'F4': class PDFType1Font
-13 0 obj
-% Font Helvetica-Oblique
-<< /BaseFont /Helvetica-Oblique
- /Encoding /WinAnsiEncoding
- /Name /F4
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Page4': class PDFPage
-14 0 obj
+% 'Page6': class PDFPage
+49 0 obj
% Page dictionary
-<< /Annots [ 11 0 R
- 12 0 R ]
- /Contents 43 0 R
+<< /Annots [ 48 0 R ]
+ /Contents 99 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -200,15 +826,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page5': class PDFPage
-15 0 obj
+% 'Annot.NUMBER38': class PDFDictionary
+50 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 370.6785
+ 442.3936
+ 392.4956
+ 454.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER39': class PDFDictionary
+51 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 455.8742
+ 442.3936
+ 477.6913
+ 454.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page7': class PDFPage
+52 0 obj
% Page dictionary
-<< /Contents 44 0 R
+<< /Annots [ 50 0 R
+ 51 0 R ]
+ /Contents 100 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -219,63 +877,127 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER6': class PDFDictionary
-16 0 obj
+% 'Annot.NUMBER40': class PDFDictionary
+53 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 185.9351
+ 583.3936
+ 207.4695
+ 595.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER41': class PDFDictionary
+54 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/shlex.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 369.8905
+ 583.3936
+ 396.425
+ 595.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER42': class PDFDictionary
+55 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
+ /Rect [ 408.8916
+ 571.3936
+ 427.2316
+ 583.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER43': class PDFDictionary
+56 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/shlex.html) >>
+ /Border [ 0
+ 0
+ 0 ]
/Rect [ 62.69291
- 119.9936
- 83.9079
- 131.9936 ]
+ 559.3936
+ 86.03291
+ 571.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER7': class PDFDictionary
-17 0 obj
+% 'Annot.NUMBER44': class PDFDictionary
+57 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 133.1029
- 119.9936
- 175.4379
- 131.9936 ]
+ /Rect [ 91.65423
+ 559.3936
+ 112.8355
+ 571.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER8': class PDFDictionary
-18 0 obj
+% 'Annot.NUMBER45': class PDFDictionary
+58 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://docs.python.org/library/shlex.html) >>
/Border [ 0
0
0 ]
- /Rect [ 454.1177
- 119.9936
- 496.4527
- 131.9936 ]
+ /Rect [ 223.1366
+ 547.3936
+ 250.7972
+ 559.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page6': class PDFPage
-19 0 obj
+% 'Annot.NUMBER46': class PDFDictionary
+59 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page8': class PDFPage
+60 0 obj
% Page dictionary
-<< /Annots [ 16 0 R
- 17 0 R
- 18 0 R ]
- /Contents 45 0 R
+<< /Annots [ 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
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -286,111 +1008,149 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER9': class PDFDictionary
-20 0 obj
+% 'Annot.NUMBER47': class PDFDictionary
+61 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 455.2227
- 744.5936
- 534.3667
- 756.5936 ]
+ /Rect [ 302.1995
+ 687.3936
+ 325.1461
+ 699.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER10': class PDFDictionary
-21 0 obj
+% 'Annot.NUMBER48': class PDFDictionary
+62 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 127.99
- 577.3936
- 149.3857
- 589.3936 ]
+ /Rect [ 286.9966
+ 223.9936
+ 309.0542
+ 235.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER11': class PDFDictionary
-22 0 obj
+% 'Annot.NUMBER49': class PDFDictionary
+63 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 166.3664
- 476.1936
- 209.1976
- 488.1936 ]
+ /Rect [ 62.69291
+ 193.9936
+ 84.70395
+ 205.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER12': class PDFDictionary
-23 0 obj
+% 'Page9': class PDFPage
+64 0 obj
+% Page dictionary
+<< /Annots [ 61 0 R
+ 62 0 R
+ 63 0 R ]
+ /Contents 102 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.NUMBER50': class PDFDictionary
+65 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 307.9178
- 176.9936
- 351.5999
- 188.9936 ]
+ /Rect [ 110.3329
+ 159.3936
+ 132.2079
+ 171.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER13': class PDFDictionary
-24 0 obj
+% 'Annot.NUMBER51': class PDFDictionary
+66 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 317.2329
- 152.9936
- 338.3529
- 164.9936 ]
+ /Rect [ 489.0277
+ 159.3936
+ 510.9027
+ 171.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER14': class PDFDictionary
-25 0 obj
+% 'Page10': class PDFPage
+67 0 obj
+% Page dictionary
+<< /Annots [ 65 0 R
+ 66 0 R ]
+ /Contents 103 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.NUMBER52': class PDFDictionary
+68 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 327.2261
- 134.9936
- 410.5152
- 146.9936 ]
+ /Rect [ 62.69291
+ 205.3935
+ 84.72012
+ 217.3935 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page7': class PDFPage
-26 0 obj
+% 'Page11': class PDFPage
+69 0 obj
% Page dictionary
-<< /Annots [ 20 0 R
- 21 0 R
- 22 0 R
- 23 0 R
- 24 0 R
- 25 0 R ]
- /Contents 46 0 R
+<< /Annots [ 68 0 R ]
+ /Contents 104 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -401,8 +1161,111 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER15': class PDFDictionary
-27 0 obj
+% 'Page12': class PDFPage
+70 0 obj
+% Page dictionary
+<< /Contents 105 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
+% 'Page13': class PDFPage
+71 0 obj
+% Page dictionary
+<< /Contents 106 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.NUMBER53': class PDFDictionary
+72 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 182.479
+ 729.5936
+ 203.7662
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page14': class PDFPage
+73 0 obj
+% Page dictionary
+<< /Annots [ 72 0 R ]
+ /Contents 107 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.NUMBER54': class PDFDictionary
+74 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://docs.python.org/library/multiprocessing.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 295.0229
+ 631.3936
+ 367.2629
+ 643.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER55': class PDFDictionary
+75 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 91.57623
+ 580.3936
+ 114.8995
+ 592.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER56': class PDFDictionary
+76 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -410,22 +1273,24 @@ endobj
0
0 ]
/Rect [ 106.6216
- 576.3936
+ 355.3936
128.3202
- 588.3936 ]
+ 367.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page8': class PDFPage
-28 0 obj
+% 'Page15': class PDFPage
+77 0 obj
% Page dictionary
-<< /Annots [ 27 0 R ]
- /Contents 47 0 R
+<< /Annots [ 74 0 R
+ 75 0 R
+ 76 0 R ]
+ /Contents 108 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -436,15 +1301,15 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page9': class PDFPage
-29 0 obj
+% 'Page16': class PDFPage
+78 0 obj
% Page dictionary
-<< /Contents 48 0 R
+<< /Contents 109 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 39 0 R
+ /Parent 93 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -455,120 +1320,187 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R30': class PDFCatalog
-30 0 obj
+% 'R79': class PDFCatalog
+79 0 obj
% Document Root
-<< /Outlines 32 0 R
- /PageLabels 49 0 R
+<< /Outlines 81 0 R
+ /PageLabels 110 0 R
/PageMode /UseNone
- /Pages 39 0 R
+ /Pages 93 0 R
/Type /Catalog >>
endobj
-% 'R31': class PDFInfo
-31 0 obj
+% 'R80': class PDFInfo
+80 0 obj
<< /Author ()
- /CreationDate (D:20100611140831-01'00')
+ /CreationDate (D:20100618073441-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Testing and scripting your applications with plac) >>
endobj
-% 'R32': class PDFOutlines
-32 0 obj
-<< /Count 6
- /First 33 0 R
- /Last 38 0 R
+% 'R81': class PDFOutlines
+81 0 obj
+<< /Count 11
+ /First 82 0 R
+ /Last 92 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-33 0 obj
-<< /Dest [ 8 0 R
+82 0 obj
+<< /Dest [ 41 0 R
/XYZ
62.69291
- 729.0236
+ 492.0236
0 ]
- /Next 34 0 R
- /Parent 32 0 R
+ /Next 83 0 R
+ /Parent 81 0 R
/Title (Introduction) >>
endobj
% 'Outline.1': class OutlineEntryObject
-34 0 obj
-<< /Dest [ 8 0 R
+83 0 obj
+<< /Dest [ 41 0 R
/XYZ
62.69291
- 618.0236
+ 207.0236
0 ]
- /Next 35 0 R
- /Parent 32 0 R
- /Prev 33 0 R
- /Title (Testing applications with plac) >>
+ /Next 84 0 R
+ /Parent 81 0 R
+ /Prev 82 0 R
+ /Title (A simple example: a shelve interface) >>
endobj
% 'Outline.2': class OutlineEntryObject
-35 0 obj
-<< /Dest [ 9 0 R
+84 0 obj
+<< /Dest [ 46 0 R
/XYZ
62.69291
- 390.6236
+ 765.0236
0 ]
- /Next 36 0 R
- /Parent 32 0 R
- /Prev 34 0 R
- /Title (Writing command-line interpreters with plac) >>
+ /Next 85 0 R
+ /Parent 81 0 R
+ /Prev 83 0 R
+ /Title (Turning a script into an interactive application) >>
endobj
% 'Outline.3': class OutlineEntryObject
-36 0 obj
-<< /Dest [ 14 0 R
+85 0 obj
+<< /Dest [ 47 0 R
/XYZ
62.69291
- 765.0236
+ 168.6236
0 ]
- /Next 37 0 R
- /Parent 32 0 R
- /Prev 35 0 R
- /Title (Multi-parsers) >>
+ /Next 86 0 R
+ /Parent 81 0 R
+ /Prev 84 0 R
+ /Title (Testing a plac application) >>
endobj
% 'Outline.4': class OutlineEntryObject
-37 0 obj
-<< /Dest [ 19 0 R
+86 0 obj
+<< /Dest [ 52 0 R
/XYZ
62.69291
- 155.4236
+ 741.0236
0 ]
- /Next 38 0 R
- /Parent 32 0 R
- /Prev 36 0 R
- /Title (Advanced usage) >>
+ /Next 87 0 R
+ /Parent 81 0 R
+ /Prev 85 0 R
+ /Title (Plac easytests) >>
endobj
% 'Outline.5': class OutlineEntryObject
-38 0 obj
-<< /Dest [ 28 0 R
+87 0 obj
+<< /Dest [ 60 0 R
/XYZ
62.69291
- 611.8236
+ 170.8826
0 ]
- /Parent 32 0 R
- /Prev 37 0 R
- /Title (Custom annotation objects) >>
+ /Next 88 0 R
+ /Parent 81 0 R
+ /Prev 86 0 R
+ /Title (Plac batch scripts) >>
endobj
-% 'R39': class PDFPages
-39 0 obj
+% 'Outline.6': class OutlineEntryObject
+88 0 obj
+<< /Dest [ 64 0 R
+ /XYZ
+ 62.69291
+ 271.4236
+ 0 ]
+ /Next 89 0 R
+ /Parent 81 0 R
+ /Prev 87 0 R
+ /Title (Command containers) >>
+endobj
+% 'Outline.7': class OutlineEntryObject
+89 0 obj
+<< /Dest [ 69 0 R
+ /XYZ
+ 62.69291
+ 240.8235
+ 0 ]
+ /Next 90 0 R
+ /Parent 81 0 R
+ /Prev 88 0 R
+ /Title (A non class-based example) >>
+endobj
+% 'Outline.8': class OutlineEntryObject
+90 0 obj
+<< /Dest [ 73 0 R
+ /XYZ
+ 62.69291
+ 765.0236
+ 0 ]
+ /Next 91 0 R
+ /Parent 81 0 R
+ /Prev 89 0 R
+ /Title (Writing your own plac runner) >>
+endobj
+% 'Outline.9': class OutlineEntryObject
+91 0 obj
+<< /Dest [ 77 0 R
+ /XYZ
+ 62.69291
+ 615.8236
+ 0 ]
+ /Next 92 0 R
+ /Parent 81 0 R
+ /Prev 90 0 R
+ /Title (Summary) >>
+endobj
+% 'Outline.10': class OutlineEntryObject
+92 0 obj
+<< /Dest [ 77 0 R
+ /XYZ
+ 62.69291
+ 390.8236
+ 0 ]
+ /Parent 81 0 R
+ /Prev 91 0 R
+ /Title (Appendix: custom annotation objects) >>
+endobj
+% 'R93': class PDFPages
+93 0 obj
% page tree
-<< /Count 9
- /Kids [ 8 0 R
- 9 0 R
- 10 0 R
- 14 0 R
- 15 0 R
- 19 0 R
- 26 0 R
- 28 0 R
- 29 0 R ]
+<< /Count 16
+ /Kids [ 41 0 R
+ 42 0 R
+ 45 0 R
+ 46 0 R
+ 47 0 R
+ 49 0 R
+ 52 0 R
+ 60 0 R
+ 64 0 R
+ 67 0 R
+ 69 0 R
+ 70 0 R
+ 71 0 R
+ 73 0 R
+ 77 0 R
+ 78 0 R ]
/Type /Pages >>
endobj
-% 'R40': class PDFStream
-40 0 obj
+% 'R94': class PDFStream
+94 0 obj
% page stream
-<< /Length 3854 >>
+<< /Length 7157 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -580,67 +1512,509 @@ 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
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 0 183 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Introduction) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 183 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (1) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 165 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (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
+q
+1 0 0 1 0 147 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Turning a script into an interactive application) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 147 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (4) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 129 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Testing a plac application) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 129 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (5) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 111 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac easytests) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 111 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 93 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac batch scripts) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 93 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (8) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 75 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (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
+q
+1 0 0 1 0 57 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A non class-based example) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 57 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 39 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Writing your own plac runner) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 39 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (14) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 21 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Summary) 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
+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
+Q
+Q
+q
+1 0 0 1 397.8898 3 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (15) Tj T* -60.88 0 Td ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 471.0236 cm
+q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Introduction) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 630.0236 cm
+1 0 0 1 62.69291 429.0236 cm
q
-BT 1 0 0 1 0 64.82 Tm .564989 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (has been designed to be simple to use for simple stuff, but in truth it is a quite advanced tool with a) Tj T* 0 Tw 1.986905 Tw (field of applicability which far outreaches the specific domain of command-line arguments parsers. In) Tj T* 0 Tw .884985 Tw (reality ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is a generic tool to write domain specific languages \(DSL\). This document explains how you) Tj T* 0 Tw 1.766303 Tw (can use plac to test your application, and how you can use it to provide a scripting interface to your) Tj T* 0 Tw 1.384651 Tw (application. Notice that your application does not need to be a command-line application: you can use) Tj T* 0 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (whenever you have an API with strings in input and strings in output.) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 597.0236 cm
+1 0 0 1 62.69291 399.0236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Testing applications with plac) Tj T* ET
+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 531.0236 cm
+1 0 0 1 62.69291 369.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
+Q
+Q
+q
+1 0 0 1 62.69291 315.0236 cm
q
-BT 1 0 0 1 0 52.82 Tm .352209 Tw 12 TL /F1 10 Tf 0 0 0 rg (In the standard usage, ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (is called only once on the main function; however in the tests it quite) Tj T* 0 Tw .297126 Tw (natural to invoke ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (multiple times on the same function with different arguments. For instance,) Tj T* 0 Tw .436457 Tw (suppose you want to store the configuration of your application into a Python shelve; then, you may want) Tj T* 0 Tw .683318 Tw (to write a command-line tool to edit your configuration, i.e. a shelve interface. A possible implementation) Tj T* 0 Tw (could be the following:) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 89.82362 cm
+1 0 0 1 62.69291 261.0236 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
+Q
+Q
q
-1 0 0 1 0 0 cm
+1 0 0 1 62.69291 219.0236 cm
q
-1 0 0 1 6.6 6.6 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 /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
+Q
+Q
+q
+1 0 0 1 62.69291 186.0236 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
+Q
+Q
+q
+1 0 0 1 62.69291 132.0236 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
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (1) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% '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 468.6898 432 re B*
+n -6 -6 492 672 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 413.71 Tm /F3 10 Tf 12 TL (import shelve) Tj T* (import plac) Tj T* T* (@plac.annotations\() Tj T* ( help=\('show help', 'flag'\),) Tj T* ( all=\('show all parameters in the shelve', 'flag'\),) Tj T* ( clear=\('clear the shelve', 'flag'\),) Tj T* ( delete=\('delete an element', 'option'\),) Tj T* ( filename=\('filename of the shelve', 'option'\),) Tj T* ( params='names of the parameters in the shelve',) Tj T* ( setters='setters param=value'\)) Tj T* (def ishelve\(help, all, clear, delete, filename='conf.shelve',) Tj T* ( *params, **setters\):) Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if help:) Tj T* ( yield 'Special commands:') Tj T* ( yield 'help, all, clear, delete') Tj T* ( elif all:) Tj T* ( for param, name in sh.items\(\):) Tj T* ( yield '%s=%s' % \(param, name\)) Tj T* ( elif clear:) Tj T* ( sh.clear\(\)) Tj T* ( yield 'cleared the shelve') Tj T* ( elif delete:) Tj T* ( try:) Tj T* ( del sh[delete]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % delete) Tj T* ( else:) Tj T* ( yield 'deleted %s' % delete) Tj T* ( for param in params:) Tj T* ( try:) Tj T* ( yield sh[param]) Tj T* ( except KeyError:) Tj T* ET
+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
Q
Q
q
+1 0 0 1 62.69291 103.6412 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 97.64124 cm
+Q
+q
+1 0 0 1 62.69291 97.64124 cm
+Q
+q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (1) Tj T* -238.1649 0 Td ET
+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
-% 'R41': class PDFStream
-41 0 obj
+% 'R96': class PDFStream
+96 0 obj
% page stream
-<< /Length 3706 >>
+<< /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 607.8236 cm
+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
+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 735.0236 cm
+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
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 705.0236 cm
+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
+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 .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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 675.0236 cm
+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
+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
+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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 645.0236 cm
+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
+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 /F5 10 Tf (.clear) Tj /F1 10 Tf (\).) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 627.0236 cm
+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
+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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 609.0236 cm
+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
+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 /F5 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 591.0236 cm
+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
+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.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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 549.0236 cm
+Q
+q
+1 0 0 1 62.69291 549.0236 cm
+Q
+q
+1 0 0 1 62.69291 531.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
+Q
+Q
+q
+1 0 0 1 62.69291 485.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -650,25 +2024,51 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F3 10 Tf 12 TL ( yield '%s: not found' % param ) Tj T* ( for param, value in setters.items\(\):) Tj T* ( sh[param] = value) Tj T* ( yield 'setting %s=%s' % \(param, value\)) Tj T* ( finally:) Tj T* ( sh.close\(\)) Tj T* T* (ishelve.add_help = False # there is a custom help) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(ishelve\):) Tj T* ( print\(output\)) Tj T* ET
+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
+Q
Q
Q
Q
Q
+q
+1 0 0 1 62.69291 465.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
Q
+Q
+q
+1 0 0 1 62.69291 396.6236 cm
q
-1 0 0 1 62.69291 587.8236 cm
+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 /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
+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 write the tests for such implementation as follows:) Tj T* ET
+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 446.6236 cm
+1 0 0 1 62.69291 127.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -678,36 +2078,66 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
+n -6 -6 468.6898 240 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (import plac) Tj T* (from ishelve import ishelve) Tj T* T* (def test\(\):) Tj T* ( assert plac.call\(ishelve, []\) == []) Tj T* ( assert plac.call\(ishelve, ['--clear']\) == ['cleared the shelve']) Tj T* ( assert plac.call\(ishelve, ['a=1']\) == ['setting a=1']) Tj T* ( assert plac.call\(ishelve, ['a']\) == ['1']) Tj T* ( assert plac.call\(ishelve, ['--delete=a']\) == ['deleted a']) Tj T* ( assert plac.call\(ishelve, ['a']\) == ['a: not found']) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 402.6236 cm
+1 0 0 1 62.69291 107.4236 cm
q
-BT 1 0 0 1 0 28.82 Tm .344651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a small optimization here: once ) Tj /F3 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (has been called, a ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute is attached) Tj T* 0 Tw 7.140814 Tw (to ) Tj /F3 10 Tf (func) Tj /F1 10 Tf (, containing the parser associated to the function annotations. The second time) Tj T* 0 Tw /F3 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (is invoked, the parser is re-used.) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 369.6236 cm
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (3) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R97': class PDFStream
+97 0 obj
+% page stream
+<< /Length 4316 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 744.0236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Writing command-line interpreters with plac) Tj T* ET
+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 327.6236 cm
+1 0 0 1 62.69291 702.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm .677485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Apart from testing, there is another typical use case where ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (is invoked multiple times, in the) Tj T* 0 Tw .334269 Tw (implementation of command interpreters. For instance, you could define an interative interpreter on top of) Tj T* 0 Tw /F3 10 Tf (ishelve ) Tj /F1 10 Tf (as follows:) Tj T* ET
+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
q
-1 0 0 1 62.69291 90.42362 cm
+1 0 0 1 62.69291 672.0236 cm
+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
+Q
+Q
+q
+1 0 0 1 62.69291 654.0236 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
+Q
+Q
+q
+1 0 0 1 62.69291 416.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -720,31 +2150,93 @@ q
n -6 -6 468.6898 228 re B*
Q
q
-BT 1 0 0 1 0 209.71 Tm 12 TL /F3 10 Tf 0 0 0 rg (import plac) Tj T* (from ishelve import ishelve) Tj T* T* (ishelve.prefix_chars = '.') Tj T* (ishelve.add_help = False) Tj T* T* (@plac.annotations\() Tj T* ( interactive=\('start interactive interface', 'flag'\)\)) Tj T* (def main\(interactive, *args\):) Tj T* ( if interactive:) Tj T* ( import shlex) Tj T* ( while True:) Tj T* ( try:) Tj T* ( line = raw_input\('i) Tj (>) Tj ( '\)) Tj T* ( except EOFError:) Tj T* ( break) Tj T* ( cmd = shlex.split\(line\)) Tj T* ( for out in plac.call\(ishelve, cmd\):) Tj T* ET
+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
+Q
Q
Q
Q
Q
+q
+1 0 0 1 62.69291 372.8236 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
+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
+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 /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
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 187.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
+Q
+Q
+q
+1 0 0 1 62.69291 142.4236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F5 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 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 (2) 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
-% 'R42': class PDFStream
-42 0 obj
+% 'R98': class PDFStream
+98 0 obj
% page stream
-<< /Length 3636 >>
+<< /Length 4748 >>
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 511.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -754,23 +2246,62 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 252 re B*
Q
q
-BT 1 0 0 1 0 65.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ( print\(out\)) Tj T* ( else:) Tj T* ( plac.call\(ishelve, args\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 455.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
+Q
+Q
+q
+1 0 0 1 62.69291 425.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
+Q
+Q
+q
+1 0 0 1 62.69291 371.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
+Q
+Q
+q
+1 0 0 1 62.69291 365.8236 cm
+Q
+q
+1 0 0 1 62.69291 353.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+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
+Q
+Q
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Here is an usage session, usinng ) Tj /F3 10 Tf (rlwrap ) Tj /F1 10 Tf (to enable readline features:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 398.6236 cm
+1 0 0 1 62.69291 353.8236 cm
+Q
+q
+1 0 0 1 62.69291 323.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
+Q
+Q
+q
+1 0 0 1 62.69291 242.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -780,23 +2311,59 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 252 re B*
+n -6 -6 468.6898 72 re B*
Q
q
-BT 1 0 0 1 0 233.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ rlwrap python shelve_interpreter.py -i) Tj T* T* (i) Tj (>) Tj ( ..clear) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* (1) Tj T* (i) Tj (>) Tj ( b=2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( a b) Tj T* (1) Tj T* (2) Tj T* (i) Tj (>) Tj ( ..delete a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( ..all) Tj T* (b=2) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* ET
+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
Q
Q
+q
+1 0 0 1 62.69291 210.6236 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
Q
Q
q
-1 0 0 1 62.69291 282.6236 cm
+1 0 0 1 62.69291 180.6236 cm
q
-BT 1 0 0 1 0 100.82 Tm 1.991654 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see, it is possibly to write command interpreters directly on top of ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (and it is not) Tj T* 0 Tw 5.022126 Tw (particularly difficult. However, the devil is in the details \(I mean error management\) and my) Tj T* 0 Tw 5.139269 Tw (recommendation, if you want to implement an interpreter of commands, is to use the class) Tj T* 0 Tw .05229 Tw /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (which is especially suited for this task. ) Tj /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is available only if you) Tj T* 0 Tw .454488 Tw (are using a recent version of Python \() Tj (>) Tj (= 2.5\), because it is a context manager object to be used with the) Tj T* 0 Tw .38229 Tw /F3 10 Tf (with ) Tj /F1 10 Tf (statement. The only important method of ) Tj /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is the ) Tj /F3 10 Tf (.send ) Tj /F1 10 Tf (method, which takes) Tj T* 0 Tw .057209 Tw (a string in input and returns a string in output. Internally the input string is splitted with ) Tj /F3 10 Tf (shlex.split ) Tj /F1 10 Tf (and) Tj T* 0 Tw 2.706136 Tw (passed to ) Tj /F3 10 Tf (plac.call) Tj /F1 10 Tf (, with some trick to manage exceptions correctly. Moreover long options are) Tj T* 0 Tw (managed with a single prefix character.) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 105.4236 cm
+1 0 0 1 62.69291 147.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 129.6236 cm
+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
+Q
+Q
+q
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (5) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R99': class PDFStream
+99 0 obj
+% page stream
+<< /Length 5743 >>
+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
q
q
1 0 0 1 0 0 cm
@@ -806,52 +2373,151 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 168 re B*
+n -6 -6 468.6898 180 re B*
Q
q
-BT 1 0 0 1 0 149.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ("""Call this script with rlwrap and you will be happy""") Tj T* (from __future__ import with_statement) Tj T* (from plac_shell import Interpreter) Tj T* (from shelve_interface import interpreter) Tj T* T* (if __name__ == '__main__':) Tj T* ( with Interpreter\(interpreter\) as i:) Tj T* ( while True:) Tj T* ( try:) Tj T* ( line = raw_input\('i) Tj (>) Tj ( '\)) Tj T* ( except EOFError:) Tj T* ( break) Tj T* ( print\(i.send\(line\)\)) Tj T* ET
+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
+Q
+Q
+Q
Q
Q
+q
+1 0 0 1 62.69291 539.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
Q
Q
+q
+1 0 0 1 62.69291 497.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
+Q
+Q
+q
+1 0 0 1 62.69291 344.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 144 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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 300.6236 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
+Q
+Q
+q
+1 0 0 1 62.69291 246.6236 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
+Q
+Q
+q
+1 0 0 1 62.69291 192.6236 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
+Q
+Q
+q
+1 0 0 1 62.69291 174.6236 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
+Q
+Q
+q
+1 0 0 1 62.69291 168.6236 cm
+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
+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
+q
+Q
+Q
+q
+1 0 0 1 62.69291 156.6236 cm
+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
+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 (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 (6) Tj T* -238.1649 0 Td ET
Q
Q
endstream
endobj
-% 'R43': class PDFStream
-43 0 obj
+% 'R100': class PDFStream
+100 0 obj
% page stream
-<< /Length 3539 >>
+<< /Length 4275 >>
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 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Multi-parsers) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 690.0236 cm
+1 0 0 1 62.69291 720.0236 cm
q
-BT 1 0 0 1 0 40.82 Tm .594988 Tw 12 TL /F1 10 Tf 0 0 0 rg (As we saw, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to infer an arguments parser from the signature of a function. In addition, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is) Tj T* 0 Tw 1.96186 Tw (also able to infer a multi-parser from a container of commands, by inferring the subparsers from the) Tj T* 0 Tw .352651 Tw (commands. That is useful if you want to implement ) Tj /F4 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line) Tj T* 0 Tw (application featuring subcommands is subversion\).) Tj T* ET
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac easytests) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 648.0236 cm
+1 0 0 1 62.69291 678.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
+Q
+Q
+q
+1 0 0 1 62.69291 636.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm .636457 Tw 12 TL /F1 10 Tf 0 0 0 rg (A container of commands is any object with a ) Tj /F3 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or methods) Tj T* 0 Tw 1.50936 Tw (which are valid commands. In particular, a Python module is a perfect container of commands. As an) Tj T* 0 Tw (example, consider the following module implementing a fake Version Control System:) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 350.8236 cm
+1 0 0 1 62.69291 458.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -861,25 +2527,51 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 288 re B*
+n -6 -6 468.6898 168 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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 414.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
+Q
+Q
+q
+1 0 0 1 62.69291 370.0393 cm
+q
+q
+.988825 0 0 .988825 0 0 cm
+q
+1 0 0 1 6.6 6.674587 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 474 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 269.71 Tm /F3 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\() Tj T* ( url=\('url of the source code', 'positional'\)\)) Tj T* (def checkout\(url\):) Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\() Tj T* ( message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( return \('commit ', message\)) Tj T* T* (@plac.annotations\(quiet=\('summary information', 'flag'\)\)) Tj T* (def status\(quiet\):) Tj T* ( return \('status ', quiet\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import __main__) Tj T* ( print\(plac.call\(__main__\)\)) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 330.8236 cm
+1 0 0 1 62.69291 350.0393 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (and you will get the following output:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 189.6236 cm
+1 0 0 1 62.69291 184.8393 cm
q
q
1 0 0 1 0 0 cm
@@ -889,42 +2581,74 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
+n -6 -6 468.6898 156 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (usage: vcs.py [-h] {status,commit,checkout} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* T* (subcommands:) Tj T* ( {status,commit,checkout}) Tj T* ( -h to get additional help) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 133.6236 cm
+1 0 0 1 62.69291 164.8393 cm
q
0 0 0 rg
-BT 1 0 0 1 0 40.82 Tm /F1 10 Tf 12 TL 1.614104 Tw (If the commands are completely independent, a module is a good fit for a method container. In other) Tj T* 0 Tw .876654 Tw (situations, it is best to use a custom class. For instance, suppose you want to store the configuration of) Tj T* 0 Tw 2.040574 Tw (your application into a Python shelve; then, you may want to write a command-line tool to edit your) Tj T* 0 Tw (configuration, i.e. a shelve interface:) Tj T* ET
+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
+Q
+Q
+q
+1 0 0 1 62.69291 119.6393 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 /F5 10 Tf 12 TL ($ plac --easytest ishelve.placet) Tj T* (run 1 plac easy test\(s\)) 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 (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 (7) Tj T* -238.1649 0 Td ET
Q
Q
endstream
endobj
-% 'R44': class PDFStream
-44 0 obj
+% 'R101': class PDFStream
+101 0 obj
% page stream
-<< /Length 2642 >>
+<< /Length 6279 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 295.8236 cm
+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
q
q
1 0 0 1 0 0 cm
@@ -934,63 +2658,99 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 468 re B*
+n -6 -6 468.6898 24 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 449.71 Tm /F3 10 Tf 12 TL (import shelve) Tj T* (import plac) Tj T* T* (# error checking is missing: this is left to the reader) Tj T* (class ShelveInterface\(object\):) Tj T* ( "A minimal interface over a shelve object") Tj T* ( commands = 'set', 'show', 'show_all', 'delete') Tj T* ( def __init__\(self, fname\):) Tj T* ( self.fname = fname) Tj T* ( self.sh = shelve.open\(fname\)) Tj T* ( def set\(self, name, value\):) Tj T* ( "set name value") Tj T* ( yield 'setting %s=%s' % \(name, value\)) Tj T* ( self.sh[name] = value) Tj T* ( def show\(self, *names\):) Tj T* ( "show given parameters") Tj T* ( for name in names:) Tj T* ( yield '%s = %s\\n' % \(name, self.sh[name]\)) Tj T* ( def show_all\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s\\n' % \(name, self.sh[name]\)) Tj T* ( def delete\(self, name=None\):) Tj T* ( "delete given parameter \(or everything\)") Tj T* ( if name is None:) Tj T* ( yield 'deleting everything') Tj T* ( self.sh.clear\(\)) Tj T* ( else:) Tj T* ( yield 'deleting %s' % name) Tj T* ( del self.sh[name]) Tj T* T* (if __name__ == '__main__':) Tj T* ( interface = ShelveInterface\('conf.shelve'\)) Tj T* ( try:) Tj T* ( for output in plac.call\(interface\):) Tj T* ( print\(output\)) Tj T* ( finally:) Tj T* ( interface.sh.close\(\)) Tj T* ET
+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
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 275.8236 cm
+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
+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
+Q
+Q
+q
+1 0 0 1 62.69291 447.8236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is a session of usage on an Unix-like operating system:) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 98.62362 cm
+1 0 0 1 62.69291 262.8826 cm
q
q
-1 0 0 1 0 0 cm
+.976496 0 0 .976496 0 0 cm
q
-1 0 0 1 6.6 6.6 cm
+1 0 0 1 6.6 6.758862 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 168 re B*
+n -6 -6 480 180 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 149.71 Tm /F3 10 Tf 12 TL ($ alias conf="python shelve_interface.py") Tj T* ($ conf set a pippo) Tj T* (setting a=pippo) Tj T* ($ conf set b lippo) Tj T* (setting b=lippo) Tj T* ($ conf show_all) Tj T* (b = lippo) Tj T* (a = pippo) Tj T* ($ conf show a b) Tj T* (a = pippo) Tj T* (b = lippo) Tj T* ($ conf delete a) Tj T* (deleting a) Tj T* ET
+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
+Q
+Q
+Q
Q
Q
+q
+1 0 0 1 62.69291 182.8826 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
Q
Q
+q
+1 0 0 1 62.69291 149.8826 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
+Q
+Q
+q
+1 0 0 1 62.69291 107.8826 cm
+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
+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 (8) Tj T* -238.1649 0 Td ET
Q
Q
endstream
endobj
-% 'R45': class PDFStream
-45 0 obj
+% 'R102': class PDFStream
+102 0 obj
% page stream
-<< /Length 3421 >>
+<< /Length 5218 >>
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 739.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -1000,11 +2760,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 24 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL ($ conf show_all) Tj T* (b = lippo) Tj T* ET
+BT 1 0 0 1 0 5.71 Tm /F5 10 Tf 12 TL (plac.Interpreter\(main\).execute\(line_iterator\)) Tj T* ET
Q
Q
Q
@@ -1013,18 +2773,17 @@ Q
q
1 0 0 1 62.69291 671.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm .581235 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a multi-parser is a parser object with an attribute ) Tj /F3 10 Tf (.subp ) Tj /F1 10 Tf (which is a dictionary of subparsers;) Tj T* 0 Tw 1.757984 Tw (each of the methods listed in the attribute ) Tj /F3 10 Tf (.commands ) Tj /F1 10 Tf (corresponds to a subparser inferred from the) Tj T* 0 Tw .593984 Tw (method signature. The original object gets a ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute containing the main parser which is associated) Tj T* 0 Tw (to an internal function which dispatches on the right method depending on the method name.) Tj T* ET
+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
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
+BT 1 0 0 1 0 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
Q
Q
q
-1 0 0 1 62.69291 308.6236 cm
+1 0 0 1 62.69291 548.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -1034,18 +2793,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 336 re B*
+n -6 -6 468.6898 96 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 317.71 Tm /F3 10 Tf 12 TL (import plac) Tj T* T* (class FVCS\(object\):) Tj T* ( "A Fake Version Control System") Tj T* ( commands = 'checkout', 'commit', 'status', 'help') Tj T* T* ( @plac.annotations\() Tj T* ( name=\('a recognized command', 'positional', None, str, commands\)\)) Tj T* ( def help\(self, name\):) Tj T* ( self.p.subp[name].print_help\(\)) Tj T* T* ( @plac.annotations\() Tj T* ( url=\('url of the source code', 'positional'\)\)) Tj T* ( def checkout\(self, url\):) Tj T* ( print\('checkout', url\)) Tj T* T* ( def commit\(self\):) Tj T* ( print\('commit'\)) Tj T* T* ( @plac.annotations\(quiet=\('summary information', 'flag'\)\)) Tj T* ( def status\(self, quiet\):) Tj T* ( print\('status', quiet\)) Tj T* T* (main = FVCS\(\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+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
+Q
+Q
Q
Q
Q
+q
+1 0 0 1 62.69291 504.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
Q
Q
q
-1 0 0 1 62.69291 167.4236 cm
+1 0 0 1 62.69291 327.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -1055,59 +2820,189 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
+n -6 -6 468.6898 168 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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 283.4236 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
+Q
+Q
+q
+1 0 0 1 62.69291 250.4236 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
+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
+Q
+Q
+q
+1 0 0 1 62.69291 166.4236 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
+Q
Q
q
+1 0 0 1 62.69291 124.4236 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
+Q
+Q
+q
+1 0 0 1 62.69291 106.4236 cm
+q
0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL (usage: example13.py [-h] {status,commit,checkout,help} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* T* (subcommands:) Tj T* ( {status,commit,checkout,help}) Tj T* ( -h to get additional help) Tj T* ET
+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 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
+% '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
+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 504 re B*
Q
q
-1 0 0 1 62.69291 134.4236 cm
+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
+Q
+Q
+Q
+Q
+Q
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Advanced usage) Tj T* ET
+1 0 0 1 62.69291 143.8236 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
Q
Q
q
-1 0 0 1 62.69291 104.4236 cm
+1 0 0 1 62.69291 125.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm .094988 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (relies on a ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (for all of the heavy lifting work and it is possible to leverage on ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features) Tj T* 0 Tw (directly or indirectly.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is a session of usage on an Unix-like operating system:) Tj T* ET
Q
Q
q
1 0 0 1 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 235.3849 0 Td (10) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R46': class PDFStream
-46 0 obj
+% 'R104': class PDFStream
+104 0 obj
% page stream
-<< /Length 5867 >>
+<< /Length 4597 >>
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
+1 0 0 1 62.69291 535.2235 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 240 re B*
+Q
q
-BT 1 0 0 1 0 28.82 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 1.435976 Tw /F3 10 Tf ('==SUPPRESS==' ) Tj /F1 10 Tf (as help string \(or ) Tj /F3 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use ) Tj 0 0 .501961 rg (argparse.FileType) Tj T* 0 Tw 0 0 0 rg (directly.) Tj T* ET
+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
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 675.0236 cm
+1 0 0 1 62.69291 515.2235 cm
q
-BT 1 0 0 1 0 40.82 Tm 1.639213 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is also possible to pass options to the underlying ) Tj /F3 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F3 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F3 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 605.8236 cm
+1 0 0 1 62.69291 434.0235 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 72 re B*
+Q
+q
+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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 414.0235 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
+Q
+Q
+q
+1 0 0 1 62.69291 360.0235 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
+Q
+Q
+q
+1 0 0 1 62.69291 342.0235 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
+Q
+Q
+q
+1 0 0 1 62.69291 272.8235 cm
q
q
1 0 0 1 0 0 cm
@@ -1120,21 +3015,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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 252.8235 cm
+q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F3 10 Tf 12 TL (def main\(...\):) Tj T* ( pass) Tj T* T* (main.add_help = False) Tj T* ET
+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
+Q
Q
+q
+1 0 0 1 62.69291 219.8235 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
+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
Q
Q
q
-1 0 0 1 62.69291 525.8236 cm
+1 0 0 1 62.69291 147.8235 cm
q
-BT 1 0 0 1 0 64.82 Tm 1.256457 Tw 12 TL /F1 10 Tf 0 0 0 rg (disable the recognition of the help flag ) Tj /F3 10 Tf (-h, --help) Tj /F1 10 Tf (. This is not particularly elegant, but I assume the) Tj T* 0 Tw .275703 Tw (typical user of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (will be happy with the defaults and would not want to change them; still it is possible if) Tj T* 0 Tw .365542 Tw (she wants to. For instance, by setting the ) Tj /F3 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add a comment to the) Tj T* 0 Tw .602339 Tw (usage message \(by default the docstring of the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function is used as description\). It is also possible) Tj T* 0 Tw .322988 Tw (to change the option prefix; for instance if your script must run under Windows and you want to use "/" as) Tj T* 0 Tw (option prefix you can add the line:) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 492.6236 cm
+1 0 0 1 62.69291 90.62352 cm
q
q
1 0 0 1 0 0 cm
@@ -1144,24 +3058,35 @@ 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 /F3 10 Tf 12 TL (main.prefix_chars='/-') Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 436.6236 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 40.82 Tm .591163 Tw 12 TL /F3 10 Tf 0 0 0 rg (prefix_chars ) Tj /F1 10 Tf (is an ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (feature. The first prefix char \() Tj /F3 10 Tf (/) Tj /F1 10 Tf (\) is used as the default in the construction) Tj T* 0 Tw .266098 Tw (of both short and long options; the second prefix char \() Tj /F3 10 Tf (-) Tj /F1 10 Tf (\) is kept to keep the ) Tj /F3 10 Tf (-h/--help ) Tj /F1 10 Tf (option working:) Tj T* 0 Tw .107209 Tw (however you can disable it and reimplement it if you like. For instance, here is how you could reimplement) Tj T* 0 Tw (the ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (command in the Fake VCS example:) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (11) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R105': class PDFStream
+105 0 obj
+% page stream
+<< /Length 3415 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 235.4236 cm
+1 0 0 1 62.69291 427.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -1171,53 +3096,347 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 192 re B*
+n -6 -6 468.6898 336 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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 383.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
+Q
+Q
+q
+1 0 0 1 62.69291 353.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
+Q
+Q
+q
+1 0 0 1 62.69291 212.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 132 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 173.71 Tm /F3 10 Tf 12 TL (import plac) Tj T* (from example13 import FVCS) Tj T* T* (class VCS_with_help\(FVCS\):) Tj T* ( commands = FVCS.commands + \('help',\)) Tj T* T* ( @plac.annotations\() Tj T* ( name=\('a recognized command', 'positional', None, str, commands\)\)) Tj T* ( def help\(self, name\):) Tj T* ( self.p.subp[name].print_help\(\)) Tj T* T* (main = VCS_with_help\(\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 192.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
+Q
+Q
+q
+1 0 0 1 62.69291 99.42362 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 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
+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
+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
+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 /F5 10 Tf 12 TL ( -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 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
+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
+Q
+Q
+q
+1 0 0 1 62.69291 512.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 156 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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 492.6236 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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 192 re B*
+Q
+q
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 191.4236 cm
+1 0 0 1 62.69291 235.4236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.938443 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (uses ) Tj /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and adds the parser as an attribute ) Tj /F3 10 Tf (.p) Tj /F1 10 Tf (. This also) Tj T* 0 Tw 8.165366 Tw (happers for multiparsers and you can take advantage of the ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute to invoke) Tj T* 0 Tw /F3 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (methods.) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 149.4236 cm
+1 0 0 1 62.69291 205.4236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.442126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Interested readers should read the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to understand the meaning of the other) Tj T* 0 Tw .771567 Tw (options. If there is a set of options that you use very often, you may consider writing a decorator adding) Tj T* 0 Tw (such options to the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function for you. For simplicity, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not perform any magic of that kind.) Tj T* ET
+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 119.4236 cm
+1 0 0 1 62.69291 151.4236 cm
q
-BT 1 0 0 1 0 16.82 Tm 7.709147 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is possible to access directly the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, by invoking the) Tj T* 0 Tw /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
+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 238.1649 0 Td (7) Tj T* -238.1649 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
-% 'R47': class PDFStream
-47 0 obj
+% 'R107': class PDFStream
+107 0 obj
% page stream
-<< /Length 3975 >>
+<< /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 655.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 (Writing your own plac runner) 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 .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
+Q
+Q
+q
+1 0 0 1 62.69291 684.0236 cm
+Q
+q
+1 0 0 1 62.69291 684.0236 cm
+Q
+q
+1 0 0 1 62.69291 666.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 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 666.0236 cm
+Q
+q
+1 0 0 1 62.69291 666.0236 cm
+Q
+q
+1 0 0 1 62.69291 648.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 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 648.0236 cm
+Q
+q
+1 0 0 1 62.69291 648.0236 cm
+Q
+q
+1 0 0 1 62.69291 630.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 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F5 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
+Q
+q
+1 0 0 1 62.69291 630.0236 cm
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 3 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F5 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
+Q
+q
+1 0 0 1 62.69291 612.0236 cm
+Q
+q
+1 0 0 1 62.69291 570.0236 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
+Q
+Q
+q
+1 0 0 1 62.69291 552.0236 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
+Q
+Q
+q
+1 0 0 1 62.69291 458.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -1227,42 +3446,81 @@ 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 /F3 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print plac.parser_from\(main\)) Tj T* (ArgumentParser\(prog='', usage=None, description=None, version=None,) Tj T* (formatter_class=) Tj (<) Tj (class 'argparse.HelpFormatter') Tj (>) Tj (, conflict_handler='error',) Tj T* (add_help=True\)) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 623.8236 cm
+1 0 0 1 62.69291 426.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm .365542 Tw 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (in the unit tests of the module, but regular users should never need to use it,) Tj T* 0 Tw (since the parser is also available as an attribute of the main function.) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 590.8236 cm
+1 0 0 1 62.69291 384.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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 228 re B*
+Q
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Custom annotation objects) Tj T* ET
+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
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 560.8236 cm
+1 0 0 1 62.69291 115.6236 cm
q
-BT 1 0 0 1 0 16.82 Tm .578651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses an ) Tj /F3 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the tuples in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F3 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
+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 62.69291 530.8236 cm
+1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .083735 Tw (Advanced users can implement their own annotation objects. For instance, here is an example of how you) Tj T* 0 Tw (could implement annotations for positional arguments:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (14) Tj T* -235.3849 0 Td 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
-1 0 0 1 62.69291 401.6236 cm
+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
q
q
1 0 0 1 0 0 cm
@@ -1272,25 +3530,229 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL (# annotations.py) Tj T* (class Positional\(object\):) Tj T* ( def __init__\(self, help='', type=None, choices=None, metavar=None\):) Tj T* ( self.help = help) Tj T* ( self.kind = 'positional') Tj T* ( self.abbrev = None) Tj T* ( self.type = type) Tj T* ( self.choices = choices) Tj T* ( self.metavar = metavar) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 381.6236 cm
+1 0 0 1 62.69291 627.8236 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
+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
+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
+Q
+q
+1 0 0 1 62.69291 546.8236 cm
+Q
+q
+1 0 0 1 62.69291 528.8236 cm
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
+BT /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 (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 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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 528.8236 cm
+Q
+q
+1 0 0 1 62.69291 528.8236 cm
+Q
+q
+1 0 0 1 62.69291 468.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 45 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 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
+Q
+Q
+q
+1 0 0 1 23 39 cm
+Q
+q
+1 0 0 1 23 39 cm
+Q
+q
+1 0 0 1 23 21 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 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 23 21 cm
+Q
+q
+1 0 0 1 23 21 cm
+Q
+q
+1 0 0 1 23 3 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 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
+Q
+Q
+q
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 23 3 cm
+Q
+q
+1 0 0 1 23 3 cm
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 468.8236 cm
+Q
+q
+1 0 0 1 62.69291 468.8236 cm
+Q
+q
+1 0 0 1 62.69291 438.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 62.69291 204.4236 cm
+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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 438.8236 cm
+Q
+q
+1 0 0 1 62.69291 438.8236 cm
+Q
+q
+1 0 0 1 62.69291 420.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 (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 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
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 420.8236 cm
+Q
+q
+1 0 0 1 62.69291 420.8236 cm
+Q
+q
+1 0 0 1 62.69291 402.8236 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
+Q
+Q
+q
+1 0 0 1 62.69291 369.8236 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
+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
+Q
+Q
+q
+1 0 0 1 62.69291 309.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .083735 Tw (Advanced users can implement their own annotation objects. For instance, here is an example of how you) Tj T* 0 Tw (could implement annotations for positional arguments:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 180.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -1300,25 +3762,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 168 re B*
+n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 149.71 Tm /F3 10 Tf 12 TL (# example11.py) Tj T* (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* ( n=Positional\("This is a float", float\),) Tj T* ( rest=Positional\("Other arguments"\)\)) Tj T* (def main\(i, n, *rest\):) Tj T* ( print\(i, n, rest\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 184.4236 cm
+1 0 0 1 62.69291 160.6236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message you get:) Tj T* ET
+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.22362 cm
+1 0 0 1 62.69291 91.42362 cm
q
q
1 0 0 1 0 0 cm
@@ -1328,11 +3790,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 60 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F3 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* ET
+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
Q
Q
Q
@@ -1342,21 +3804,21 @@ q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
+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
-% 'R48': class PDFStream
-48 0 obj
+% 'R109': class PDFStream
+109 0 obj
% page stream
-<< /Length 1026 >>
+<< /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 715.8236 cm
+1 0 0 1 62.69291 643.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -1366,178 +3828,330 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F3 10 Tf 12 TL T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+BT 1 0 0 1 0 101.71 Tm /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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 671.8236 cm
+1 0 0 1 62.69291 623.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm .713516 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can go on and define ) Tj /F3 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (Flag ) Tj /F1 10 Tf (classes, if you like. Using custom annotation objects you) Tj T* 0 Tw .17528 Tw (could do advanced things like extracting the annotations from a configuration file or from a database, but I) Tj T* 0 Tw (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message you get:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 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
+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
+Q
+Q
+q
+1 0 0 1 62.69291 450.6236 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
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
+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
-% 'R49': class PDFPageLabels
-49 0 obj
+% 'R110': class PDFPageLabels
+110 0 obj
% Document Root
<< /Nums [ 0
- 50 0 R
+ 111 0 R
1
- 51 0 R
+ 112 0 R
2
- 52 0 R
+ 113 0 R
3
- 53 0 R
+ 114 0 R
4
- 54 0 R
+ 115 0 R
5
- 55 0 R
+ 116 0 R
6
- 56 0 R
+ 117 0 R
7
- 57 0 R
+ 118 0 R
8
- 58 0 R ] >>
+ 119 0 R
+ 9
+ 120 0 R
+ 10
+ 121 0 R
+ 11
+ 122 0 R
+ 12
+ 123 0 R
+ 13
+ 124 0 R
+ 14
+ 125 0 R
+ 15
+ 126 0 R ] >>
endobj
-% 'R50': class PDFPageLabel
-50 0 obj
+% 'R111': class PDFPageLabel
+111 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R51': class PDFPageLabel
-51 0 obj
+% 'R112': class PDFPageLabel
+112 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R52': class PDFPageLabel
-52 0 obj
+% 'R113': class PDFPageLabel
+113 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R53': class PDFPageLabel
-53 0 obj
+% 'R114': class PDFPageLabel
+114 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R54': class PDFPageLabel
-54 0 obj
+% 'R115': class PDFPageLabel
+115 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R55': class PDFPageLabel
-55 0 obj
+% 'R116': class PDFPageLabel
+116 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R56': class PDFPageLabel
-56 0 obj
+% 'R117': class PDFPageLabel
+117 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R57': class PDFPageLabel
-57 0 obj
+% 'R118': class PDFPageLabel
+118 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R58': class PDFPageLabel
-58 0 obj
+% 'R119': class PDFPageLabel
+119 0 obj
% None
<< /S /D
/St 9 >>
endobj
+% 'R120': class PDFPageLabel
+120 0 obj
+% None
+<< /S /D
+ /St 10 >>
+endobj
+% 'R121': class PDFPageLabel
+121 0 obj
+% None
+<< /S /D
+ /St 11 >>
+endobj
+% 'R122': class PDFPageLabel
+122 0 obj
+% None
+<< /S /D
+ /St 12 >>
+endobj
+% 'R123': class PDFPageLabel
+123 0 obj
+% None
+<< /S /D
+ /St 13 >>
+endobj
+% 'R124': class PDFPageLabel
+124 0 obj
+% None
+<< /S /D
+ /St 14 >>
+endobj
+% 'R125': class PDFPageLabel
+125 0 obj
+% None
+<< /S /D
+ /St 15 >>
+endobj
+% 'R126': class PDFPageLabel
+126 0 obj
+% None
+<< /S /D
+ /St 16 >>
+endobj
xref
-0 59
+0 127
0000000000 65535 f
0000000113 00000 n
-0000000246 00000 n
-0000000411 00000 n
-0000000598 00000 n
-0000000847 00000 n
-0000001096 00000 n
-0000001334 00000 n
-0000001493 00000 n
-0000001808 00000 n
-0000002087 00000 n
-0000002381 00000 n
-0000002632 00000 n
-0000002871 00000 n
-0000003051 00000 n
-0000003361 00000 n
-0000003655 00000 n
-0000003905 00000 n
-0000004154 00000 n
-0000004389 00000 n
-0000004722 00000 n
-0000005038 00000 n
-0000005288 00000 n
-0000005538 00000 n
-0000005788 00000 n
-0000006040 00000 n
-0000006312 00000 n
-0000006673 00000 n
-0000006910 00000 n
-0000007211 00000 n
-0000007492 00000 n
-0000007651 00000 n
-0000007912 00000 n
-0000008037 00000 n
-0000008209 00000 n
-0000008414 00000 n
-0000008632 00000 n
-0000008821 00000 n
-0000009011 00000 n
-0000009181 00000 n
-0000009359 00000 n
-0000013314 00000 n
-0000017121 00000 n
-0000020858 00000 n
-0000024498 00000 n
-0000027241 00000 n
-0000030763 00000 n
-0000036731 00000 n
-0000040807 00000 n
-0000041938 00000 n
-0000042135 00000 n
-0000042212 00000 n
-0000042289 00000 n
-0000042366 00000 n
-0000042443 00000 n
-0000042520 00000 n
-0000042597 00000 n
-0000042674 00000 n
-0000042751 00000 n
+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
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\030N\026;L\241\300\254\011\265\273\363\247\201 N) (\030N\026;L\241\300\254\011\265\273\363\247\201 N)]
+ [(\001z\236\304t\027\345#m\031\\1\234-\227\346) (\001z\236\304t\027\345#m\031\\1\234-\227\346)]
- /Info 31 0 R
- /Root 30 0 R
- /Size 59 >>
+ /Info 80 0 R
+ /Root 79 0 R
+ /Size 127 >>
startxref
-42798
+104203
%%EOF
diff --git a/plac/doc/plac_adv.txt b/plac/doc/plac_adv.txt
index 16a974b..378e692 100644
--- a/plac/doc/plac_adv.txt
+++ b/plac/doc/plac_adv.txt
@@ -1,61 +1,162 @@
Testing and scripting your applications with plac
=========================================================
+.. contents::
+
Introduction
-----------------------------------------------------
-plac_ has been designed to be simple to use for simple stuff, but in
-truth it is a quite advanced tool with a field of applicability
-which far outreaches the specific domain of command-line arguments parsers.
-In reality plac_ is a generic tool to write domain specific
-languages (DSL).
-This document explains how you can use plac to test your application, and
-how you can use it to provide a scripting interface to your application.
-Notice that your application does not need to be a command-line
-application: you can use plac_ whenever you have an API with strings
-in input and strings in output.
-
-Testing applications with plac
--------------------------------------------
-
-In the standard usage, ``plac.call`` is called only once on the main
-function; however in the tests it quite natural to invoke ``plac.call``
-multiple times on the same function with different arguments.
-For instance, suppose you want to store the configuration of
-your application into a Python shelve; then, you may want to write
-a command-line tool to edit your configuration, i.e. a shelve
-interface. A possible implementation could be the following:
+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.
+
+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.
+
+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
+can test your application interactively as well as with batch
+scripts, and even with the analogous of Python doctests for your
+defined language.
+
+You can easily replace the ``cmd`` module of the standard library and
+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:
-You can write the tests for such implementation as follows:
+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
-.. include:: test_ishelve.py
- :literal:
+Command-line scripts have many advantages, but are no substitute
+for a real interactive application.
+
+Turning a script into an interactive application
+------------------------------------------------------------
-There is a small optimization here: once ``plac.call(func)``
-has been called, a ``.p`` attribute is attached to ``func``, containing
-the parser associated to the function annotations. The second time
-``plac.call(func)`` is invoked, the parser is re-used.
+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.
-Writing command-line interpreters with plac
------------------------------------------------
+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.
-Apart from testing, there is another typical use case where ``plac.call``
-is invoked multiple times, in the implementation of command interpreters.
-For instance, you could define an interative interpreter on top
-of ``ishelve`` as follows:
+You could define an interactive interpreter on top of ``ishelve`` as
+follows:
.. include:: shelve_interpreter.py
:literal:
-Here is an usage session, usinng ``rlwrap`` to enable readline features::
+A trick has been used here: the ishelve command-line interface has been
+hidden inside and external interface. They are distinct: for instance
+the external interface recognizes the ``-h/--help`` flag whereas the
+internal interface only recognizes the ``.help`` command:
+
+.. include:: shelve_interpreter.help
+ :literal:
- $ rlwrap python shelve_interpreter.py -i
+Thanks to this ingenuous trick, the script can be run both interactively
+and non-interactively::
- i> ..clear
+ $ 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)::
+
+ $ rlwrap python shelve_interpreter.py -i # interactive use
+ usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]
+ [.filename /home/micheles/conf.shelve]
+ [params [params ...]] [setters [setters ...]]
i> a=1
setting a=1
i> a
@@ -65,83 +166,416 @@ Here is an usage session, usinng ``rlwrap`` to enable readline features::
i> a b
1
2
- i> ..delete a
+ i> .del a
deleted a
i> a
a: not found
- i> ..all
+ i> .show
b=2
i> [CTRL-D]
-As you see, it is possibly to write command interpreters directly on top of
-``plac.call`` and it is not particularly difficult. However, the devil
-is in the details (I mean error management) and my recommendation, if
-you want to implement an interpreter of commands, is to use the
-class ``plac.Interpreter`` which is especially suited for this
-task. ``plac.Interpreter`` is available only if you are using a recent
+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.
+
+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
-to be used with the ``with`` statement. The only important method
-of ``plac.Interpreter`` is the ``.send`` method, which takes a
-string in input and returns a string in output. Internally the input string
-is splitted with ``shlex.split`` and passed to ``plac.call``,
-with some trick to manage exceptions correctly. Moreover long options
-are managed with a single prefix character.
-
-.. include:: shelve_cli.py
+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.
+
+Testing a plac application
+-----------------------------------------------------------
+
+In principle, one could write automatic tests for the
+``ishelve`` application by using ``plac.call`` directly:
+
+.. include:: test_ishelve.py
:literal:
-Multi-parsers
-----------------------------------------
+However, using ``plac.call`` is not especially nice. The big
+issue is that ``plac.call`` responds to invalid input by printing an
+error message on stderr and by raising a ``SystemExit``: this is
+certainly not a nice thing to do in a test.
-As we saw, plac_ is able to infer an arguments parser from the
-signature of a function. In addition, plac_ is also able to infer a
-multi-parser from a container of commands, by inferring the subparsers
-from the commands. That is useful if you want to implement
-*subcommands* (a familiar example of a command-line application
-featuring subcommands is subversion).
+As a consequence of this behavior it is impossible to test for invalid
+commands, unless you wrap the ``SystemExit`` exception by
+hand each time (a possibly you do something with the error message in
+stderr too). Luckily, ``plac`` offers a better testing support through
+the ``check`` method of ``Interpreter`` objects:
-A container of commands is any object with a ``.commands`` attribute
-listing a set of functions or methods which are valid commands. In
-particular, a Python module is a perfect container of commands. As an
-example, consider the following module implementing a fake Version
-Control System:
+.. include:: test_ishelve2.py
+ :literal:
-.. include:: vcs.py
+The method ``.check(given_input, expected_output)`` works on strings
+and raises an ``AssertionError`` if the output produced by the
+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).
+
+Interpreters offer a minor syntactic advantage with respect to calling
+``plac.call`` directly, but they offer a *major* semantic advantage when things
+go wrong (read exceptions): an ``Interpreter`` object internally invokes
+something like ``plac.call``, but it wraps all exceptions, so that ``i.check``
+is guaranteed not to raise any exception except ``AssertionError``.
+
+Even the ``SystemExit`` exception is captured and you can write your test as
+
+ ``i.check('-cler', 'SystemExit: unrecognized arguments: -cler')``
+
+without risk of exiting from the Python interpreter.
+
+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 easytests
+---------------------------------------------------------
+
+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*.
+
+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):
+
+.. include:: ishelve.placet
:literal:
-Here is the usage message:
+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
-.. include:: vcs.help
+::
+
+ $ 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:
-If the commands are completely independent, a module is a good fit for
-a method container. In other situations, it is best to use a custom
-class. For instance, suppose you want to store the configuration of
-your application into a Python shelve; then, you may want to write
-a command-line tool to edit your configuration, i.e. a shelve
-interface:
+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
+easy tests supported by ``plac`` are *not* unittests: they should be
+used to model user interaction when the order of the operations
+matters. Since the single subtests in a ``.placet`` file are not
+independent, it makes sense to exit immediately at the first failure.
+
+The support for doctests in plac_ comes nearly for free, thanks to the shlex_
+module in the standard library, which is able to parse simple
+languages as the ones you can implement with plac_. In particular,
+thanks to shlex_, plac_ is able to recognize comments (the default
+comment character is ``#``), continuation lines, escape sequences and
+more. Look at the shlex_ documentation if you need to customize how
+the language is interpreted.
+
+In addition, I have implemented from scratch some support for line number
+recognition, so that if a test fail you get the line number of the
+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 ``.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:
+
+.. include:: test_doct.py
+ :literal:
+
+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
+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
+a string containing directory names separated by colons). If the variable
+``PLACPATH`` is not defined, it just looks in the current directory.
+If the plac tool is not found, an ``ImportError`` is raised.
+
+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)
+
+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.
+
+In particular consider the following batch file, which contains a syntax
+error (``.dl`` instead of ``.del``):
+
+.. include:: ishelve.batch
+ :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::
+
+ $ plac --batch --verbose ishelve.batch
+ i> .clear
+ cleared the shelve
+ i> a=1 b=2
+ setting a=1
+ setting b=2
+ i> .show
+ b=2
+ a=1
+ i> .del a
+ deleted a
+ i> .dl b
+ unrecognized arguments: .dl
+
+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
+----------------------------------------
+
+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.
-.. include:: shelve_interface2.py
+plac_ is actually able to infer a set of subparsers from a
+generic container of commands. This is useful if you want to
+implement *subcommands* (a familiar example of a command-line
+application featuring subcommands is subversion).
+
+Technically a container of commands is any object with a ``.commands`` attribute
+listing a set of functions or methods which are valid commands. A command
+container may have initialization/finalization hooks (``__enter__/__exit__``)
+and dispatch hooks (``__missing__``, invoked for invalid command names).
+
+Using this feature the shelve interface can be rewritten in a more
+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).
+
Here is a session of usage on an Unix-like operating system::
- $ alias conf="python shelve_interface.py"
+ $ alias conf="python ishelve2.py"
$ conf set a pippo
setting a=pippo
$ conf set b lippo
setting b=lippo
- $ conf show_all
+ $ conf showall
b = lippo
a = pippo
$ conf show a b
a = pippo
b = lippo
- $ conf delete a
+ $ conf del a # an abbreviation
deleting a
- $ conf show_all
+ $ conf 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
+ 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``).
+
+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``
+(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).
+
+If an abbreviation is ambiguous, plac warns you::
+
+ $ plac ishelve2.py -i
+ 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.
+
+A non class-based example
+--------------------------------------------------------
+
+plac_ 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 ``.commands`` attribute and possibly
+``__enter__`` and/or ``__exit__`` attributes.
+
+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__``
+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.
+
+The ``vcs`` module does not contain an ``if __name__ == '__main__'``
+block, but you can still run it through the plac runner
+(try ``plac vcs.py -h``):
+
+.. include:: vcs.help
+ :literal:
+
+You can get help for the subcommands by postponing ``-h`` after the
+name of the command::
+
+ $ plac vcs.py status -h
+ usage: vcs.py status [-h] [-q]
+
+ A fake status command
+ optional arguments:
+ -h, --help show this help message and exit
+ -q, --quiet summary information
+
+Notice how the docstring of the command is automatically shown in
+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
+
+and here is an interactive session::
+
+ $ plac -i vcs.py
+ usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
+ i> check url
+ checkout
+ url
+ i> st -q
+ status
+ True
+ i> co
+ commit
+ None
+ i> sto
+ Command 'sto' does not exist
+ i> [CTRL-D]
+ 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.
+
+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
@@ -149,91 +583,111 @@ the method signature. The original object gets a ``.p`` attribute
containing the main parser which is associated to an internal function
which dispatches on the right method depending on the method name.
-Here is the usage message:
+Writing your own plac runner
+----------------------------------------------------------
-.. include:: example13.py
- :literal:
+The runner included in the plac_ distribution is intentionally kept
+small (around 40 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
+strings in input and returns a four-tuple with attributes
+``.str``, ``.etype``, ``.exc`` and ``.tb``:
-.. include:: example13.help
- :literal:
+- ``.str`` is the output of the command, if successful (a string);
+- ``.etype`` is the class of the exception, if the command fail;
+- ``.exc`` is the exception instance;
+- ``.tb`` is the traceback.
-Advanced usage
-----------------------------------------------------
+Moreover the ``__str__`` representation of the output object is redefined
+to return the output string if the command was successful or the error
+message if the command failed (actually it returns the error message
+preceded by the name of the exception class).
-plac_ relies on a argparse_ for all of the heavy lifting work and it is
-possible to leverage on argparse_ features directly or indirectly.
+For instance, if you send a mispelled option to
+the interpreter a ``SystemExit`` will be trapped:
-For instance, you can make invisible an argument in the usage message
-simply by using ``'==SUPPRESS=='`` as help string (or
-``argparse.SUPPRESS``). Similarly, you can use argparse.FileType_
-directly.
+>>> import plac
+>>> from ishelve import ishelve
+>>> with plac.Interpreter(ishelve) as i:
+... print(i.send('.cler'))
+...
+SystemExit: unrecognized arguments: .cler
-It is also possible to pass options to the underlying
-``argparse.ArgumentParser`` object (currently it accepts the default
-arguments ``description``, ``epilog``, ``prog``, ``usage``,
-``add_help``, ``argument_default``, ``parents``, ``prefix_chars``,
-``fromfile_prefix_chars``, ``conflict_handler``, ``formatter_class``).
-It is enough to set such attributes on the ``main`` function. For
-instance
+It is important to invoke the ``.send`` method inside the context manager,
+otherwise you will get a ``RuntimeError``.
-::
+For instance, suppose you want to implement a graphical runner for a
+plac-based interpreter with two text widgets: one to enter the commands
+and one to display the results. Suppose you want to display the errors
+with tracebacks in red. You will need to code something like that
+(pseudocode follows)::
- def main(...):
- pass
+ input_widget = WidgetReadingInput()
+ output_widget = WidgetDisplayingOutput()
- main.add_help = False
+ def send(interpreter, line):
+ out = interpreter.send(line)
+ if out.tb: # there was an error
+ output_widget.display(out.tb, color='red')
+ else:
+ output_widget.display(out.str)
-disable the recognition of the help flag ``-h, --help``. This is not
-particularly elegant, but I assume the typical user of plac_ will be
-happy with the defaults and would not want to change them; still it is
-possible if she wants to. For instance, by setting the ``description``
-attribute, it is possible to add a comment to the usage message (by
-default the docstring of the ``main`` function is used as
-description). It is also possible to change the option prefix; for
-instance if your script must run under Windows and you want to use "/"
-as option prefix you can add the line::
+ main = plac.import_main(tool_path) # get the main object
- main.prefix_chars='/-'
+ with plac.Interpreter(main) as i:
+ def callback(event):
+ if event.user_pressed_ENTER():
+ send(i, input_widget.last_line)
+ input_widget.addcallback(callback)
+ gui_mainloop.start()
-``prefix_chars`` is an argparse_ feature. The first prefix char (``/``) is used
-as the default in the construction of both short and long options;
-the second prefix char (``-``) is kept to keep the ``-h/--help`` option
-working: however you can disable it and reimplement it if you like.
-For instance, here is how you could reimplement the ``help`` command
-in the Fake VCS example:
+You can adapt the pseudocode to your GUI toolkit of choice and you can
+also change the file associations in such a way that clicking on a
+plac tool file the graphical user interface starts.
-.. include:: example14.py
- :literal:
+There is a final *caveat*: since the plac interpreter loop is
+implemented via extended generators, plac interpreters are single threaded: you
+will get an error if you ``.send`` commands from separated threads.
+You can circumvent the problem by using a queue. If EXIT is a sentinel
+value to signal exiting from the interpreter look, you can write code
+like this::
-Internally ``plac.call`` uses
-``plac.parser_from`` and adds the parser as an attribute ``.p``.
-This also happers for multiparsers and you can take advantage of
-the ``.p`` attribute to invoke ``argparse.ArgumentParser`` methods.
+ with interpreter:
+ for input_value in iter(input_queue.get, EXIT):
+ output_queue.put(interpreter.send(input_value))
-Interested readers should read the documentation of argparse_ to
-understand the meaning of the other options. If there is a set of
-options that you use very often, you may consider writing a decorator
-adding such options to the ``main`` function for you. For simplicity,
-plac_ does not perform any magic of that kind.
+The same trick also work for processes; you could run the interpreter
+loop in a separate process and send commands to it via the Queue
+class provided by the multiprocessing_ module.
-It is possible to access directly the underlying ArgumentParser_ object, by
-invoking the ``plac.parser_from`` utility function:
+Summary
+-------------------------------------------------------
->>> import plac
->>> def main(arg):
-... pass
-...
->>> print plac.parser_from(main)
-ArgumentParser(prog='', usage=None, description=None, version=None,
-formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error',
-add_help=True)
+Once plac_ 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
+rules are quite simple:
+
+1.
+ if you want to implement a command-line script, use ``plac.call``;
+
+2.
+ if you want to implement a command interpreter, use ``plac.Interpreter``:
+
+ - for an interactive interpreter, call the ``.interact`` method;
+ - for an batch interpreter, call the ``.execute`` method;
+
+3. for testing call the ``Interpreter.check`` method in the appropriate context
+ or use the ``Interpreter.doctest`` feature;
+
+4. if you need to go at a lower level, you may need to call the
+ ``Interpreter.send`` method.
-I use ``plac.parser_from`` in the unit tests of the module, but regular
-users should never need to use it, since the parser is also available
-as an attribute of the main function.
+Moreover, remember that ``plac_runner.py`` is your friend.
-Custom annotation objects
-------------------------------------------------------
+Appendix: custom annotation objects
+--------------------------------------------------------
Internally plac_ uses an ``Annotation`` class to convert the tuples
in the function signature into annotation objects, i.e. objects with
@@ -277,3 +731,7 @@ pretty well for most users.
.. _CLIArgs: http://pypi.python.org/pypi/CLIArgs
.. _opterator: http://pypi.python.org/pypi/opterator
.. _advanced usage document: in-writing
+.. _twill: http://twill.idyll.org/
+.. _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
diff --git a/plac/doc/read_stdin.py b/plac/doc/read_stdin.py
new file mode 100644
index 0000000..584644f
--- /dev/null
+++ b/plac/doc/read_stdin.py
@@ -0,0 +1,13 @@
+"""
+You can run this script as
+$ python read_stdin.py < ishelve.bat
+"""
+from __future__ import with_statement
+import sys
+from ishelve import ishelve
+import plac
+
+if __name__ == '__main__':
+ with plac.Interpreter(ishelve) as i:
+ for line in sys.stdin:
+ print(i.send(line))
diff --git a/plac/doc/shelve_interpreter.help b/plac/doc/shelve_interpreter.help
new file mode 100644
index 0000000..e8c5320
--- /dev/null
+++ b/plac/doc/shelve_interpreter.help
@@ -0,0 +1,12 @@
+usage: shelve_interpreter.py [-h] [-interactive]
+ [subcommands [subcommands ...]]
+
+This script works both interactively and non-interactively. Use .help to see
+the internal commands.
+
+positional arguments:
+ subcommands the commands of the underlying ishelve interpreter
+
+optional arguments:
+ -h, --help show this help message and exit
+ -interactive start interactive interface
diff --git a/plac/doc/shelve_interpreter.py b/plac/doc/shelve_interpreter.py
new file mode 100644
index 0000000..a19f5c9
--- /dev/null
+++ b/plac/doc/shelve_interpreter.py
@@ -0,0 +1,18 @@
+import plac, ishelve
+
+@plac.annotations(
+ interactive=('start interactive interface', 'flag'),
+ subcommands='the commands of the underlying ishelve interpreter')
+def main(interactive, *subcommands):
+ """
+ This script works both interactively and non-interactively.
+ Use .help to see the internal commands.
+ """
+ if interactive:
+ plac.Interpreter(ishelve.main).interact()
+ else:
+ for out in plac.call(ishelve.main, subcommands):
+ print(out)
+
+if __name__ == '__main__':
+ plac.call(main)
diff --git a/plac/doc/test_ishelve.py b/plac/doc/test_ishelve.py
new file mode 100644
index 0000000..23cf2bd
--- /dev/null
+++ b/plac/doc/test_ishelve.py
@@ -0,0 +1,14 @@
+# test_ishelve.py
+import plac
+from ishelve import ishelve
+
+def test():
+ assert plac.call(ishelve, []) == []
+ assert plac.call(ishelve, ['.clear']) == ['cleared the shelve']
+ assert plac.call(ishelve, ['a=1']) == ['setting a=1']
+ assert plac.call(ishelve, ['a']) == ['1']
+ assert plac.call(ishelve, ['.delete=a']) == ['deleted a']
+ assert plac.call(ishelve, ['a']) == ['a: not found']
+
+if __name__ == '__main__':
+ test()
diff --git a/plac/doc/test_ishelve2.py b/plac/doc/test_ishelve2.py
new file mode 100644
index 0000000..5625d4f
--- /dev/null
+++ b/plac/doc/test_ishelve2.py
@@ -0,0 +1,11 @@
+# test_ishelve2.py
+from __future__ import with_statement
+import plac, ishelve
+
+def test():
+ with plac.Interpreter(ishelve.main) as i:
+ i.check('.clear', 'cleared the shelve')
+ i.check('a=1', 'setting a=1')
+ i.check('a', '1')
+ i.check('.delete=a', 'deleted a')
+ i.check('a', 'a: not found')
diff --git a/plac/test_plac.py b/plac/doc/test_plac.py
index 3cfd936..6a8e7bb 100644
--- a/plac/test_plac.py
+++ b/plac/doc/test_plac.py
@@ -5,8 +5,8 @@ The tests are runnable with nose, with py.test, or even as standalone script
import os, sys
import plac
-sys.path.insert(0, 'doc')
sys_argv0 = sys.argv[0]
+os.chdir(os.path.dirname(__file__) or '.') # work in the current directory
######################## helpers #######################
@@ -23,20 +23,21 @@ def parser_from(f, **kw):
return plac.parser_from(f)
def check_help(name):
- sys.argv[0] = name + '.py'
- dic = {}
- if sys.version >= '3':
- exec(open(os.path.join('doc', name + '.py')).read(), dic)
- else: # Python 2.X
+ sys.argv[0] = name + '.py' # avoid issue with nosetests
+ try:
try:
- execfile(os.path.join('doc', name + '.py'), dic)
- except SyntaxError: # raised by some tests when using Python 2
- return
- p = plac.parser_from(dic['main'])
- expected = open(os.path.join('doc', name + '.help')).read().strip()
- got = p.format_help().strip()
- assert got == expected, got
- sys.argv[0] = sys_argv0
+ main = plac.import_main(name + '.py')
+ except SyntaxError:
+ if sys.version < '3': # expected for Python 2.X
+ return
+ else: # not expected for Python 3.X
+ raise
+ p = plac.parser_from(main)
+ expected = open(name + '.help').read().strip()
+ got = p.format_help().strip()
+ assert got == expected, got
+ finally:
+ sys.argv[0] = sys_argv0
####################### tests ############################
@@ -114,23 +115,32 @@ def test_kwargs():
expect(SystemExit, plac.call, main, ['arg1', 'arg2', 'a=1', 'opt=2'])
def test_expected_help():
- for fname in os.listdir('doc'):
- if fname.endswith('.help') and fname != 'vcs.help':
- yield check_help, fname[:-5]
+ for fname in os.listdir('.'):
+ if fname.endswith('.help'):
+ name = fname[:-5]
+ if name not in ('vcs', 'ishelve'):
+ yield check_help, fname[:-5]
class Cmds(object):
+ add_help = False
commands = 'help', 'commit'
def help(self, name):
return 'help', name
def commit(self):
return 'commit'
+cmds = Cmds()
+
def test_cmds():
- cmds = Cmds()
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 ['help', 'foo'] == plac.call(cmds, ['h', 'foo'])
+ expect(SystemExit, plac.call, cmds, ['foo'])
+
def test_yield():
def main():
for i in (1, 2, 3):
diff --git a/plac/doc/vcs.help b/plac/doc/vcs.help
index 5d7ac77..4af805f 100644
--- a/plac/doc/vcs.help
+++ b/plac/doc/vcs.help
@@ -1,4 +1,4 @@
-usage: vcs.py [-h] {status,commit,checkout} ...
+usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
A Fake Version Control System
diff --git a/plac/doc/vcs.py b/plac/doc/vcs.py
index a893fd9..c423fb0 100644
--- a/plac/doc/vcs.py
+++ b/plac/doc/vcs.py
@@ -4,20 +4,27 @@ import plac
commands = 'checkout', 'commit', 'status'
-@plac.annotations(
- url=('url of the source code', 'positional'))
+@plac.annotations(url='url of the source code')
def checkout(url):
+ "A fake checkout command"
return ('checkout ', url)
-@plac.annotations(
- message=('commit message', 'option'))
+@plac.annotations(message=('commit message', 'option'))
def commit(message):
+ "A fake commit command"
return ('commit ', message)
-@plac.annotations(quiet=('summary information', 'flag'))
+@plac.annotations(quiet=('summary information', 'flag', 'q'))
def status(quiet):
+ "A fake status command"
return ('status ', quiet)
-if __name__ == '__main__':
- import __main__
- print(plac.call(__main__))
+def __missing__(name):
+ return 'Command %r does not exist' % name
+
+def __exit__(etype, exc, tb):
+ "Will be called automatically at the end of the call/cmdloop"
+ if etype in (None, GeneratorExit): # success
+ print('ok')
+
+main = __import__(__name__) # the module imports itself!
diff --git a/plac/plac.py b/plac/plac.py
index 31bc89b..361522e 100644
--- a/plac/plac.py
+++ b/plac/plac.py
@@ -30,8 +30,26 @@ See doc/plac.pdf for the documentation.
__version__ = '0.5.0'
-import sys
+import imp, os, sys
from plac_core import *
-
if sys.version >= '2.5':
- from plac_ext import Interpreter
+ from plac_ext import Interpreter, cmd_interface
+
+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"
+ if not os.path.isabs(path): # relative path, look at PLACDIRS
+ for placdir in PLACDIRS:
+ fullpath = os.path.join(placdir, path)
+ if os.path.exists(fullpath):
+ break
+ else: # no break
+ raise ImportError('Cannot find %s', 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
diff --git a/plac/plac_core.py b/plac/plac_core.py
index 8df1d03..defb215 100644
--- a/plac/plac_core.py
+++ b/plac/plac_core.py
@@ -1,6 +1,5 @@
# this module should be kept Python 2.3 compatible
import re, sys, inspect, argparse
-
if sys.version >= '3':
from inspect import getfullargspec
else:
@@ -88,13 +87,11 @@ def _parser_from(func, baseparser=None):
p = baseparser or argparse.ArgumentParser(**pconf(func))
p.func = func
p.argspec = f = getfullargspec(func)
- p.parselist = _parser_call.__get__(p)
+ p.parselist = _parser_call.__get__(p, p.__class__)
+ # add func.p
if inspect.ismethod(func):
del f.args[0] # remove self
- try:
- func.im_func.p = p # Python 2.X
- except AttributeError:
- func.__func__.p = p # Python 2.3
+ func.im_func.p = p
else:
func.p = p
defaults = f.defaults or ()
@@ -141,6 +138,13 @@ 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
@@ -151,14 +155,13 @@ def parser_from(obj, baseparser=None):
return obj.p
elif hasattr(obj, 'commands'): # a command container
p = obj.p = baseparser or argparse.ArgumentParser(**pconf(obj))
- subparsers = p.add_subparsers(
- title='subcommands', help='-h to get additional help')
- p.subp = {}
for cmd in obj.commands:
- method = getattr(obj, cmd)
- baseparser = subparsers.add_parser(cmd, **pconf(method))
- p.subp[cmd] = _parser_from(method, baseparser)
- p.parselist = _parser_call.__get__(p)
+ addsubparser(cmd, getattr(obj, cmd), p)
+ 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)
@@ -178,16 +181,45 @@ def _extract_kwargs(args):
arglist.append(arg)
return arglist, kwargs
+def _match_cmd(abbrev, commands):
+ "Extract the full 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]
+ matches = [name for name in commands if name.startswith(abbrev)]
+ n = len(matches)
+ if n == 1:
+ return matches[0]
+ elif n > 1:
+ 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):
"""
Given a parser, calls its underlying callable with the arglist.
Works also for multiparsers by dispatching to the underlying parser.
"""
- subp = getattr(p, 'subp', None)
- if subp: # subparsers
- p.parse_args(arglist) # argument checking
- return _parser_call(subp[arglist[0]], arglist[1:])
- # regular parser
+ if 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)
else:
@@ -198,13 +230,7 @@ def _parser_call(p, arglist):
collision = set(p.argspec.args) & set(kwargs)
if collision:
p.error('colliding keyword arguments: %s' % ' '.join(collision))
- result = p.func(*(args + varargs), **kwargs)
- if result is None:
- return []
- elif hasattr(result, '__iter__') and not isinstance(result, str):
- return [str(x) for x in result]
- else:
- return [str(result)]
+ return stringlist( p.func(*(args + varargs), **kwargs))
def call(obj, arglist=sys.argv[1:]):
"""
@@ -215,4 +241,14 @@ def call(obj, arglist=sys.argv[1:]):
and dispatches to the associated subparsers.
Return a list of strings.
"""
- return parser_from(obj).parselist(arglist)
+ 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
diff --git a/plac/plac_ext.py b/plac/plac_ext.py
index 6c904f2..9f8b60f 100644
--- a/plac/plac_ext.py
+++ b/plac/plac_ext.py
@@ -1,42 +1,64 @@
-import os, sys, cmd, shlex
-import plac
+# this module requires Python 2.5+
+from __future__ import with_statement
+import os, sys, cmd, shlex, traceback
+import plac_core
def cmd_interface(obj):
"Returns a cmd.Cmd wrapper over the command container"
- dic = {}
+ i = Interpreter(obj)
+ def default(self, line):
+ print(i.send(line))
+ dic = dict(preloop=lambda self: i.__enter__(),
+ postloop=lambda self: i.__exit__(),
+ do_EOF=lambda self, line: True,
+ default=default)
for command in obj.commands:
method = getattr(obj, command)
def do_func(self, line, command=command):
- args = [command] + shlex.split(line)
- try:
- for output in plac.call(obj, args):
- print(output)
- except SystemExit:
- print(e)
- except Exception:
- print('%s: %s' % (e.__class__.__name__, e))
+ print(i.send(command + ' ' + line))
do_func.__doc__ = method.__doc__
- if sys.version >= '2.4':
- do_func.__name__ = method.__name__
+ do_func.__name__ = method.__name__
dic['do_' + command] = do_func
clsname = '_%s_' % obj.__class__.__name__
cls = type(clsname, (cmd.Cmd, object), dic)
return cls()
-# requires Python 2.5+
+def _getoutputs(lines, intlist):
+ "helper used in parse_doctest"
+ for i, start in enumerate(intlist[:-1]):
+ end = intlist[i + 1]
+ yield '\n'.join(lines[start+1:end])
+
+class Output(tuple):
+ """
+ The output returned by the .send method of an Interpreter object.
+ Contains the output string (or None if there is an exception)
+ and the exception information (exception type, exception, traceback).
+ """
+ def __new__(cls, outstr, etype, exc, tb):
+ self = tuple.__new__(cls, (outstr, etype, exc, tb))
+ self.str = outstr
+ self.etype = etype
+ self.exc = exc
+ self.tb = tb
+ return self
+ def __str__(self):
+ "Returns the output string or the error message"
+ if self.str is None: # there was an error
+ return '%s: %s' % (self.etype.__name__, self.exc)
+ else:
+ return self.str
+
class Interpreter(object):
"""
- The safety_net is a function taking a parsing function and a list of
- arguments and applying the first to the second by managing some class
- of exceptions.
+ A context manager with a .send method and a few utility methods:
+ execute, test and doctest.
"""
- def __init__(self, obj, safety_net=lambda parse, arglist: parse(arglist),
- commentchar='#'):
+ def __init__(self, obj, commentchar='#'):
self.obj = obj
- self.safety_net = safety_net
self.commentchar = commentchar
self.interpreter = None
- self.p = plac.parser_from(obj)
+ self.p = plac_core.parser_from(obj)
self.p.error = lambda msg: sys.exit(msg) # patch the parser
def __enter__(self):
@@ -45,11 +67,10 @@ class Interpreter(object):
return self
def send(self, line):
- """
- Send a line to the underlying interpreter.
- Return a string or None for comment lines.
- The line should end with a newline.
- """
+ "Send a line to the underlying interpreter and return an Output object"
+ if self.interpreter is None:
+ raise RuntimeError('%r not initialized: probably you forgot to '
+ 'use the with statement' % self)
return self.interpreter.send(line)
def close(self):
@@ -60,97 +81,89 @@ class Interpreter(object):
def _make_interpreter(self):
enter = getattr(self.obj, '__enter__', lambda : None)
- exit = getattr(self.obj, '__exit__', lambda a1, a2, a3: None)
+ exit = getattr(self.obj, '__exit__', lambda et, ex, tb: None)
enter()
- result = None
- prefix = self.p.short_prefix
+ output = None
try:
while True:
- line = yield result
- if not line:
- break
- elif line.startswith(self.commentchar):
- yield; continue
- arglist = shlex.split(line)
- for i, long_opt in enumerate(arglist):
- # avoid double prefix in long options
- if len(long_opt) > 2 and long_opt[0] == prefix:
- arglist[i] = prefix + long_opt
+ line = yield output
+ arglist = shlex.split(line, self.commentchar)
try:
- output = self.safety_net(self.p.parselist, arglist)
- except SystemExit, e:
- output = [str(e)]
- result = os.linesep.join(output)
+ lines = self.p.parselist(arglist)
+ except:
+ output = Output(None, *sys.exc_info())
+ else:
+ output = Output(os.linesep.join(lines), None, None, None)
except:
exit(*sys.exc_info())
raise
else:
exit(None, None, None)
-## from cmd.py
-# def complete(self, text, state):
-# """Return the next possible completion for 'text'.
-
-# If a command has not been entered, then complete against command list.
-# Otherwise try to call complete_<command> to get list of completions.
-# """
-# if state == 0:
-# import readline
-# origline = readline.get_line_buffer()
-# line = origline.lstrip()
-# stripped = len(origline) - len(line)
-# begidx = readline.get_begidx() - stripped
-# endidx = readline.get_endidx() - stripped
-# if begidx>0:
-# cmd, args, foo = self.parseline(line)
-# if cmd == '':
-# compfunc = self.completedefault
-# else:
-# try:
-# compfunc = getattr(self, 'complete_' + cmd)
-# except AttributeError:
-# compfunc = self.completedefault
-# else:
-# compfunc = self.completenames
-# self.completion_matches = compfunc(text, line, begidx, endidx)
-# try:
-# return self.completion_matches[state]
-# except IndexError:
-# return None
-
-# def readlines(completekey='tab'):
-# if readline:
-# old_completer = readline.get_completer()
-# readline.set_completer(self.complete)
-# readline.parse_and_bind(self.completekey + ": complete")
-# try:
-# while True:
-# try:
-# yield raw_input('cli> ')
-# except EOFError:
-# break
-# finally:
-# if readline:
-# readline.set_completer(old_completer)
-
-class Cmds(object):
- commands = 'checkout', 'commit', 'status', 'help'
- quit = False
-
- @plac.annotations(
- name=('a recognized command', 'positional', None, str, commands))
- def help(self, name):
- return self.p.subp[name].format_help()
-
- def checkout(self, url):
- return ('checkout', url)
-
- def commit(self):
- return ('commit')
-
- @plac.annotations(quiet=('summary information', 'flag'))
- def status(self, quiet):
- return ('status', quiet)
-
-if __name__ == '__main__':
- cmdloop(Cmds())
+ def check(self, given_input, expected_output, lineno=None):
+ "Make sure you get the expected_output from the given_input"
+ output = str(self.send(given_input))
+ ok = output == expected_output
+ if not ok:
+ msg = 'input: %s\noutput: %s\nexpected: %s' % (
+ given_input, output, expected_output)
+ if lineno:
+ msg = 'line %d: %s' % (lineno + 1, msg)
+ raise AssertionError(msg)
+
+ def _parse_doctest(self, lineiter):
+ lines = [line.strip() for line in lineiter]
+ inputs = []
+ positions = []
+ for i, line in enumerate(lines):
+ if line.startswith('i> '):
+ inputs.append(line[3:])
+ positions.append(i)
+ positions.append(len(lines) + 1) # last position
+ return zip(inputs, _getoutputs(lines, positions), positions)
+
+ def doctest(self, lineiter, out=sys.stdout, verbose=False):
+ """
+ Parse a text containing doctests in a context and tests of all them.
+ Raise an error even if a single doctest if broken. Use this for
+ sequential tests which are logically grouped.
+ """
+ with self:
+ for input, output, no in self._parse_doctest(lineiter):
+ if verbose:
+ out.write('i> %s\n' % input)
+ out.write('-> %s\n' % output)
+ out.flush()
+ self.check(input, output, no)
+
+ def execute(self, lineiter, out=sys.stdout, verbose=False):
+ """
+ Execute a lineiter of commands in a context and print the output.
+ """
+ with self:
+ for line in lineiter:
+ if verbose:
+ out.write('i> ' + line); out.flush()
+ output = self.send(line)
+ if output.str is None: # there was an error
+ raise output.etype, output.exc, output.tb
+ out.write('%s\n' % output.str)
+ out.flush()
+
+ def interact(self, prompt='i> ', intro=None, verbose=False):
+ """Starts an interactive command loop reading commands from the
+ consolle. Using rlwrap is recommended."""
+ if intro is None:
+ self.p.print_usage()
+ else:
+ print(intro)
+ with self:
+ while True:
+ try:
+ line = raw_input(prompt)
+ except EOFError:
+ break
+ out = self.send(line)
+ if verbose:
+ traceback.print_tb(out.tb)
+ print(out)
diff --git a/plac/setup.py b/plac/setup.py
index 0a7ee88..f255d96 100644
--- a/plac/setup.py
+++ b/plac/setup.py
@@ -20,7 +20,9 @@ if __name__ == '__main__':
url='http://pypi.python.org/pypi/plac',
license="BSD License",
py_modules = ['plac_core', 'plac_ext', 'plac'],
+ scripts = ['plac_runner.py'],
install_requires=['argparse>=1.1'],
+ use_2to3=True,
keywords="command line arguments parser",
platforms=["All"],
classifiers=['Development Status :: 3 - Alpha',