summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-08-02 07:21:07 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-08-02 07:21:07 +0200
commitde277c015f8a816dba98963b3ffc6cb19c69319d (patch)
tree73b5959b56f7c33dff4e5aa29b116bac3c6c9c82
parent39670ca6b766a1c576c68f5a80729b6fde4d0e06 (diff)
parentc90ae5e83fbf0d47c87de6267e04e0d3435bca66 (diff)
downloadmicheles-de277c015f8a816dba98963b3ffc6cb19c69319d.tar.gz
Merged from main branch
-rw-r--r--plac/CHANGES.txt9
-rw-r--r--plac/Makefile4
-rw-r--r--plac/doc/importer_ui.py29
-rw-r--r--plac/doc/ishelve2.plac2
-rw-r--r--plac/doc/ishelve2.placet2
-rw-r--r--plac/doc/ishelve2.py5
-rw-r--r--plac/doc/picalculator.py63
-rw-r--r--plac/doc/plac.el77
-rw-r--r--plac/doc/plac.html176
-rw-r--r--plac/doc/plac.pdf230
-rw-r--r--plac/doc/plac.txt2
-rw-r--r--plac/doc/plac_adv.html435
-rw-r--r--plac/doc/plac_adv.pdf3828
-rw-r--r--plac/doc/plac_adv.txt223
-rw-r--r--plac/doc/server_ex.py9
-rw-r--r--plac/doc/sql_interface.py5
-rw-r--r--plac/doc/test_pi.py11
-rw-r--r--plac/doc/test_server.py35
-rw-r--r--plac/plac.py4
-rw-r--r--plac/plac_core.py10
-rw-r--r--plac/plac_ext.py333
-rw-r--r--plac/plac_runner.py15
22 files changed, 3793 insertions, 1714 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt
index ddd57e0..9d1414a 100644
--- a/plac/CHANGES.txt
+++ b/plac/CHANGES.txt
@@ -1,16 +1,19 @@
HISTORY
----------
+0.7.0 Improved and documented the support for parallel programming;
+ added multiline and emacs support; added an asynchronous server
+ (2010-08-XX)
0.6.1 Fixed the history file location; added the ability to pass a split
function; added two forgotten files; added a reference to cmd2 by
Catherine Devlin (2010-07-12)
-0.6.0 Improvement the interactive experience with full readline support and
+0.6.0 Improved the interactive experience with full readline support and
custom help. Added support for long running command, via threads and
- processes (2010-07-11).
+ processes (2010-07-11)
0.5.0 Gigantic release. Introduced smart options, added an Interpreter class
and the command container concept. Made the split plac/plac_core/plac_ext
and added a plac runner, able to run scripts, batch files and doctests.
- Removed the default formatter class (2010-06-20).
+ Removed the default formatter class (2010-06-20)
0.4.3 Fixed the installation procedure to automatically download argparse
if needed (2010-06-11)
0.4.2 Added missing .help files, made the tests generative and added a
diff --git a/plac/Makefile b/plac/Makefile
index 7e119b6..5afc3e5 100644
--- a/plac/Makefile
+++ b/plac/Makefile
@@ -7,6 +7,6 @@ doc/plac_adv.pdf: doc/plac_adv.txt
upload:
python3 setup.py register sdist upload
2:
- python setup.py build; sudo python setup.py install
+ python setup.py build; sudo python setup.py install; sudo rm -rf dist
3:
- python3 setup.py build; sudo python3 setup.py install
+ python3 setup.py build; sudo python3 setup.py install; sudo rm -rf dist
diff --git a/plac/doc/importer_ui.py b/plac/doc/importer_ui.py
new file mode 100644
index 0000000..a4592b0
--- /dev/null
+++ b/plac/doc/importer_ui.py
@@ -0,0 +1,29 @@
+from Tkinter import *
+from importer3 import FakeImporter
+
+def taskwidget(root, task, tick=500):
+ "A Label widget showing the output of a task every 500 ms"
+ sv = StringVar(root)
+ lb = Label(root, textvariable=sv)
+ def show_outlist():
+ try:
+ out = task.outlist[-1]
+ except IndexError: # no output yet
+ out = ''
+ sv.set('%s %s' % (task, out))
+ root.after(tick, show_outlist)
+ root.after(0, show_outlist)
+ return lb
+
+def monitor(tasks):
+ root = Tk()
+ for task in tasks:
+ task.run()
+ taskwidget(root, task).pack()
+ root.mainloop()
+
+if __name__ == '__main__':
+ import plac
+ with plac.Interpreter(plac.call(FakeImporter)) as i:
+ tasks = [i.submit('import_file f1'), i.submit('import_file f2')]
+ monitor(tasks)
diff --git a/plac/doc/ishelve2.plac b/plac/doc/ishelve2.plac
index 7d6ea02..5ca0064 100644
--- a/plac/doc/ishelve2.plac
+++ b/plac/doc/ishelve2.plac
@@ -1,4 +1,4 @@
-#!ishelve2.py -c ~/conf.shelve
+#!ishelve2.py:ShelveInterface -c ~/conf.shelve
set a 1
del a
del a # intentional error
diff --git a/plac/doc/ishelve2.placet b/plac/doc/ishelve2.placet
index f954932..fdc89c4 100644
--- a/plac/doc/ishelve2.placet
+++ b/plac/doc/ishelve2.placet
@@ -1,4 +1,4 @@
-#!ishelve2.py -configfile=~/test.shelve
+#!ishelve2.py:ShelveInterface -configfile=~/test.shelve
i> del
deleting everything
i> set a 1
diff --git a/plac/doc/ishelve2.py b/plac/doc/ishelve2.py
index 9b8b57e..baef210 100644
--- a/plac/doc/ishelve2.py
+++ b/plac/doc/ishelve2.py
@@ -36,8 +36,7 @@ class ShelveInterface(object):
yield 'deleting %s' % name
del self.sh[name] # no error checking
-main = ShelveInterface # the main 'function' can also be a class!
+main = ShelveInterface # useful for the tests
if __name__ == '__main__':
- shelve_interface = plac.call(main)
- plac.Interpreter(shelve_interface).interact()
+ plac.Interpreter(plac.call(ShelveInterface)).interact()
diff --git a/plac/doc/picalculator.py b/plac/doc/picalculator.py
new file mode 100644
index 0000000..75df2b2
--- /dev/null
+++ b/plac/doc/picalculator.py
@@ -0,0 +1,63 @@
+from __future__ import with_statement
+from random import random
+import multiprocessing
+import plac
+
+class PiCalculator(object):
+ """Compute pi in parallel with threads or processes"""
+
+ @plac.annotations(
+ npoints=('number of integration points', 'positional', None, int),
+ mode=('sequential|parallel|threaded', 'option', 'm', str, 'SPT'))
+ def __init__(self, npoints, mode='S'):
+ self.npoints = npoints
+ if mode == 'P':
+ self.mpcommands = ['calc_pi']
+ elif mode == 'T':
+ self.thcommands = ['calc_pi']
+ elif mode == 'S':
+ self.commands = ['calc_pi']
+ self.n_cpu = multiprocessing.cpu_count()
+
+ def submit_tasks(self):
+ self.i = plac.Interpreter(self).__enter__()
+ return [self.i.submit('calc_pi %d' % (self.npoints / self.n_cpu))
+ for _ in range(self.n_cpu)]
+
+ def close(self):
+ self.i.close()
+
+ @plac.annotations(
+ npoints=('npoints', 'positional', None, int))
+ def calc_pi(self, npoints):
+ counts = 0
+ for j in xrange(npoints):
+ n, r = divmod(j, 1000000)
+ if r == 0:
+ yield '%dM iterations' % n
+ x, y = random(), random()
+ if x*x + y*y < 1:
+ counts += 1
+ yield (4.0 * counts)/npoints
+
+ def run(self):
+ tasks = self.i.tasks()
+ for t in tasks:
+ t.run()
+ try:
+ total = 0
+ for task in tasks:
+ total += task.result
+ except: # the task was killed
+ print tasks
+ return
+ return total / self.n_cpu
+
+if __name__ == '__main__':
+ pc = plac.call(PiCalculator)
+ pc.submit_tasks()
+ try:
+ import time; t0 = time.time()
+ print '%f in %f seconds ' % (pc.run(), time.time() - t0)
+ finally:
+ pc.close()
diff --git a/plac/doc/plac.el b/plac/doc/plac.el
new file mode 100644
index 0000000..0eddcbb
--- /dev/null
+++ b/plac/doc/plac.el
@@ -0,0 +1,77 @@
+;;; Emacs-plac integration: add the following to your .emacs
+
+(define-generic-mode 'plac-mode
+ '("#") ; comment chars
+ '(); highlighted commands
+ nil
+ '(".plac\\'"); file extensions
+ nil)
+
+(add-hook 'plac-mode-hook (lambda () (local-set-key [f4] 'plac-start)))
+(add-hook 'plac-mode-hook (lambda () (local-set-key [f5] 'plac-send)))
+(add-hook 'plac-mode-hook (lambda () (local-set-key [f6] 'plac-stop)))
+
+(defconst terminator 59); ASCII code for the semicolon
+(defvar *plac-process* nil)
+
+(defun plac-start ()
+ "Start an inferior plac process by inferring the script to use from the
+ shebang line"
+ (interactive)
+ (let ((shebang-line
+ (save-excursion
+ (goto-line 1) (end-of-line)
+ (buffer-substring-no-properties 3 (point)))))
+ (if *plac-process* (princ "plac already started")
+ (setq *plac-process*
+ (start-process
+ "plac" "*plac*" "plac_runner.py" "-m" shebang-line))))
+ (display-buffer "*plac*"))
+
+;(defun plac-send ()
+; "Send the current region to the inferior plac process"
+; (interactive)
+; (save-excursion (set-buffer "*plac*") (erase-buffer))
+; (process-send-region *plac-process* (region-beginning) (region-end)))
+
+(defun current-paragraph-beg-end ()
+ "Returns the extrema of the current paragraph, delimited by semicolons"
+ (interactive)
+ (save-excursion
+ (let ((beg (save-excursion (goto-line 2) (point))); skip the shebang
+ (end (point-max)))
+ ;; go backward
+ (while (> (point) beg)
+ (goto-char (1- (point)))
+ (if (= terminator (following-char))
+ (setq beg (point))))
+ (if (= terminator (following-char))
+ (setq beg (1+ beg)))
+ ;; go forward
+ (while (< (point) end)
+ (goto-char (1+ (point)))
+ (if (= 59 (following-char))
+ (setq end (point))))
+ (if (= 59 (following-char))
+ (setq end (1+ end)))
+ (list beg end))))
+
+(defun plac-send ()
+ "Send the current region to the inferior plac process"
+ (interactive)
+ (save-excursion (set-buffer "*plac*") (erase-buffer))
+ (let ((p (apply 'buffer-substring-no-properties (current-paragraph-beg-end))))
+ (message p)
+ (process-send-string *plac-process* (concat p "\n"))))
+ ;(switch-to-buffer-other-window "*plac*")))
+ ;(save-excursion (set-buffer "*plac*")
+ ; (set-window-start (selected-window) 1 nil))))
+
+(defun plac-stop ()
+ "Stop the inferior plac process by sending to it an EOF"
+ (interactive)
+ (process-send-eof *plac-process*)
+ (setq *plac-process* nil)
+ "killed *plac-process*")
+
+(provide 'plac)
diff --git a/plac/doc/plac.html b/plac/doc/plac.html
index 37184e5..a634480 100644
--- a/plac/doc/plac.html
+++ b/plac/doc/plac.html
@@ -3,10 +3,10 @@
<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" />
-<meta name="date" content="June 2010" />
+<meta name="date" content="July 2010" />
<style type="text/css">
.first {
@@ -424,14 +424,14 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
</tr>
<tr><th class="docinfo-name">Date:</th>
-<td>June 2010</td></tr>
+<td>July 2010</td></tr>
<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/plac">http://pypi.python.org/pypi/plac</a></td>
</tr>
<tr class="field"><th class="docinfo-name">Project page:</th><td class="field-body"><a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">http://micheles.googlecode.com/hg/plac/doc/plac.html</a></td>
</tr>
<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.3+</td>
</tr>
-<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal"><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>
@@ -525,7 +525,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 exist. Actually I think
the language should recognize the main function and pass to it the
command-line arguments automatically; unfortunaly this is unlikely to
@@ -615,12 +615,12 @@ if __name__ == '__main__':
</pre>
<p>Here I want to perform a query on a database table, by extracting the
-most recent 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>)
+most recent data: it makes sense for <tt class="docutils literal">today</tt> to be a default argument.
+If there is a most used table (in this example a table called <tt class="docutils literal">'product'</tt>)
it also makes sense to make it a default argument. Performing the parsing
of the command-line arguments by hand takes 8 ugly lines of boilerplate
(using <a class="reference external" href="http://argparse.googlecode.com">argparse</a> would require about the same number of lines).
-With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal"><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)
@@ -680,7 +680,7 @@ let's the machine take care of the details.</p>
the sense that it delivers the programmer from the burden of writing
the parser, but is less of a hack: instead of extracting the parser
from the docstring of the module, it extracts it from the signature of
-the <tt class="docutils literal"><span class="pre">main</span></tt> function.</p>
+the <tt class="docutils literal">main</tt> function.</p>
<p>The idea comes from the <cite>function annotations</cite> concept, a new
feature of Python 3. An example is worth a thousand words, so here
it is:</p>
@@ -698,7 +698,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 ...]]
@@ -740,10 +740,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">
@@ -767,7 +767,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
@@ -808,8 +808,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
@@ -843,7 +843,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
@@ -873,12 +873,12 @@ $ python3 example9.py -v dsn
connecting to dsn
</pre>
<p>Notice that it is an error trying to specify a default for flags: the
-default value for a flag is always <tt class="docutils literal"><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
@@ -913,7 +913,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;,
@@ -922,7 +922,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 the core features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> run even on Python 2.3.</p>
</div>
<div class="section" id="more-features">
@@ -934,19 +934,19 @@ 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 or <tt class="docutils literal"><span class="pre">None</span></tt>, <tt class="docutils literal"><span class="pre">type</span></tt> is a callable taking a
+<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 or <tt class="docutils literal">None</tt>, <tt class="docutils literal">type</tt> is a callable taking a
string in input,
-<tt class="docutils literal"><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">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>
@@ -989,7 +989,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
@@ -1000,13 +1000,13 @@ $ python example10.py ad 1 2 3 4 # a mispelling error
usage: example10.py [-h] {add,mul} [n [n ...]]
example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul')
</pre>
-<p><tt class="docutils literal"><span class="pre">plac.call</span></tt> can also be used in doctests like this:</p>
+<p><tt class="docutils literal">plac.call</tt> can also be used in doctests like this:</p>
<pre class="doctest-block">
&gt;&gt;&gt; import plac, example10
&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)):
@@ -1014,12 +1014,12 @@ example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add',
&gt;&gt;&gt; plac.call(main, ['3'])
[0, 1, 2]
</pre>
-<p>Internally <tt class="docutils literal"><span class="pre">plac.call</span></tt> tries to convert the output of the main function
+<p>Internally <tt class="docutils literal">plac.call</tt> tries to convert the output of the main function
into a list, if possible. If the output is not iterable or it is a
string, it is left unchanged, but if it is iterable it is converted.
-In particular, generator objects are exhausted by <tt class="docutils literal"><span class="pre">plac.call</span></tt>.</p>
+In particular, generator objects are exhausted by <tt class="docutils literal">plac.call</tt>.</p>
<p>This behavior avoids mistakes like forgetting of applying
-<tt class="docutils literal"><span class="pre">list(result)</span></tt> to the result of <tt class="docutils literal"><span class="pre">plac.call</span></tt>; moreover it makes
+<tt class="docutils literal">list(result)</tt> to the result of <tt class="docutils literal">plac.call</tt>; moreover it makes
errors visible early, and avoids mistakes in code like the following:</p>
<pre class="literal-block">
try:
@@ -1030,13 +1030,13 @@ except:
<p>Without the &quot;listify&quot; functionality, a main function returning a
generator object would not raise any exception until the generator
is iterated over.</p>
-<p>If you are a fan of lazyness, you can still have it by setting the <tt class="docutils literal"><span class="pre">eager</span></tt>
-flag to <tt class="docutils literal"><span class="pre">False</span></tt>, as in the following example:</p>
+<p>If you are a fan of lazyness, you can still have it by setting the <tt class="docutils literal">eager</tt>
+flag to <tt class="docutils literal">False</tt>, as in the following example:</p>
<pre class="literal-block">
for line in plac.call(main, args, eager=False):
print(line)
</pre>
-<p>If <tt class="docutils literal"><span class="pre">main</span></tt> returns a generator object this example will print each
+<p>If <tt class="docutils literal">main</tt> returns a generator object this example will print each
line as soon as available, whereas the default behaviour is to print
all the lines together and the end of the computation.</p>
</div>
@@ -1044,7 +1044,7 @@ all the lines together and the end of the computation.</p>
<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
@@ -1079,12 +1079,12 @@ 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, I use <tt class="docutils literal"><span class="pre">yield</span></tt>, and I perform the
-print in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block. The advantage of the pattern is that
-tests invoking <tt class="docutils literal"><span class="pre">plac.call</span></tt> and checking the result become trivial:
+<tt class="docutils literal">print</tt> in the main function, I use <tt class="docutils literal">yield</tt>, and I perform the
+print in the <tt class="docutils literal">__main__</tt> block. The advantage of the pattern is that
+tests invoking <tt class="docutils literal">plac.call</tt> and checking the result become trivial:
had I performed the printing in the main function, the test would have
-involved an ugly hack like redirecting <tt class="docutils literal"><span class="pre">sys.stdout</span></tt> to a
-<tt class="docutils literal"><span class="pre">StringIO</span></tt> object.</p>
+involved an ugly hack like redirecting <tt class="docutils literal">sys.stdout</tt> to a
+<tt class="docutils literal">StringIO</tt> object.</p>
<p>Here is the usage message:</p>
<pre class="literal-block">
usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
@@ -1108,7 +1108,7 @@ optional arguments:
<h1><a class="toc-backref" href="#id9">Keyword arguments</a></h1>
<p>Starting from release 0.4, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports keyword arguments. In
practice that means that if your main function has keyword arguments,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> treats specially arguments of the form <tt class="docutils literal"><span class="pre">&quot;name=value&quot;</span></tt> in the
+<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
@@ -1152,7 +1152,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
@@ -1234,29 +1234,29 @@ if __name__ == '__main__':
implemented a custom help command.</li>
<li>I have changed the prefix character used to recognize the options
to a dot.</li>
-<li>Keyword arguments recognition (in the <tt class="docutils literal"><span class="pre">**setters</span></tt>) is used to make it
+<li>Keyword arguments recognition (in the <tt class="docutils literal">**setters</tt>) is used to make it
possible to store a value in the shelve with the syntax
-<tt class="docutils literal"><span class="pre">param_name=param_value</span></tt>.</li>
-<li><tt class="docutils literal"><span class="pre">*params</span></tt> are used to retrieve parameters from the shelve and some
+<tt class="docutils literal">param_name=param_value</tt>.</li>
+<li><tt class="docutils literal">*params</tt> are used to retrieve parameters from the shelve and some
error checking is performed in the case of missing parameters</li>
-<li>A command to clear the shelve is implemented as a flag (<tt class="docutils literal"><span class="pre">.clear</span></tt>).</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"><span class="pre">.delete</span></tt>).</li>
-<li>There is an option with default (<tt class="docutils literal"><span class="pre">.filename=conf.shelve</span></tt>) to store
+(<tt class="docutils literal">.delete</tt>).</li>
+<li>There is an option with default (<tt class="docutils literal">.filename=conf.shelve</tt>) to store
the filename of the shelve.</li>
<li>All things considered, the code looks like a poor man object oriented
interface implemented with a chain of elifs instead of methods. Of course,
<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can do better than that, but let me start from a low-level approach
first.</li>
</ol>
-<p>If you run <tt class="docutils literal"><span class="pre">ishelve.py</span></tt> without arguments you get the following
+<p>If you run <tt class="docutils literal">ishelve.py</tt> without arguments you get the following
message:</p>
<pre class="literal-block">
$ python ishelve.py
no arguments passed, use .help to see the available commands
</pre>
-<p>If you run <tt class="docutils literal"><span class="pre">ishelve.py</span></tt> with the option <tt class="docutils literal"><span class="pre">.h</span></tt> (or any abbreviation
-of <tt class="docutils literal"><span class="pre">.help</span></tt>) you get:</p>
+<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
@@ -1296,7 +1296,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
@@ -1307,16 +1307,16 @@ form - normal users expect options to be optional. You should avoid
the use of required options whenever possible.</em> Notice that since
<a class="reference external" href="http://argparse.googlecode.com">argparse</a> supports them, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can manage them too, but not directly.</li>
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to
-define generalized two-value flags with values different from <tt class="docutils literal"><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
@@ -1327,14 +1327,14 @@ the <a class="reference external" href="in-writing">advanced usage document</a>
<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"><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>
+<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"><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
+<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(...):
@@ -1347,22 +1347,22 @@ 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"><span class="pre">description</span></tt> attribute, it is possible
+<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"><span class="pre">main</span></tt> function is used as description).</p>
+<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"><span class="pre">/</span></tt>) is used
+<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"><span class="pre">-</span></tt>) is kept to keep the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> option
+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,
-as seen in the <tt class="docutils literal"><span class="pre">ishelve</span></tt> example.</p>
+as seen in the <tt class="docutils literal">ishelve</tt> example.</p>
<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
-invoking the <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> utility function:</p>
+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):
@@ -1371,17 +1371,17 @@ invoking the <tt class="docutils literal"><span class="pre">plac.parser_from</sp
&gt;&gt;&gt; print(plac.parser_from(main)) #doctest: +ELLIPSIS
ArgumentParser(prog=...)
</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
-to the main function as an attribute. When <tt class="docutils literal"><span class="pre">plac.call(func)</span></tt> is
+<p>Internally <tt class="docutils literal">plac.call</tt> uses <tt class="docutils literal">plac.parser_from</tt> and adds the parser
+to the main function as an attribute. When <tt class="docutils literal">plac.call(func)</tt> is
invoked multiple time, the parser is re-used and not rebuilt from scratch again.</p>
-<p>I use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> in the unit tests of the module, but regular
+<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"><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 except the addition of the <tt class="docutils literal"><span class="pre">.p</span></tt>
+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">
@@ -1422,15 +1422,15 @@ quite advanced tool with a domain of applicability which far exceeds
the realm of command-line arguments parsers.</p>
<p>Version 0.5 of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> doubled the code base and the documentation: it is
based on the idea of using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to implement command-line interpreters,
-i.e. something akin to the <tt class="docutils literal"><span class="pre">cmd</span></tt> module in the standard library, only better.
+i.e. something akin to the <tt class="docutils literal">cmd</tt> module in the standard library, only better.
The new features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> are described in the <a class="reference external" href="in-writing">advanced usage document</a> .
-They are implemented in a separated module (<tt class="docutils literal"><span class="pre">plac_ext.py</span></tt>), since
-they require Python 2.5 to work, whereas <tt class="docutils literal"><span class="pre">plac_core.py</span></tt> only requires
+They are implemented in a separated module (<tt class="docutils literal">plac_ext.py</tt>), since
+they require Python 2.5 to work, whereas <tt class="docutils literal">plac_core.py</tt> only requires
Python 2.3.</p>
</div>
<div class="section" id="trivia-the-story-behind-the-name">
<h1><a class="toc-backref" href="#id14">Trivia: the story behind the name</a></h1>
-<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> project started very humble: I just wanted to make
+<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> project started very humbly: I just wanted to make
easy_installable my old <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, and to publish it on PyPI.
The original name of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was optionparser and the idea behind it was
to build an <a class="reference external" href="http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser">OptionParser</a> object from the docstring of the module.
@@ -1445,8 +1445,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
diff --git a/plac/doc/plac.pdf b/plac/doc/plac.pdf
index b8b99fe..5a6c78c 100644
--- a/plac/doc/plac.pdf
+++ b/plac/doc/plac.pdf
@@ -1930,7 +1930,7 @@ endobj
117 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/gcode/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
@@ -1975,7 +1975,7 @@ endobj
120 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/gcode/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
@@ -1990,7 +1990,7 @@ endobj
121 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/gcode/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
@@ -2537,7 +2537,7 @@ endobj
155 0 obj
<< /A << /S /URI
/Type /Action
- /URI (file:///home/msimionato/Dropbox/gcode/plac/in-writing) >>
+ /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/in-writing) >>
/Border [ 0
0
0 ]
@@ -2556,9 +2556,9 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 83.64556
+ /Rect [ 83.6829
360.5936
- 105.7082
+ 105.7829
372.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -2571,7 +2571,7 @@ endobj
/Border [ 0
0
0 ]
- /Rect [ 446.6
+ /Rect [ 446.5627
360.5936
502.5727
372.5936 ]
@@ -2777,7 +2777,7 @@ endobj
% 'R169': class PDFInfo
169 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100712091726-01'00')
+ /CreationDate (D:20100730114415-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
@@ -3049,7 +3049,7 @@ q
1 0 0 1 91.03937 3 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (June 2010) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (July 2010) Tj T* ET
Q
Q
q
@@ -5867,7 +5867,7 @@ Q
q
1 0 0 1 62.69291 309.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
+BT 1 0 0 1 0 52.82 Tm .979984 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 humbly: 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
@@ -6225,117 +6225,117 @@ xref
0000029570 00000 n
0000029822 00000 n
0000030075 00000 n
-0000030349 00000 n
-0000030602 00000 n
-0000030856 00000 n
-0000031130 00000 n
-0000031405 00000 n
-0000031659 00000 n
-0000031896 00000 n
-0000032417 00000 n
-0000032735 00000 n
-0000032989 00000 n
-0000033278 00000 n
-0000033530 00000 n
-0000033782 00000 n
-0000034036 00000 n
-0000034290 00000 n
-0000034529 00000 n
-0000034920 00000 n
-0000035179 00000 n
-0000035436 00000 n
-0000035688 00000 n
-0000035942 00000 n
-0000036200 00000 n
-0000036452 00000 n
-0000036710 00000 n
-0000036964 00000 n
-0000037217 00000 n
-0000037478 00000 n
-0000037732 00000 n
-0000037986 00000 n
-0000038240 00000 n
-0000038494 00000 n
-0000038746 00000 n
-0000038998 00000 n
-0000039251 00000 n
-0000039505 00000 n
-0000039759 00000 n
-0000040013 00000 n
-0000040267 00000 n
-0000040542 00000 n
-0000040796 00000 n
-0000041083 00000 n
-0000041337 00000 n
-0000041648 00000 n
-0000041900 00000 n
-0000042152 00000 n
-0000042441 00000 n
-0000042693 00000 n
-0000042945 00000 n
-0000043203 00000 n
-0000043442 00000 n
-0000044069 00000 n
-0000044233 00000 n
-0000044507 00000 n
-0000044636 00000 n
-0000044830 00000 n
-0000045041 00000 n
-0000045251 00000 n
-0000045473 00000 n
-0000045671 00000 n
-0000045876 00000 n
-0000046069 00000 n
-0000046268 00000 n
-0000046465 00000 n
-0000046679 00000 n
-0000046877 00000 n
-0000047088 00000 n
-0000047280 00000 n
-0000047463 00000 n
-0000047722 00000 n
-0000057166 00000 n
-0000062190 00000 n
-0000066487 00000 n
-0000070589 00000 n
-0000075366 00000 n
-0000079603 00000 n
-0000084197 00000 n
-0000089365 00000 n
-0000094802 00000 n
-0000099416 00000 n
-0000103755 00000 n
-0000107281 00000 n
-0000111473 00000 n
-0000118371 00000 n
-0000125678 00000 n
-0000132641 00000 n
-0000140973 00000 n
-0000141300 00000 n
-0000141379 00000 n
-0000141458 00000 n
-0000141537 00000 n
-0000141616 00000 n
-0000141695 00000 n
-0000141774 00000 n
-0000141853 00000 n
-0000141932 00000 n
-0000142011 00000 n
-0000142091 00000 n
-0000142171 00000 n
-0000142251 00000 n
-0000142331 00000 n
-0000142411 00000 n
-0000142491 00000 n
-0000142571 00000 n
+0000030353 00000 n
+0000030606 00000 n
+0000030860 00000 n
+0000031138 00000 n
+0000031417 00000 n
+0000031671 00000 n
+0000031908 00000 n
+0000032429 00000 n
+0000032747 00000 n
+0000033001 00000 n
+0000033290 00000 n
+0000033542 00000 n
+0000033794 00000 n
+0000034048 00000 n
+0000034302 00000 n
+0000034541 00000 n
+0000034932 00000 n
+0000035191 00000 n
+0000035448 00000 n
+0000035700 00000 n
+0000035954 00000 n
+0000036212 00000 n
+0000036464 00000 n
+0000036722 00000 n
+0000036976 00000 n
+0000037229 00000 n
+0000037490 00000 n
+0000037744 00000 n
+0000037998 00000 n
+0000038252 00000 n
+0000038506 00000 n
+0000038758 00000 n
+0000039010 00000 n
+0000039263 00000 n
+0000039517 00000 n
+0000039771 00000 n
+0000040025 00000 n
+0000040279 00000 n
+0000040558 00000 n
+0000040811 00000 n
+0000041101 00000 n
+0000041355 00000 n
+0000041666 00000 n
+0000041918 00000 n
+0000042170 00000 n
+0000042459 00000 n
+0000042711 00000 n
+0000042963 00000 n
+0000043221 00000 n
+0000043460 00000 n
+0000044087 00000 n
+0000044251 00000 n
+0000044525 00000 n
+0000044654 00000 n
+0000044848 00000 n
+0000045059 00000 n
+0000045269 00000 n
+0000045491 00000 n
+0000045689 00000 n
+0000045894 00000 n
+0000046087 00000 n
+0000046286 00000 n
+0000046483 00000 n
+0000046697 00000 n
+0000046895 00000 n
+0000047106 00000 n
+0000047298 00000 n
+0000047481 00000 n
+0000047740 00000 n
+0000057184 00000 n
+0000062208 00000 n
+0000066505 00000 n
+0000070607 00000 n
+0000075384 00000 n
+0000079621 00000 n
+0000084215 00000 n
+0000089383 00000 n
+0000094820 00000 n
+0000099434 00000 n
+0000103773 00000 n
+0000107299 00000 n
+0000111491 00000 n
+0000118389 00000 n
+0000125696 00000 n
+0000132659 00000 n
+0000140991 00000 n
+0000141318 00000 n
+0000141397 00000 n
+0000141476 00000 n
+0000141555 00000 n
+0000141634 00000 n
+0000141713 00000 n
+0000141792 00000 n
+0000141871 00000 n
+0000141950 00000 n
+0000142029 00000 n
+0000142109 00000 n
+0000142189 00000 n
+0000142269 00000 n
+0000142349 00000 n
+0000142429 00000 n
+0000142509 00000 n
+0000142589 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\272\236\026\327\302X\352+\277U\362\364\256\2152\272) (\272\236\026\327\302X\352+\277U\362\364\256\2152\272)]
+ [(h\344_g\030\351%P\347\313k\017\322\274~\366) (h\344_g\030\351%P\347\313k\017\322\274~\366)]
/Info 169 0 R
/Root 168 0 R
/Size 221 >>
startxref
-142620
+142638
%%EOF
diff --git a/plac/doc/plac.txt b/plac/doc/plac.txt
index 7ae8e44..ab797b6 100644
--- a/plac/doc/plac.txt
+++ b/plac/doc/plac.txt
@@ -730,7 +730,7 @@ Python 2.3.
Trivia: the story behind the name
-----------------------------------------
-The plac_ project started very humble: I just wanted to make
+The plac_ project started very humbly: I just wanted to make
easy_installable my old optionparse_ recipe, and to publish it on PyPI.
The original name of plac_ was optionparser and the idea behind it was
to build an OptionParser_ object from the docstring of the module.
diff --git a/plac/doc/plac_adv.html b/plac/doc/plac_adv.html
index 5629997..45328f4 100644
--- a/plac/doc/plac_adv.html
+++ b/plac/doc/plac_adv.html
@@ -6,7 +6,7 @@
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title>Advanced usages of plac</title>
<meta name="author" content="Michele Simionato" />
-<meta name="date" content="July 2010" />
+<meta name="date" content="August 2010" />
<style type="text/css">
.first {
@@ -424,7 +424,7 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
</tr>
<tr><th class="docinfo-name">Date:</th>
-<td>July 2010</td></tr>
+<td>August 2010</td></tr>
<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/plac">http://pypi.python.org/pypi/plac</a></td>
</tr>
<tr class="field"><th class="docinfo-name">Project page:</th><td class="field-body"><a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">http://micheles.googlecode.com/hg/plac/doc/plac.html</a></td>
@@ -458,8 +458,11 @@ basic documentation.</em></p>
<li><a class="reference internal" href="#long-running-commands" id="id11">Long running commands</a></li>
<li><a class="reference internal" href="#threaded-commands" id="id12">Threaded commands</a></li>
<li><a class="reference internal" href="#running-commands-as-external-processes" id="id13">Running commands as external processes</a></li>
-<li><a class="reference internal" href="#summary" id="id14">Summary</a></li>
-<li><a class="reference internal" href="#appendix-custom-annotation-objects" id="id15">Appendix: custom annotation objects</a></li>
+<li><a class="reference internal" href="#managing-the-output-of-concurrent-commands" id="id14">Managing the output of concurrent commands</a></li>
+<li><a class="reference internal" href="#parallel-computing-with-plac" id="id15">Parallel computing with plac</a></li>
+<li><a class="reference internal" href="#the-plac-server" id="id16">The plac server</a></li>
+<li><a class="reference internal" href="#summary" id="id17">Summary</a></li>
+<li><a class="reference internal" href="#appendix-custom-annotation-objects" id="id18">Appendix: custom annotation objects</a></li>
</ul>
</div>
<div class="section" id="introduction">
@@ -479,12 +482,15 @@ 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>
+could use it to script your building procedure. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> also supports
+parallel execution of multiple commands and can be used as
+task manager and monitor. It is also quite easy to build a GUI
+or a Web application on top of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. When speaking of things
+you can do with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, your imagination is the only limit!</p>
</div>
<div class="section" id="from-scripts-to-interactive-applications">
<h1><a class="toc-backref" href="#id2">From scripts to interactive applications</a></h1>
-<p>Command-line scripts have many advantages, but are no substitute
+<p>Command-line scripts have many advantages, but they are no substitute
for interactive applications.
In particular, if you have a script with a large startup time which must be run
multiple times, it is best to turn it into an interactive application,
@@ -703,7 +709,9 @@ languages as the ones you can implement with <a class="reference external" href=
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>), 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>
+interpreted. For more flexibility, it is even possible to pass to the
+interpreter a custom split function with signature <tt class="docutils literal">split(line,
+commentchar)</tt>.</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
@@ -810,7 +818,7 @@ container may have initialization/finalization hooks (<tt class="docutils litera
and dispatch hooks (<tt class="docutils literal">__missing__</tt>, invoked for invalid command names).
Moreover, only when using command containers <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to provide
automatic autocompletion of commands.</p>
-<p>Rhe shelve interface can be rewritten in an object-oriented way as follows:</p>
+<p>The shelve interface can be rewritten in an object-oriented way as follows:</p>
<pre class="literal-block">
# ishelve2.py
import shelve, os, sys, plac
@@ -850,11 +858,10 @@ class ShelveInterface(object):
yield 'deleting %s' % name
del self.sh[name] # no error checking
-main = ShelveInterface # the main 'function' can also be a class!
+main = ShelveInterface # useful for the tests
if __name__ == '__main__':
- shelve_interface = plac.call(main)
- plac.Interpreter(shelve_interface).interact()
+ plac.Interpreter(plac.call(ShelveInterface)).interact()
</pre>
<p><tt class="docutils literal">plac.Interpreter</tt> objects wrap context manager objects
@@ -927,14 +934,13 @@ you pass the <tt class="docutils literal">verbose</tt> flag to the <tt class="do
<h1><a class="toc-backref" href="#id7">Readline support</a></h1>
<p>Starting from release 0.6 <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> offers full readline support. That
means that if your Python was compiled with readline support you get
-autocompletion and persistent command history for free.
-By default all commands are autocomplete in a case sensitive way.
-If you want to add new words to the autocompletion set, or you want
-to change the location of the <tt class="docutils literal">.history</tt> file, or to change the
-case sensitivirt, or you want to change the prompt, the way to do
-it is to pass a <tt class="docutils literal">plac.ReadlineInput</tt> object to the interpreter.
-Here is an example, assuming you want to build a database interface
-understanding SQL commands:</p>
+autocompletion and persistent command history for free. By default
+all commands are autocomplete in a case sensitive way. If you want to
+add new words to the autocompletion set, or you want to change the
+location of the <tt class="docutils literal">.history</tt> file, or to change the case sensitivity,
+the way to go is to pass a <tt class="docutils literal">plac.ReadlineInput</tt> object to the
+interpreter. Here is an example, assuming you want to build a
+database interface understanding SQL commands:</p>
<pre class="literal-block">
import os, plac
from sqlalchemy.ext.sqlsoup import SqlSoup
@@ -949,18 +955,22 @@ class SqlInterface(object):
commands = ['SELECT']
def __init__(self, dsn):
self.soup = SqlSoup(dsn)
- def SELECT(self, *args):
- sql = 'SELECT ' + ' '.join(args)
+ def SELECT(self, argstring):
+ sql = 'SELECT ' + argstring
for row in self.soup.bind.execute(sql):
yield str(row) # the formatting can be much improved
rl_input = plac.ReadlineInput(
- COMPLETIONS, prompt='sql&gt; ',
- histfile=os.path.expanduser('~/.sql_interface.history'),
+ COMPLETIONS, histfile=os.path.expanduser('~/.sql_interface.history'),
case_sensitive=False)
+def split_on_first_space(line, commentchar):
+ return line.strip().split(' ', 1) # ignoring comments
+
if __name__ == '__main__':
- plac.Interpreter(plac.call(SqlInterface)).interact(rl_input)
+ si = plac.call(SqlInterface)
+ i = plac.Interpreter(si, split=split_on_first_space)
+ i.interact(rl_input, prompt='sql&gt; ')
</pre>
<p>Here is an example of usage:</p>
@@ -979,8 +989,9 @@ and recorded and saved in the file <tt class="docutils literal"><span class="pre
exiting from the command-line interface.</p>
<p>If the readline library is not available, my suggestion is to use the
<a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a> tool which provides similar features, at least on Unix-like
-platforms. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> should also work fine on Windows with the pyreadline
-library (I do not use Windows, so this part is very little tested).
+platforms. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> should also work fine on Windows with the <a href="#id19"><span class="problematic" id="id20">pyreadline_</span></a>
+library (I do not use Windows, so this part is very little tested: I
+tried it only once and it worked, but your mileage may vary).
For people worried about licenses, I will notice that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses the
readline library only if available, it does not include it and it does
not rely on it in any fundamental way, so that the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> licence does
@@ -1023,7 +1034,7 @@ arguments with a fixed number of choices, type conversion and all the
features provided of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> which should be reimplemented from scratch
using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
<p>Moreover at the moment <tt class="docutils literal">plac</tt> also understands command abbreviations.
-However, this feature should be considered deprecated and may disappear in
+However, this feature may disappear in
future releases. It was meaningful in the past, when <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> did not support
readline.</p>
<p>Notice that if an abbreviation is ambiguous, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> warns you:</p>
@@ -1044,7 +1055,7 @@ even a command container class.</p>
<p>For instance, suppose you want to execute a script containing commands
defined in the <tt class="docutils literal">ishelve2</tt> module like the following one:</p>
<pre class="literal-block">
-#!ishelve2.py -c ~/conf.shelve
+#!ishelve2.py:ShelveInterface -c ~/conf.shelve
set a 1
del a
del a # intentional error
@@ -1053,8 +1064,12 @@ del a # intentional error
<p>The first line of the <tt class="docutils literal">.plac</tt> script contains the name of the
python module containing the plac interpreter and the arguments
which must be passed to its main function in order to be able
-to instantiate an interpreter object. The other lines contains
-commands. Then you can run the script as follows:</p>
+to instantiate an interpreter object. In this case I appended
+<tt class="docutils literal">:ShelveInterface</tt> to the name of the module to specify the
+object that must be imported: if not specified, by default the
+object named 'main' is imported.
+The other lines contains commands.
+You can run the script as follows:</p>
<pre class="literal-block">
$ plac_runner.py --batch ishelve2.plac
setting a=1
@@ -1090,7 +1105,7 @@ b = 2
<p>Now you can cut and paste the interactive session an turns into into
a <tt class="docutils literal">.placet</tt> file like the following:</p>
<pre class="literal-block">
-#!ishelve2.py -configfile=~/test.shelve
+#!ishelve2.py:ShelveInterface -configfile=~/test.shelve
i&gt; del
deleting everything
i&gt; set a 1
@@ -1135,8 +1150,128 @@ setting a=1
$ plac ishelve.py .show
a=1
</pre>
-<p>Notice that it non-interactive mode the runner just invokes <tt class="docutils literal">plac.call</tt>
+<p>Notice that in non-interactive mode the runner just invokes <tt class="docutils literal">plac.call</tt>
on the <tt class="docutils literal">main</tt> object of the Python module.</p>
+<!-- -->
+<blockquote>
+<p>Multiline support and Emacs integration</p>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is optimized for the simplest use case and by default it provide
+support for simple command-line languages where a command take
+a single line. This is the simplest case: it is easy to keep
+track of the line number and to print it in the error message, if
+there is some error in a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> script. Starting from release 0.7
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is beginning to support multiline input: it is now possible
+to define command-line languages with commands spanning multiple
+lines. The topical use case is the implementation of a tool
+to interact with a relational database: the tool must be able to send
+complex SQL queries spanning multiple lines to the backend.
+To support multiline input the <tt class="docutils literal">Interpreter</tt> class provides
+a method <tt class="docutils literal">multiline(stdin=sys.stdin, <span class="pre">terminator=';',</span> verbose=False)</tt>
+which reads input from <tt class="docutils literal">stdin</tt> until the terminator character
+(by default a semicolon) is reached.</p>
+<p>Since the Python readline module does not expose the
+multiline functionality of the underlying C library (which is there),
+<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> multiline mode does not have readline functionality. This is
+not a big deal really, because if you are writing multiple line
+commands you don't really want to type them at the command-line. It is
+much better to use a real editor to type them, and to call <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> from
+the editor. Since I use Emacs I will give the recipe to integrate
+Emacs with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: something equivalent can be done for vi and for
+other editors/IDEs.</p>
+<p>The multiline mode can be enabled by invoking the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> runner with
+the <tt class="docutils literal"><span class="pre">-m</span></tt> option. Since the multiline mode is intended for use with
+Emacs in inferior mode, it does not print any prompt. Here is an example
+of usage:</p>
+<pre class="literal-block">
+$ plac -m ishelve2.py
+set a 1;
+setting a=1
+show a;
+a = 1
+</pre>
+<p>To integrate <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> with Emacs, enters the following lines in your
+.emacs:</p>
+<pre class="literal-block">
+;;; Emacs-plac integration: add the following to your .emacs
+
+(define-generic-mode 'plac-mode
+ '(&quot;#&quot;) ; comment chars
+ '(); highlighted commands
+ nil
+ '(&quot;.plac\\'&quot;); file extensions
+ nil)
+
+(add-hook 'plac-mode-hook (lambda () (local-set-key [f4] 'plac-start)))
+(add-hook 'plac-mode-hook (lambda () (local-set-key [f5] 'plac-send)))
+(add-hook 'plac-mode-hook (lambda () (local-set-key [f6] 'plac-stop)))
+
+(defconst terminator 59); ASCII code for the semicolon
+(defvar *plac-process* nil)
+
+(defun plac-start ()
+ &quot;Start an inferior plac process by inferring the script to use from the
+ shebang line&quot;
+ (interactive)
+ (let ((shebang-line
+ (save-excursion
+ (goto-line 1) (end-of-line)
+ (buffer-substring-no-properties 3 (point)))))
+ (if *plac-process* (princ &quot;plac already started&quot;)
+ (setq *plac-process*
+ (start-process
+ &quot;plac&quot; &quot;*plac*&quot; &quot;plac_runner.py&quot; &quot;-m&quot; shebang-line))))
+ (display-buffer &quot;*plac*&quot;))
+
+;(defun plac-send ()
+; &quot;Send the current region to the inferior plac process&quot;
+; (interactive)
+; (save-excursion (set-buffer &quot;*plac*&quot;) (erase-buffer))
+; (process-send-region *plac-process* (region-beginning) (region-end)))
+
+(defun current-paragraph-beg-end ()
+ &quot;Returns the extrema of the current paragraph, delimited by semicolons&quot;
+ (interactive)
+ (save-excursion
+ (let ((beg (save-excursion (goto-line 2) (point))); skip the shebang
+ (end (point-max)))
+ ;; go backward
+ (while (&gt; (point) beg)
+ (goto-char (1- (point)))
+ (if (= terminator (following-char))
+ (setq beg (point))))
+ (if (= terminator (following-char))
+ (setq beg (1+ beg)))
+ ;; go forward
+ (while (&lt; (point) end)
+ (goto-char (1+ (point)))
+ (if (= 59 (following-char))
+ (setq end (point))))
+ (if (= 59 (following-char))
+ (setq end (1+ end)))
+ (list beg end))))
+
+(defun plac-send ()
+ &quot;Send the current region to the inferior plac process&quot;
+ (interactive)
+ (save-excursion (set-buffer &quot;*plac*&quot;) (erase-buffer))
+ (let ((p (apply 'buffer-substring-no-properties (current-paragraph-beg-end))))
+ (message p)
+ (process-send-string *plac-process* (concat p &quot;\n&quot;))))
+ ;(switch-to-buffer-other-window &quot;*plac*&quot;)))
+ ;(save-excursion (set-buffer &quot;*plac*&quot;)
+ ; (set-window-start (selected-window) 1 nil))))
+
+(defun plac-stop ()
+ &quot;Stop the inferior plac process by sending to it an EOF&quot;
+ (interactive)
+ (process-send-eof *plac-process*)
+ (setq *plac-process* nil)
+ &quot;killed *plac-process*&quot;)
+
+(provide 'plac)
+
+</pre>
+</blockquote>
</div>
<div class="section" id="a-non-class-based-example">
<h1><a class="toc-backref" href="#id9">A non class-based example</a></h1>
@@ -1411,6 +1546,9 @@ Imported 400 lines
i&gt; .output 1
&lt;ThreadedTask 1 [import_file file1] FINISHED&gt;
</pre>
+<p>You can even skip the number argument: then <tt class="docutils literal">.output</tt> will the return
+the output of the last launched command (the special commands like .output
+do not count).</p>
<p>You can launch many tasks one after the other:</p>
<pre class="literal-block">
i&gt; import_file file2
@@ -1513,8 +1651,227 @@ loop, so that the command is closed properly.</p>
and it is safer than using threads, so it is the recommended approach
unless you are working on Windows.</p>
</div>
+<div class="section" id="managing-the-output-of-concurrent-commands">
+<h1><a class="toc-backref" href="#id14">Managing the output of concurrent commands</a></h1>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> acts as a command-line task launcher and can be used as the base
+to build a GUI-based task launcher and task monitor. To this aim the
+interpreter class provides a <tt class="docutils literal">.submit</tt> method which returns a task
+object and a <tt class="docutils literal">.tasks</tt> method returning the list of all the tasks
+submitted to the interpreter. The <tt class="docutils literal">submit</tt> method does not start the task
+and thus it is nonblocking.
+Each task has an <tt class="docutils literal">.outlist</tt> attribute which is a list
+storing the value yielded by the generator underlying the task (the
+<tt class="docutils literal">None</tt> values are skipped though): the <tt class="docutils literal">.outlist</tt> grows as the
+task runs and more values are yielded. Accessing the <tt class="docutils literal">.outlist</tt> is
+nonblocking and can be done freely.
+Finally there is a <tt class="docutils literal">.result</tt>
+property which waits for the task to finish and returns the last yielded
+value or raises an exception.</p>
+<p>Here is some example code to visualize the output of the FakeImporter
+in Tkinter (I chose Tkinter because it is easy to use and it is
+in the standard library, but you can use any GUI):</p>
+<pre class="literal-block">
+from Tkinter import *
+from importer3 import FakeImporter
+
+def taskwidget(root, task, tick=500):
+ &quot;A Label widget showing the output of a task every 500 ms&quot;
+ sv = StringVar(root)
+ lb = Label(root, textvariable=sv)
+ def show_outlist():
+ try:
+ out = task.outlist[-1]
+ except IndexError: # no output yet
+ out = ''
+ sv.set('%s %s' % (task, out))
+ root.after(tick, show_outlist)
+ root.after(0, show_outlist)
+ return lb
+
+def monitor(tasks):
+ root = Tk()
+ for task in tasks:
+ task.run()
+ taskwidget(root, task).pack()
+ root.mainloop()
+
+if __name__ == '__main__':
+ import plac
+ with plac.Interpreter(plac.call(FakeImporter)) as i:
+ tasks = [i.submit('import_file f1'), i.submit('import_file f2')]
+ monitor(tasks)
+
+</pre>
+</div>
+<div class="section" id="parallel-computing-with-plac">
+<h1><a class="toc-backref" href="#id15">Parallel computing with plac</a></h1>
+<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is certainly not intended as a tool for parallel computing, but
+still you can use it to launch a set of commands and to collect the
+results, similarly to the MapReduce pattern recently popularized by
+Google. In order to give an example, I will consider the &quot;Hello
+World&quot; of parallel computing, i.e. the computation of pi with
+independent processes. There is a huge number of algorithms to
+compute pi; here I will describe a trivial one chosen for simplicity,
+not per efficienty. The trick is to consider the first quadrant of a
+circle with radius 1 and to extract a number of points <tt class="docutils literal">(x, y)</tt> with
+<tt class="docutils literal">x</tt> and <tt class="docutils literal">y</tt> random variables in the interval <tt class="docutils literal">[0,1]</tt>. The
+probability of extracting a number inside the quadrant (i.e. with
+<tt class="docutils literal">x^2 + y^2 &lt; 1</tt>) is proportional to the area of the quadrant
+(i.e. <tt class="docutils literal">pi/4</tt>). The value of <tt class="docutils literal">pi</tt> therefore can be extracted by
+multiplying by 4 the ratio between the number of points in the
+quadrant versus the total number of points <tt class="docutils literal">N</tt>, for <tt class="docutils literal">N</tt> large:</p>
+<pre class="literal-block">
+def calc_pi(N):
+ inside = 0
+ for j in xrange(N):
+ x, y = random(), random()
+ if x*x + y*y &lt; 1:
+ inside += 1
+ return (4.0 * inside) / N
+</pre>
+<p>The algorithm is trivially parallelizable: if you have n CPUs, you can
+compute pi n times with N/n iterations, sum the results and divide the total
+by n. I have a Macbook with two cores, therefore I would expect a speedup
+factor of 2 with respect to a sequential computation. Moreover, I would
+expect a threaded computation to be even slower than a sequential
+computation, due to the GIL and the scheduling overhead.</p>
+<p>Here is a script implementing the algorithm and working in three different
+modes (parallel mode, threaded mode and sequential mode) depending on a
+<tt class="docutils literal">mode</tt> option:</p>
+<pre class="literal-block">
+from __future__ import with_statement
+from random import random
+import multiprocessing
+import plac
+
+class PiCalculator(object):
+ &quot;&quot;&quot;Compute pi in parallel with threads or processes&quot;&quot;&quot;
+
+ &#64;plac.annotations(
+ npoints=('number of integration points', 'positional', None, int),
+ mode=('sequential|parallel|threaded', 'option', 'm', str, 'SPT'))
+ def __init__(self, npoints, mode='S'):
+ self.npoints = npoints
+ if mode == 'P':
+ self.mpcommands = ['calc_pi']
+ elif mode == 'T':
+ self.thcommands = ['calc_pi']
+ elif mode == 'S':
+ self.commands = ['calc_pi']
+ self.n_cpu = multiprocessing.cpu_count()
+
+ def submit_tasks(self):
+ self.i = plac.Interpreter(self).__enter__()
+ return [self.i.submit('calc_pi %d' % (self.npoints / self.n_cpu))
+ for _ in range(self.n_cpu)]
+
+ def close(self):
+ self.i.close()
+
+ &#64;plac.annotations(
+ npoints=('npoints', 'positional', None, int))
+ def calc_pi(self, npoints):
+ counts = 0
+ for j in xrange(npoints):
+ n, r = divmod(j, 1000000)
+ if r == 0:
+ yield '%dM iterations' % n
+ x, y = random(), random()
+ if x*x + y*y &lt; 1:
+ counts += 1
+ yield (4.0 * counts)/npoints
+
+ def run(self):
+ tasks = self.i.tasks()
+ for t in tasks:
+ t.run()
+ try:
+ total = 0
+ for task in tasks:
+ total += task.result
+ except: # the task was killed
+ print tasks
+ return
+ return total / self.n_cpu
+
+if __name__ == '__main__':
+ pc = plac.call(PiCalculator)
+ pc.submit_tasks()
+ try:
+ import time; t0 = time.time()
+ print '%f in %f seconds ' % (pc.run(), time.time() - t0)
+ finally:
+ pc.close()
+
+</pre>
+<p>Notice the <tt class="docutils literal">submit_tasks</tt> method, which instantiates and initializes a
+<tt class="docutils literal">plac.Interpreter</tt> object and submits a number of commands corresponding
+to the number of available CPUs. The <tt class="docutils literal">calc_pi</tt> command yield a log
+message every million of interactions, just to monitor the progress of
+the computation. The <tt class="docutils literal">run</tt> method starts all the submitted commands
+in parallel and sums the results. It returns the average value of <tt class="docutils literal">pi</tt>
+after the slowest CPU has finished its job (if the CPUs are equal and
+equally busy they should finish more or less at the same time).</p>
+<p>Here are the results on my old Macbook with Ubuntu 10.04 and Python 2.6,
+for 10 million of iterations:</p>
+<pre class="literal-block">
+$ python picalculator.py -mP 10000000
+3.141904 in 5.744545 seconds
+$ python picalculator.py -mT 10000000
+3.141272 in 13.875645 seconds
+$ python picalculator.py -mS 10000000
+3.141586 in 11.353841 seconds
+</pre>
+<p>As you see using processes one gets a 2x speedup indeed, where the threaded
+mode is some 20% slower than the sequential mode.</p>
+</div>
+<div class="section" id="the-plac-server">
+<h1><a class="toc-backref" href="#id16">The plac server</a></h1>
+<p>A command-line oriented interface can be easily converted into a
+socket-based interface. Starting from release 0.7 plac features
+a builtin server which is able to accept commands from multiple
+clients and to execute them. The server works by instantiating
+a separate interpreter for each client, so that if a client interpreter
+dies for any reason the other interpreters keep working.
+To avoid external dependencies the server is based on the <tt class="docutils literal">asynchat</tt>
+module in the standard library, but it would not be difficult to
+replace the server with a different one (for instance, a Twisted server).
+Since <tt class="docutils literal">asynchat</tt>-based servers are asynchronous, any blocking command
+in the interpreter should be run in a separated process or thread.
+The default port for the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> server is 2199, and the command to
+signal end-of-connection is EOF.
+For instance, here is how you could manage remote import on a database:</p>
+<pre class="literal-block">
+import plac
+from importer2 import FakeImporter
+
+def main(port=2199):
+ main = FakeImporter('dsn')
+ plac.Interpreter(main).start_server(port)
+
+if __name__ == '__main__':
+ plac.call(main)
+
+</pre>
+<p>You can connect to the server with <tt class="docutils literal">telnet</tt> on port 2199, as follows:</p>
+<pre class="literal-block">
+$ telnet localhost 2199
+Trying ::1...
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+i&gt; import_file f1
+i&gt; .list
+&lt;ThreadedTask 1 [import_file f1] RUNNING&gt;
+i&gt; .out
+Imported 100 lines
+Imported 200 lines
+i&gt; EOF
+Connection closed by foreign host.
+</pre>
+</div>
<div class="section" id="summary">
-<h1><a class="toc-backref" href="#id14">Summary</a></h1>
+<h1><a class="toc-backref" href="#id17">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
@@ -1533,11 +1890,13 @@ or use the <tt class="docutils literal">Interpreter.doctest</tt> feature;</li>
<li>long running command can be executed in the background as threads or
processes: just declare them in the lists <tt class="docutils literal">thcommands</tt> and <tt class="docutils literal">mpcommands</tt>
respectively.</li>
+<li>the <tt class="docutils literal">.start_server</tt> method starts an asynchronous server on the
+given port number (default 2199)</li>
</ol>
<p>Moreover, remember that <tt class="docutils literal">plac_runner.py</tt> is your friend.</p>
</div>
<div class="section" id="appendix-custom-annotation-objects">
-<h1><a class="toc-backref" href="#id15">Appendix: custom annotation objects</a></h1>
+<h1><a class="toc-backref" href="#id18">Appendix: custom annotation objects</a></h1>
<p>Internally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses an <tt class="docutils literal">Annotation</tt> class to convert the tuples
in the function signature into annotation objects, i.e. objects with
six attributes <tt class="docutils literal">help, kind, short, type, choices, metavar</tt>.</p>
@@ -1592,6 +1951,12 @@ annotations from a configuration file or from a database, but I expect such
use cases to be quite rare: the default mechanism should work
pretty well for most users.</p>
</div>
+<div class="system-messages section">
+<h1>Docutils System Messages</h1>
+<div class="system-message" id="id19">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">plac_adv.txt</tt>, line 438); <em><a href="#id20">backlink</a></em></p>
+Unknown target name: &quot;pyreadline&quot;.</div>
+</div>
</div>
</body>
</html>
diff --git a/plac/doc/plac_adv.pdf b/plac/doc/plac_adv.pdf
index 7abe4f7..ce9ae5f 100644
--- a/plac/doc/plac_adv.pdf
+++ b/plac/doc/plac_adv.pdf
@@ -105,10 +105,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 45 0 R
+ /Dest [ 48 0 R
/XYZ
62.69291
- 236.0236
+ 182.0236
0 ]
/Rect [ 62.69291
506.5936
@@ -123,10 +123,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 45 0 R
+ /Dest [ 48 0 R
/XYZ
62.69291
- 236.0236
+ 182.0236
0 ]
/Rect [ 527.0227
506.5936
@@ -141,10 +141,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 49 0 R
+ /Dest [ 58 0 R
/XYZ
62.69291
- 717.0236
+ 633.0236
0 ]
/Rect [ 62.69291
488.5936
@@ -159,10 +159,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 49 0 R
+ /Dest [ 58 0 R
/XYZ
62.69291
- 717.0236
+ 633.0236
0 ]
/Rect [ 527.0227
488.5936
@@ -177,10 +177,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 51 0 R
+ /Dest [ 60 0 R
/XYZ
62.69291
- 259.4236
+ 175.4236
0 ]
/Rect [ 62.69291
470.5936
@@ -195,10 +195,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 51 0 R
+ /Dest [ 60 0 R
/XYZ
62.69291
- 259.4236
+ 175.4236
0 ]
/Rect [ 527.0227
470.5936
@@ -213,10 +213,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 52 0 R
+ /Dest [ 72 0 R
/XYZ
62.69291
- 222.6236
+ 765.0236
0 ]
/Rect [ 62.69291
452.5936
@@ -231,10 +231,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 52 0 R
+ /Dest [ 72 0 R
/XYZ
62.69291
- 222.6236
+ 765.0236
0 ]
/Rect [ 527.0227
452.5936
@@ -249,10 +249,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 66 0 R
+ /Dest [ 75 0 R
/XYZ
62.69291
- 588.7757
+ 461.8485
0 ]
/Rect [ 62.69291
434.5936
@@ -267,10 +267,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 66 0 R
+ /Dest [ 75 0 R
/XYZ
62.69291
- 588.7757
+ 461.8485
0 ]
/Rect [ 527.0227
434.5936
@@ -285,10 +285,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 71 0 R
+ /Dest [ 80 0 R
/XYZ
62.69291
- 671.8236
+ 539.8236
0 ]
/Rect [ 62.69291
416.5936
@@ -303,10 +303,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 71 0 R
+ /Dest [ 80 0 R
/XYZ
62.69291
- 671.8236
+ 539.8236
0 ]
/Rect [ 527.0227
416.5936
@@ -321,10 +321,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 77 0 R
+ /Dest [ 86 0 R
/XYZ
62.69291
- 562.2269
+ 469.3633
0 ]
/Rect [ 62.69291
398.5936
@@ -339,10 +339,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 77 0 R
+ /Dest [ 86 0 R
/XYZ
62.69291
- 562.2269
+ 469.3633
0 ]
/Rect [ 527.0227
398.5936
@@ -357,10 +357,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 93 0 R
+ /Dest [ 102 0 R
/XYZ
62.69291
- 715.8236
+ 566.6236
0 ]
/Rect [ 62.69291
380.5936
@@ -375,10 +375,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 93 0 R
+ /Dest [ 102 0 R
/XYZ
62.69291
- 715.8236
+ 566.6236
0 ]
/Rect [ 521.4627
380.5936
@@ -393,10 +393,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 96 0 R
+ /Dest [ 116 0 R
/XYZ
62.69291
- 242.2236
+ 765.0236
0 ]
/Rect [ 62.69291
362.5936
@@ -411,10 +411,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 96 0 R
+ /Dest [ 116 0 R
/XYZ
62.69291
- 242.2236
+ 765.0236
0 ]
/Rect [ 521.4627
362.5936
@@ -429,10 +429,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 99 0 R
+ /Dest [ 119 0 R
/XYZ
62.69291
- 253.4236
+ 765.0236
0 ]
/Rect [ 62.69291
344.5936
@@ -447,10 +447,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 99 0 R
+ /Dest [ 119 0 R
/XYZ
62.69291
- 253.4236
+ 765.0236
0 ]
/Rect [ 521.4627
344.5936
@@ -465,10 +465,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 104 0 R
+ /Dest [ 123 0 R
/XYZ
62.69291
- 765.0236
+ 615.8236
0 ]
/Rect [ 62.69291
326.5936
@@ -483,10 +483,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 104 0 R
+ /Dest [ 123 0 R
/XYZ
62.69291
- 765.0236
+ 615.8236
0 ]
/Rect [ 521.4627
326.5936
@@ -501,10 +501,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 104 0 R
+ /Dest [ 125 0 R
/XYZ
62.69291
- 223.6236
+ 741.0236
0 ]
/Rect [ 62.69291
308.5936
@@ -519,10 +519,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 104 0 R
+ /Dest [ 125 0 R
/XYZ
62.69291
- 223.6236
+ 741.0236
0 ]
/Rect [ 521.4627
308.5936
@@ -537,10 +537,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 109 0 R
+ /Dest [ 128 0 R
/XYZ
62.69291
- 419.8236
+ 237.4236
0 ]
/Rect [ 62.69291
290.5936
@@ -555,10 +555,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 109 0 R
+ /Dest [ 128 0 R
/XYZ
62.69291
- 419.8236
+ 237.4236
0 ]
/Rect [ 521.4627
290.5936
@@ -573,14 +573,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 112 0 R
+ /Dest [ 130 0 R
/XYZ
62.69291
- 621.0236
+ 447.8236
0 ]
/Rect [ 62.69291
272.5936
- 108.2629
+ 283.8229
284.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -591,10 +591,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 112 0 R
+ /Dest [ 130 0 R
/XYZ
62.69291
- 621.0236
+ 447.8236
0 ]
/Rect [ 521.4627
272.5936
@@ -609,14 +609,14 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 112 0 R
+ /Dest [ 132 0 R
/XYZ
62.69291
- 354.0236
+ 571.8236
0 ]
/Rect [ 62.69291
254.5936
- 241.6029
+ 197.7329
266.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -627,10 +627,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 112 0 R
+ /Dest [ 132 0 R
/XYZ
62.69291
- 354.0236
+ 571.8236
0 ]
/Rect [ 521.4627
254.5936
@@ -639,83 +639,146 @@ endobj
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER34': class PDFDictionary
+% 'Annot.NUMBER34': class LinkAnnotation
40 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+<< /Border [ 0
0
0 ]
- /Rect [ 185.4471
- 200.5936
- 207.1062
- 212.5936 ]
+ /Contents ()
+ /Dest [ 135 0 R
+ /XYZ
+ 62.69291
+ 537.8236
+ 0 ]
+ /Rect [ 62.69291
+ 236.5936
+ 136.0629
+ 248.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER35': class PDFDictionary
+% 'Annot.NUMBER35': class LinkAnnotation
41 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+<< /Border [ 0
0
0 ]
- /Rect [ 177.6784
- 188.5936
- 199.6123
- 200.5936 ]
+ /Contents ()
+ /Dest [ 135 0 R
+ /XYZ
+ 62.69291
+ 537.8236
+ 0 ]
+ /Rect [ 521.4627
+ 236.5936
+ 532.5827
+ 248.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER36': class PDFDictionary
+% 'Annot.NUMBER36': class LinkAnnotation
42 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+<< /Border [ 0
0
0 ]
+ /Contents ()
+ /Dest [ 138 0 R
+ /XYZ
+ 62.69291
+ 715.8236
+ 0 ]
/Rect [ 62.69291
- 134.5936
- 83.81291
- 146.5936 ]
+ 218.5936
+ 108.2629
+ 230.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER37': class PDFDictionary
+% 'Annot.NUMBER37': class LinkAnnotation
43 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 138 0 R
+ /XYZ
+ 62.69291
+ 715.8236
+ 0 ]
+ /Rect [ 521.4627
+ 218.5936
+ 532.5827
+ 230.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER38': class LinkAnnotation
+44 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 138 0 R
+ /XYZ
+ 62.69291
+ 418.8236
+ 0 ]
+ /Rect [ 62.69291
+ 200.5936
+ 241.6029
+ 212.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER39': class LinkAnnotation
+45 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 138 0 R
+ /XYZ
+ 62.69291
+ 418.8236
+ 0 ]
+ /Rect [ 521.4627
+ 200.5936
+ 532.5827
+ 212.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER40': class PDFDictionary
+46 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 278.4678
- 116.5936
- 300.0328
- 128.5936 ]
+ /Rect [ 185.4471
+ 146.5936
+ 207.1062
+ 158.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER38': class PDFDictionary
-44 0 obj
+% 'Annot.NUMBER41': class PDFDictionary
+47 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 117.3573
- 104.5936
- 138.5845
- 116.5936 ]
+ /Rect [ 177.6784
+ 134.5936
+ 199.6123
+ 146.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page1': class PDFPage
-45 0 obj
+48 0 obj
% Page dictionary
<< /Annots [ 5 0 R
6 0 R
@@ -754,13 +817,16 @@ endobj
41 0 R
42 0 R
43 0 R
- 44 0 R ]
- /Contents 133 0 R
+ 44 0 R
+ 45 0 R
+ 46 0 R
+ 47 0 R ]
+ /Contents 162 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -771,38 +837,128 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER39': class PDFDictionary
-46 0 obj
+% 'Annot.NUMBER42': class PDFDictionary
+49 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 756.5936
+ 83.81291
+ 768.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER43': class PDFDictionary
+50 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 278.4678
+ 738.5936
+ 300.0328
+ 750.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER44': class PDFDictionary
+51 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 117.3573
+ 726.5936
+ 138.5845
+ 738.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER45': class PDFDictionary
+52 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://twill.idyll.org/) >>
/Border [ 0
0
0 ]
- /Rect [ 83.20457
- 744.5936
- 105.3762
- 756.5936 ]
+ /Rect [ 82.74466
+ 684.5936
+ 104.4564
+ 696.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER40': class PDFDictionary
-47 0 obj
+% 'Annot.NUMBER46': class PDFDictionary
+53 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 128.6679
- 744.5936
- 147.0079
- 756.5936 ]
+ /Rect [ 127.2882
+ 684.5936
+ 145.6282
+ 696.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER41': class PDFDictionary
-48 0 obj
+% 'Annot.NUMBER47': class PDFDictionary
+54 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 410.1674
+ 684.5936
+ 433.5592
+ 696.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER48': class PDFDictionary
+55 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 265.9578
+ 660.5936
+ 284.2978
+ 672.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER49': class PDFDictionary
+56 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 487.7492
+ 660.5936
+ 506.0892
+ 672.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER50': class PDFDictionary
+57 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
@@ -810,24 +966,30 @@ endobj
0
0 ]
/Rect [ 62.69291
- 585.5936
+ 501.5936
157.1829
- 597.5936 ]
+ 513.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page2': class PDFPage
-49 0 obj
+58 0 obj
% Page dictionary
-<< /Annots [ 46 0 R
- 47 0 R
- 48 0 R ]
- /Contents 134 0 R
+<< /Annots [ 49 0 R
+ 50 0 R
+ 51 0 R
+ 52 0 R
+ 53 0 R
+ 54 0 R
+ 55 0 R
+ 56 0 R
+ 57 0 R ]
+ /Contents 163 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -838,8 +1000,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER42': class PDFDictionary
-50 0 obj
+% 'Annot.NUMBER51': class PDFDictionary
+59 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -847,22 +1009,22 @@ endobj
0
0 ]
/Rect [ 383.9329
- 304.9936
+ 220.9936
405.0529
- 316.9936 ]
+ 232.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page3': class PDFPage
-51 0 obj
+60 0 obj
% Page dictionary
-<< /Annots [ 50 0 R ]
- /Contents 135 0 R
+<< /Annots [ 59 0 R ]
+ /Contents 164 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -874,14 +1036,14 @@ endobj
/Type /Page >>
endobj
% 'Page4': class PDFPage
-52 0 obj
+61 0 obj
% Page dictionary
-<< /Contents 136 0 R
+<< /Contents 165 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -892,8 +1054,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER43': class PDFDictionary
-53 0 obj
+% 'Annot.NUMBER52': class PDFDictionary
+62 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -901,14 +1063,14 @@ endobj
0
0 ]
/Rect [ 370.6785
- 579.3936
+ 466.3936
392.4956
- 591.3936 ]
+ 478.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER44': class PDFDictionary
-54 0 obj
+% 'Annot.NUMBER53': class PDFDictionary
+63 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -916,14 +1078,14 @@ endobj
0
0 ]
/Rect [ 455.8742
- 579.3936
+ 466.3936
477.6913
- 591.3936 ]
+ 478.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER45': class PDFDictionary
-55 0 obj
+% 'Annot.NUMBER54': class PDFDictionary
+64 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -931,14 +1093,14 @@ endobj
0
0 ]
/Rect [ 185.9351
- 364.6093
+ 251.6093
207.4695
- 376.6093 ]
+ 263.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER46': class PDFDictionary
-56 0 obj
+% 'Annot.NUMBER55': class PDFDictionary
+65 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -946,14 +1108,14 @@ endobj
0
0 ]
/Rect [ 369.8905
- 364.6093
+ 251.6093
396.425
- 376.6093 ]
+ 263.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER47': class PDFDictionary
-57 0 obj
+% 'Annot.NUMBER56': class PDFDictionary
+66 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -961,14 +1123,14 @@ endobj
0
0 ]
/Rect [ 408.8916
- 352.6093
+ 239.6093
427.2316
- 364.6093 ]
+ 251.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER48': class PDFDictionary
-58 0 obj
+% 'Annot.NUMBER57': class PDFDictionary
+67 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -976,14 +1138,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 340.6093
+ 227.6093
86.03291
- 352.6093 ]
+ 239.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER49': class PDFDictionary
-59 0 obj
+% 'Annot.NUMBER58': class PDFDictionary
+68 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -991,29 +1153,29 @@ endobj
0
0 ]
/Rect [ 92.4689
- 340.6093
+ 227.6093
114.4649
- 352.6093 ]
+ 239.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER50': class PDFDictionary
-60 0 obj
+% 'Annot.NUMBER59': class PDFDictionary
+69 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
/Border [ 0
0
0 ]
- /Rect [ 143.2929
- 328.6093
- 169.4129
- 340.6093 ]
+ /Rect [ 149.3204
+ 215.6093
+ 176.9472
+ 227.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER51': class PDFDictionary
-61 0 obj
+% 'Annot.NUMBER60': class PDFDictionary
+70 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1021,14 +1183,14 @@ endobj
0
0 ]
/Rect [ 129.6923
- 256.6093
+ 119.6093
151.4655
- 268.6093 ]
+ 131.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER52': class PDFDictionary
-62 0 obj
+% 'Annot.NUMBER61': class PDFDictionary
+71 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1036,31 +1198,31 @@ endobj
0
0 ]
/Rect [ 173.8529
- 244.6093
+ 107.6093
194.9729
- 256.6093 ]
+ 119.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page5': class PDFPage
-63 0 obj
+72 0 obj
% Page dictionary
-<< /Annots [ 53 0 R
- 54 0 R
- 55 0 R
- 56 0 R
- 57 0 R
- 58 0 R
- 59 0 R
- 60 0 R
- 61 0 R
- 62 0 R ]
- /Contents 137 0 R
+<< /Annots [ 62 0 R
+ 63 0 R
+ 64 0 R
+ 65 0 R
+ 66 0 R
+ 67 0 R
+ 68 0 R
+ 69 0 R
+ 70 0 R
+ 71 0 R ]
+ /Contents 166 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1071,8 +1233,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER53': class PDFDictionary
-64 0 obj
+% 'Annot.NUMBER62': class PDFDictionary
+73 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1080,14 +1242,14 @@ endobj
0
0 ]
/Rect [ 460.388
- 541.3457
+ 414.4185
482.0127
- 553.3457 ]
+ 426.4185 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER54': class PDFDictionary
-65 0 obj
+% 'Annot.NUMBER63': class PDFDictionary
+74 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1095,23 +1257,23 @@ endobj
0
0 ]
/Rect [ 95.32996
- 475.3457
+ 348.4185
116.857
- 487.3457 ]
+ 360.4185 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page6': class PDFPage
-66 0 obj
+75 0 obj
% Page dictionary
-<< /Annots [ 64 0 R
- 65 0 R ]
- /Contents 138 0 R
+<< /Annots [ 73 0 R
+ 74 0 R ]
+ /Contents 167 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1122,8 +1284,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER55': class PDFDictionary
-67 0 obj
+% 'Annot.NUMBER64': class PDFDictionary
+76 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
@@ -1131,14 +1293,14 @@ endobj
0
0 ]
/Rect [ 316.3528
- 636.3936
+ 504.3936
409.2453
- 648.3936 ]
+ 516.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER56': class PDFDictionary
-68 0 obj
+% 'Annot.NUMBER65': class PDFDictionary
+77 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1146,14 +1308,14 @@ endobj
0
0 ]
/Rect [ 419.1694
- 624.3936
+ 492.3936
440.4061
- 636.3936 ]
+ 504.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER57': class PDFDictionary
-69 0 obj
+% 'Annot.NUMBER66': class PDFDictionary
+78 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1161,14 +1323,14 @@ endobj
0
0 ]
/Rect [ 62.69291
- 594.3936
+ 462.3936
84.70395
- 606.3936 ]
+ 474.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER58': class PDFDictionary
-70 0 obj
+% 'Annot.NUMBER67': class PDFDictionary
+79 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1176,25 +1338,25 @@ endobj
0
0 ]
/Rect [ 293.2359
- 516.3936
+ 384.3936
316.4697
- 528.3936 ]
+ 396.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page7': class PDFPage
-71 0 obj
+80 0 obj
% Page dictionary
-<< /Annots [ 67 0 R
- 68 0 R
- 69 0 R
- 70 0 R ]
- /Contents 139 0 R
+<< /Annots [ 76 0 R
+ 77 0 R
+ 78 0 R
+ 79 0 R ]
+ /Contents 168 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1205,8 +1367,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER59': class PDFDictionary
-72 0 obj
+% 'Annot.NUMBER68': class PDFDictionary
+81 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1214,14 +1376,14 @@ endobj
0
0 ]
/Rect [ 431.7904
- 531.3936
+ 411.3936
453.7245
- 543.3936 ]
+ 423.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER60': class PDFDictionary
-73 0 obj
+% 'Annot.NUMBER69': class PDFDictionary
+82 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1229,14 +1391,14 @@ endobj
0
0 ]
/Rect [ 255.5885
- 501.3936
+ 381.3936
278.2757
- 513.3936 ]
+ 393.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER61': class PDFDictionary
-74 0 obj
+% 'Annot.NUMBER70': class PDFDictionary
+83 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/cmd.html) >>
@@ -1244,24 +1406,24 @@ endobj
0
0 ]
/Rect [ 513.6927
- 477.3936
+ 357.3936
532.1846
- 489.3936 ]
+ 369.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page8': class PDFPage
-75 0 obj
+84 0 obj
% Page dictionary
-<< /Annots [ 72 0 R
- 73 0 R
- 74 0 R ]
- /Contents 140 0 R
+<< /Annots [ 81 0 R
+ 82 0 R
+ 83 0 R ]
+ /Contents 169 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1272,8 +1434,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER62': class PDFDictionary
-76 0 obj
+% 'Annot.NUMBER71': class PDFDictionary
+85 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1281,22 +1443,22 @@ endobj
0
0 ]
/Rect [ 179.0529
- 526.7969
+ 433.9333
201.1953
- 538.7969 ]
+ 445.9333 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page9': class PDFPage
-77 0 obj
+86 0 obj
% Page dictionary
-<< /Annots [ 76 0 R ]
- /Contents 141 0 R
+<< /Annots [ 85 0 R ]
+ /Contents 170 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1307,8 +1469,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER63': class PDFDictionary
-78 0 obj
+% 'Annot.NUMBER72': class PDFDictionary
+87 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://freshmeat.net/projects/rlwrap/) >>
@@ -1316,59 +1478,59 @@ endobj
0
0 ]
/Rect [ 377.8504
- 621.3936
+ 484.1936
409.861
- 633.3936 ]
+ 496.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER64': class PDFDictionary
-79 0 obj
+% 'Annot.NUMBER73': class PDFDictionary
+88 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 242.4466
- 609.3936
- 263.7922
- 621.3936 ]
+ /Rect [ 252.3128
+ 472.1936
+ 275.3028
+ 484.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER65': class PDFDictionary
-80 0 obj
+% 'Annot.NUMBER74': class PDFDictionary
+89 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 585.3936
- 85.67624
- 597.3936 ]
+ /Rect [ 381.9869
+ 448.1936
+ 403.548
+ 460.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER66': class PDFDictionary
-81 0 obj
+% 'Annot.NUMBER75': class PDFDictionary
+90 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 211.9062
- 573.3936
- 236.7428
- 585.3936 ]
+ /Rect [ 480.1313
+ 436.1936
+ 501.4627
+ 448.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER67': class PDFDictionary
-82 0 obj
+% 'Annot.NUMBER76': class PDFDictionary
+91 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/cmd.html) >>
@@ -1376,14 +1538,14 @@ endobj
0
0 ]
/Rect [ 366.9454
- 543.3936
+ 406.1936
388.8033
- 555.3936 ]
+ 418.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER68': class PDFDictionary
-83 0 obj
+% 'Annot.NUMBER77': class PDFDictionary
+92 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/cmd.html) >>
@@ -1391,14 +1553,39 @@ endobj
0
0 ]
/Rect [ 170.8855
- 531.3936
+ 394.1936
189.7755
- 543.3936 ]
+ 406.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER69': class PDFDictionary
-84 0 obj
+% 'Page10': class PDFPage
+93 0 obj
+% Page dictionary
+<< /Annots [ 87 0 R
+ 88 0 R
+ 89 0 R
+ 90 0 R
+ 91 0 R
+ 92 0 R ]
+ /Contents 171 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER78': class PDFDictionary
+94 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1406,14 +1593,14 @@ endobj
0
0 ]
/Rect [ 390.8027
- 190.1936
+ 711.3936
435.0027
- 202.1936 ]
+ 723.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER70': class PDFDictionary
-85 0 obj
+% 'Annot.NUMBER79': class PDFDictionary
+95 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1421,14 +1608,14 @@ endobj
0
0 ]
/Rect [ 213.451
- 178.1936
+ 699.3936
237.5255
- 190.1936 ]
+ 711.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER71': class PDFDictionary
-86 0 obj
+% 'Annot.NUMBER80': class PDFDictionary
+96 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1436,14 +1623,14 @@ endobj
0
0 ]
/Rect [ 114.9429
- 154.1936
+ 675.3936
157.1829
- 166.1936 ]
+ 687.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER72': class PDFDictionary
-87 0 obj
+% 'Annot.NUMBER81': class PDFDictionary
+97 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1451,29 +1638,29 @@ endobj
0
0 ]
/Rect [ 385.0429
- 154.1936
+ 675.3936
403.3829
- 166.1936 ]
+ 687.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER73': class PDFDictionary
-88 0 obj
+% 'Annot.NUMBER82': class PDFDictionary
+98 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 514.2427
- 124.1936
- 532.3264
- 136.1936 ]
+ /Rect [ 350.6129
+ 645.3936
+ 371.7329
+ 657.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER74': class PDFDictionary
-89 0 obj
+% 'Annot.NUMBER83': class PDFDictionary
+99 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1481,45 +1668,14 @@ endobj
0
0 ]
/Rect [ 256.6729
- 94.19362
+ 627.3936
277.7929
- 106.1936 ]
+ 639.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page10': class PDFPage
-90 0 obj
-% Page dictionary
-<< /Annots [ 78 0 R
- 79 0 R
- 80 0 R
- 81 0 R
- 82 0 R
- 83 0 R
- 84 0 R
- 85 0 R
- 86 0 R
- 87 0 R
- 88 0 R
- 89 0 R ]
- /Contents 142 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 132 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER75': class PDFDictionary
-91 0 obj
+% 'Annot.NUMBER84': class PDFDictionary
+100 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1527,14 +1683,14 @@ endobj
0
0 ]
/Rect [ 149.5469
- 680.3936
+ 531.1936
172.1982
- 692.3936 ]
+ 543.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER76': class PDFDictionary
-92 0 obj
+% 'Annot.NUMBER85': class PDFDictionary
+101 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/distutils/) >>
@@ -1542,23 +1698,29 @@ endobj
0
0 ]
/Rect [ 224.3178
- 668.3936
+ 519.1936
260.8853
- 680.3936 ]
+ 531.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page11': class PDFPage
-93 0 obj
+102 0 obj
% Page dictionary
-<< /Annots [ 91 0 R
- 92 0 R ]
- /Contents 143 0 R
+<< /Annots [ 94 0 R
+ 95 0 R
+ 96 0 R
+ 97 0 R
+ 98 0 R
+ 99 0 R
+ 100 0 R
+ 101 0 R ]
+ /Contents 172 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1569,8 +1731,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER77': class PDFDictionary
-94 0 obj
+% 'Annot.NUMBER86': class PDFDictionary
+103 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1578,38 +1740,169 @@ endobj
0
0 ]
/Rect [ 381.1529
- 615.3936
+ 418.1936
423.3929
- 627.3936 ]
+ 430.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER78': class PDFDictionary
-95 0 obj
+% 'Page12': class PDFPage
+104 0 obj
+% Page dictionary
+<< /Annots [ 103 0 R ]
+ /Contents 173 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER87': class PDFDictionary
+105 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 206.7936
- 84.72012
- 218.7936 ]
+ /Rect [ 82.69291
+ 692.7662
+ 104.1773
+ 704.7662 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page12': class PDFPage
-96 0 obj
+% 'Annot.NUMBER88': class PDFDictionary
+106 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 445.3587
+ 668.7662
+ 467.5757
+ 680.7662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER89': class PDFDictionary
+107 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 157.9797
+ 656.7662
+ 179.1854
+ 668.7662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER90': class PDFDictionary
+108 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 187.0217
+ 566.7662
+ 208.3864
+ 578.7662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER91': class PDFDictionary
+109 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 440.3127
+ 542.7662
+ 462.7327
+ 554.7662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER92': class PDFDictionary
+110 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 377.541
+ 530.7662
+ 395.881
+ 542.7662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER93': class PDFDictionary
+111 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 310.5934
+ 500.7662
+ 331.7145
+ 512.7662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER94': class PDFDictionary
+112 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 138.8329
+ 369.5662
+ 159.9529
+ 381.5662 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page13': class PDFPage
+113 0 obj
% Page dictionary
-<< /Annots [ 94 0 R
- 95 0 R ]
- /Contents 144 0 R
+<< /Annots [ 105 0 R
+ 106 0 R
+ 107 0 R
+ 108 0 R
+ 109 0 R
+ 110 0 R
+ 111 0 R
+ 112 0 R ]
+ /Contents 174 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1620,15 +1913,15 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page13': class PDFPage
-97 0 obj
+% 'Page14': class PDFPage
+114 0 obj
% Page dictionary
-<< /Contents 145 0 R
+<< /Contents 175 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1639,31 +1932,31 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER79': class PDFDictionary
-98 0 obj
+% 'Annot.NUMBER95': class PDFDictionary
+115 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 182.479
- 217.9936
- 203.7662
- 229.9936 ]
+ /Rect [ 62.69291
+ 729.5936
+ 84.72012
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page14': class PDFPage
-99 0 obj
+% 'Page15': class PDFPage
+116 0 obj
% Page dictionary
-<< /Annots [ 98 0 R ]
- /Contents 146 0 R
+<< /Annots [ 115 0 R ]
+ /Contents 176 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1674,31 +1967,50 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER80': class PDFDictionary
-100 0 obj
+% 'Page16': class PDFPage
+117 0 obj
+% Page dictionary
+<< /Contents 177 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER96': class PDFDictionary
+118 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/multiprocessing.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 295.0229
- 134.9936
- 367.2629
- 146.9936 ]
+ /Rect [ 182.479
+ 729.5936
+ 203.7662
+ 741.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page15': class PDFPage
-101 0 obj
+% 'Page17': class PDFPage
+119 0 obj
% Page dictionary
-<< /Annots [ 100 0 R ]
- /Contents 147 0 R
+<< /Annots [ 118 0 R ]
+ /Contents 178 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1709,8 +2021,23 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER81': class PDFDictionary
-102 0 obj
+% 'Annot.NUMBER97': class PDFDictionary
+120 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.NUMBER98': class PDFDictionary
+121 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1718,14 +2045,14 @@ endobj
0
0 ]
/Rect [ 179.1295
- 729.5936
+ 580.3936
201.6839
- 741.5936 ]
+ 592.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER82': class PDFDictionary
-103 0 obj
+% 'Annot.NUMBER99': class PDFDictionary
+122 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1733,23 +2060,24 @@ endobj
0
0 ]
/Rect [ 414.8874
- 251.1936
+ 101.9936
436.9487
- 263.1936 ]
+ 113.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page16': class PDFPage
-104 0 obj
+% 'Page18': class PDFPage
+123 0 obj
% Page dictionary
-<< /Annots [ 102 0 R
- 103 0 R ]
- /Contents 148 0 R
+<< /Annots [ 120 0 R
+ 121 0 R
+ 122 0 R ]
+ /Contents 179 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1760,8 +2088,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER83': class PDFDictionary
-105 0 obj
+% 'Annot.NUMBER100': class PDFDictionary
+124 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1769,22 +2097,22 @@ endobj
0
0 ]
/Rect [ 122.7054
- 756.5936
+ 621.5936
145.2085
- 768.5936 ]
+ 633.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page17': class PDFPage
-106 0 obj
+% 'Page19': class PDFPage
+125 0 obj
% Page dictionary
-<< /Annots [ 105 0 R ]
- /Contents 149 0 R
+<< /Annots [ 124 0 R ]
+ /Contents 180 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1795,8 +2123,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER84': class PDFDictionary
-107 0 obj
+% 'Annot.NUMBER101': class PDFDictionary
+126 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1804,14 +2132,14 @@ endobj
0
0 ]
/Rect [ 183.3657
- 372.3936
+ 189.9936
207.8364
- 384.3936 ]
+ 201.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER85': class PDFDictionary
-108 0 obj
+% 'Annot.NUMBER102': class PDFDictionary
+127 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/multiprocessing.html) >>
@@ -1819,23 +2147,23 @@ endobj
0
0 ]
/Rect [ 254.9929
- 348.3936
+ 165.9936
327.2329
- 360.3936 ]
+ 177.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page18': class PDFPage
-109 0 obj
+% 'Page20': class PDFPage
+128 0 obj
% Page dictionary
-<< /Annots [ 107 0 R
- 108 0 R ]
- /Contents 150 0 R
+<< /Annots [ 126 0 R
+ 127 0 R ]
+ /Contents 181 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1846,8 +2174,132 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER86': class PDFDictionary
-110 0 obj
+% 'Annot.NUMBER103': class PDFDictionary
+129 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 412.3936
+ 85.70846
+ 424.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page21': class PDFPage
+130 0 obj
+% Page dictionary
+<< /Annots [ 129 0 R ]
+ /Contents 182 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER104': class PDFDictionary
+131 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 536.3936
+ 84.98766
+ 548.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page22': class PDFPage
+132 0 obj
+% Page dictionary
+<< /Annots [ 131 0 R ]
+ /Contents 183 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Page23': class PDFPage
+133 0 obj
+% Page dictionary
+<< /Contents 184 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER105': class PDFDictionary
+134 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 172.4311
+ 418.3936
+ 194.7087
+ 430.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page24': class PDFPage
+135 0 obj
+% Page dictionary
+<< /Annots [ 134 0 R ]
+ /Contents 185 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 161 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER106': class PDFDictionary
+136 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1855,14 +2307,14 @@ endobj
0
0 ]
/Rect [ 91.57623
- 585.5936
+ 680.3936
114.8995
- 597.5936 ]
+ 692.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER87': class PDFDictionary
-111 0 obj
+% 'Annot.NUMBER107': class PDFDictionary
+137 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1870,23 +2322,23 @@ endobj
0
0 ]
/Rect [ 106.6216
- 318.5936
+ 383.3936
128.3202
- 330.5936 ]
+ 395.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page19': class PDFPage
-112 0 obj
+% 'Page25': class PDFPage
+138 0 obj
% Page dictionary
-<< /Annots [ 110 0 R
- 111 0 R ]
- /Contents 151 0 R
+<< /Annots [ 136 0 R
+ 137 0 R ]
+ /Contents 186 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1897,15 +2349,15 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page20': class PDFPage
-113 0 obj
+% 'Page26': class PDFPage
+139 0 obj
% Page dictionary
-<< /Contents 152 0 R
+<< /Contents 187 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 132 0 R
+ /Parent 161 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1916,239 +2368,281 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R114': class PDFCatalog
-114 0 obj
+% 'R140': class PDFCatalog
+140 0 obj
% Document Root
-<< /Outlines 116 0 R
- /PageLabels 153 0 R
+<< /Outlines 142 0 R
+ /PageLabels 188 0 R
/PageMode /UseNone
- /Pages 132 0 R
+ /Pages 161 0 R
/Type /Catalog >>
endobj
-% 'R115': class PDFInfo
-115 0 obj
+% 'R141': class PDFInfo
+141 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100710083035-01'00')
+ /CreationDate (D:20100802070955-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Advanced usages of plac) >>
endobj
-% 'R116': class PDFOutlines
-116 0 obj
-<< /Count 15
- /First 117 0 R
- /Last 131 0 R
+% 'R142': class PDFOutlines
+142 0 obj
+<< /Count 18
+ /First 143 0 R
+ /Last 160 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-117 0 obj
-<< /Dest [ 45 0 R
+143 0 obj
+<< /Dest [ 48 0 R
/XYZ
62.69291
- 236.0236
+ 182.0236
0 ]
- /Next 118 0 R
- /Parent 116 0 R
+ /Next 144 0 R
+ /Parent 142 0 R
/Title (Introduction) >>
endobj
% 'Outline.1': class OutlineEntryObject
-118 0 obj
-<< /Dest [ 49 0 R
+144 0 obj
+<< /Dest [ 58 0 R
/XYZ
62.69291
- 717.0236
+ 633.0236
0 ]
- /Next 119 0 R
- /Parent 116 0 R
- /Prev 117 0 R
+ /Next 145 0 R
+ /Parent 142 0 R
+ /Prev 143 0 R
/Title (From scripts to interactive applications) >>
endobj
% 'Outline.2': class OutlineEntryObject
-119 0 obj
-<< /Dest [ 51 0 R
+145 0 obj
+<< /Dest [ 60 0 R
/XYZ
62.69291
- 259.4236
+ 175.4236
0 ]
- /Next 120 0 R
- /Parent 116 0 R
- /Prev 118 0 R
+ /Next 146 0 R
+ /Parent 142 0 R
+ /Prev 144 0 R
/Title (Testing a plac application) >>
endobj
% 'Outline.3': class OutlineEntryObject
-120 0 obj
-<< /Dest [ 52 0 R
+146 0 obj
+<< /Dest [ 72 0 R
/XYZ
62.69291
- 222.6236
+ 765.0236
0 ]
- /Next 121 0 R
- /Parent 116 0 R
- /Prev 119 0 R
+ /Next 147 0 R
+ /Parent 142 0 R
+ /Prev 145 0 R
/Title (Plac easy tests) >>
endobj
% 'Outline.4': class OutlineEntryObject
-121 0 obj
-<< /Dest [ 66 0 R
+147 0 obj
+<< /Dest [ 75 0 R
/XYZ
62.69291
- 588.7757
+ 461.8485
0 ]
- /Next 122 0 R
- /Parent 116 0 R
- /Prev 120 0 R
+ /Next 148 0 R
+ /Parent 142 0 R
+ /Prev 146 0 R
/Title (Plac batch scripts) >>
endobj
% 'Outline.5': class OutlineEntryObject
-122 0 obj
-<< /Dest [ 71 0 R
+148 0 obj
+<< /Dest [ 80 0 R
/XYZ
62.69291
- 671.8236
+ 539.8236
0 ]
- /Next 123 0 R
- /Parent 116 0 R
- /Prev 121 0 R
+ /Next 149 0 R
+ /Parent 142 0 R
+ /Prev 147 0 R
/Title (Implementing subcommands) >>
endobj
% 'Outline.6': class OutlineEntryObject
-123 0 obj
-<< /Dest [ 77 0 R
+149 0 obj
+<< /Dest [ 86 0 R
/XYZ
62.69291
- 562.2269
+ 469.3633
0 ]
- /Next 124 0 R
- /Parent 116 0 R
- /Prev 122 0 R
+ /Next 150 0 R
+ /Parent 142 0 R
+ /Prev 148 0 R
/Title (Readline support) >>
endobj
% 'Outline.7': class OutlineEntryObject
-124 0 obj
-<< /Dest [ 93 0 R
+150 0 obj
+<< /Dest [ 102 0 R
/XYZ
62.69291
- 715.8236
+ 566.6236
0 ]
- /Next 125 0 R
- /Parent 116 0 R
- /Prev 123 0 R
+ /Next 151 0 R
+ /Parent 142 0 R
+ /Prev 149 0 R
/Title (The plac runner) >>
endobj
% 'Outline.8': class OutlineEntryObject
-125 0 obj
-<< /Dest [ 96 0 R
+151 0 obj
+<< /Dest [ 116 0 R
/XYZ
62.69291
- 242.2236
+ 765.0236
0 ]
- /Next 126 0 R
- /Parent 116 0 R
- /Prev 124 0 R
+ /Next 152 0 R
+ /Parent 142 0 R
+ /Prev 150 0 R
/Title (A non class-based example) >>
endobj
% 'Outline.9': class OutlineEntryObject
-126 0 obj
-<< /Dest [ 99 0 R
+152 0 obj
+<< /Dest [ 119 0 R
/XYZ
62.69291
- 253.4236
+ 765.0236
0 ]
- /Next 127 0 R
- /Parent 116 0 R
- /Prev 125 0 R
+ /Next 153 0 R
+ /Parent 142 0 R
+ /Prev 151 0 R
/Title (Writing your own plac runner) >>
endobj
% 'Outline.10': class OutlineEntryObject
-127 0 obj
-<< /Dest [ 104 0 R
+153 0 obj
+<< /Dest [ 123 0 R
/XYZ
62.69291
- 765.0236
+ 615.8236
0 ]
- /Next 128 0 R
- /Parent 116 0 R
- /Prev 126 0 R
+ /Next 154 0 R
+ /Parent 142 0 R
+ /Prev 152 0 R
/Title (Long running commands) >>
endobj
% 'Outline.11': class OutlineEntryObject
-128 0 obj
-<< /Dest [ 104 0 R
+154 0 obj
+<< /Dest [ 125 0 R
/XYZ
62.69291
- 223.6236
+ 741.0236
0 ]
- /Next 129 0 R
- /Parent 116 0 R
- /Prev 127 0 R
+ /Next 155 0 R
+ /Parent 142 0 R
+ /Prev 153 0 R
/Title (Threaded commands) >>
endobj
% 'Outline.12': class OutlineEntryObject
-129 0 obj
-<< /Dest [ 109 0 R
+155 0 obj
+<< /Dest [ 128 0 R
/XYZ
62.69291
- 419.8236
+ 237.4236
0 ]
- /Next 130 0 R
- /Parent 116 0 R
- /Prev 128 0 R
+ /Next 156 0 R
+ /Parent 142 0 R
+ /Prev 154 0 R
/Title (Running commands as external processes) >>
endobj
% 'Outline.13': class OutlineEntryObject
-130 0 obj
-<< /Dest [ 112 0 R
+156 0 obj
+<< /Dest [ 130 0 R
/XYZ
62.69291
- 621.0236
+ 447.8236
0 ]
- /Next 131 0 R
- /Parent 116 0 R
- /Prev 129 0 R
- /Title (Summary) >>
+ /Next 157 0 R
+ /Parent 142 0 R
+ /Prev 155 0 R
+ /Title (Managing the output of concurrent commands) >>
endobj
% 'Outline.14': class OutlineEntryObject
-131 0 obj
-<< /Dest [ 112 0 R
+157 0 obj
+<< /Dest [ 132 0 R
+ /XYZ
+ 62.69291
+ 571.8236
+ 0 ]
+ /Next 158 0 R
+ /Parent 142 0 R
+ /Prev 156 0 R
+ /Title (Parallel computing with plac) >>
+endobj
+% 'Outline.15': class OutlineEntryObject
+158 0 obj
+<< /Dest [ 135 0 R
+ /XYZ
+ 62.69291
+ 537.8236
+ 0 ]
+ /Next 159 0 R
+ /Parent 142 0 R
+ /Prev 157 0 R
+ /Title (The plac server) >>
+endobj
+% 'Outline.16': class OutlineEntryObject
+159 0 obj
+<< /Dest [ 138 0 R
/XYZ
62.69291
- 354.0236
+ 715.8236
+ 0 ]
+ /Next 160 0 R
+ /Parent 142 0 R
+ /Prev 158 0 R
+ /Title (Summary) >>
+endobj
+% 'Outline.17': class OutlineEntryObject
+160 0 obj
+<< /Dest [ 138 0 R
+ /XYZ
+ 62.69291
+ 418.8236
0 ]
- /Parent 116 0 R
- /Prev 130 0 R
+ /Parent 142 0 R
+ /Prev 159 0 R
/Title (Appendix: custom annotation objects) >>
endobj
-% 'R132': class PDFPages
-132 0 obj
+% 'R161': class PDFPages
+161 0 obj
% page tree
-<< /Count 20
- /Kids [ 45 0 R
- 49 0 R
- 51 0 R
- 52 0 R
- 63 0 R
- 66 0 R
- 71 0 R
+<< /Count 26
+ /Kids [ 48 0 R
+ 58 0 R
+ 60 0 R
+ 61 0 R
+ 72 0 R
75 0 R
- 77 0 R
- 90 0 R
+ 80 0 R
+ 84 0 R
+ 86 0 R
93 0 R
- 96 0 R
- 97 0 R
- 99 0 R
- 101 0 R
+ 102 0 R
104 0 R
- 106 0 R
- 109 0 R
- 112 0 R
- 113 0 R ]
+ 113 0 R
+ 114 0 R
+ 116 0 R
+ 117 0 R
+ 119 0 R
+ 123 0 R
+ 125 0 R
+ 128 0 R
+ 130 0 R
+ 132 0 R
+ 133 0 R
+ 135 0 R
+ 138 0 R
+ 139 0 R ]
/Type /Pages >>
endobj
-% 'R133': class PDFStream
-133 0 obj
+% 'R162': class PDFStream
+162 0 obj
% page stream
-<< /Length 9046 >>
+<< /Length 9279 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -2215,7 +2709,7 @@ q
1 0 0 1 91.03937 3 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (July 2010) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (August 2010) Tj T* ET
Q
Q
q
@@ -2342,17 +2836,17 @@ 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 248.0236 cm
+1 0 0 1 62.69291 194.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 0 255 cm
+1 0 0 1 0 309 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 255 cm
+1 0 0 1 397.8898 309 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2360,13 +2854,13 @@ BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (1) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 0 237 cm
+1 0 0 1 0 291 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (From scripts to interactive applications) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 237 cm
+1 0 0 1 397.8898 291 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2374,13 +2868,13 @@ BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (2) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 0 219 cm
+1 0 0 1 0 273 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 219 cm
+1 0 0 1 397.8898 273 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2388,23 +2882,65 @@ BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (3) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 0 201 cm
+1 0 0 1 0 255 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac easy tests) Tj T* ET
Q
Q
q
+1 0 0 1 397.8898 255 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (5) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 237 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac batch scripts) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 237 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (6) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 219 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Implementing subcommands) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 219 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 201 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Readline support) Tj T* ET
+Q
+Q
+q
1 0 0 1 397.8898 201 cm
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (4) Tj T* -66.44 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
Q
Q
q
1 0 0 1 0 183 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac batch scripts) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (The plac runner) Tj T* ET
Q
Q
q
@@ -2412,13 +2948,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (6) Tj T* -66.44 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 0 Td ET
Q
Q
q
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 (Implementing subcommands) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A non class-based example) Tj T* ET
Q
Q
q
@@ -2426,13 +2962,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (15) Tj T* -60.88 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 (Readline support) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Writing your own plac runner) Tj T* ET
Q
Q
q
@@ -2440,13 +2976,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (17) Tj T* -60.88 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 (The plac runner) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Long running commands) Tj T* ET
Q
Q
q
@@ -2454,13 +2990,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (18) Tj T* -60.88 0 Td ET
Q
Q
q
1 0 0 1 0 111 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A non class-based example) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Threaded commands) Tj T* ET
Q
Q
q
@@ -2468,13 +3004,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (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 (19) Tj T* -60.88 0 Td ET
Q
Q
q
1 0 0 1 0 93 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Writing your own plac runner) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Running commands as external processes) Tj T* ET
Q
Q
q
@@ -2482,13 +3018,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (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 (20) Tj T* -60.88 0 Td ET
Q
Q
q
1 0 0 1 0 75 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Long running commands) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Managing the output of concurrent commands) Tj T* ET
Q
Q
q
@@ -2496,13 +3032,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (16) 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 (21) Tj T* -60.88 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 (Threaded commands) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Parallel computing with plac) Tj T* ET
Q
Q
q
@@ -2510,13 +3046,13 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (16) 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 (22) 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 (Running commands as external processes) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (The plac server) Tj T* ET
Q
Q
q
@@ -2524,7 +3060,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 (18) 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 (24) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2538,7 +3074,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 (19) 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 (25) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2552,34 +3088,28 @@ 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 (19) 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 (25) Tj T* -60.88 0 Td ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 215.0236 cm
+1 0 0 1 62.69291 161.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 173.0236 cm
+1 0 0 1 62.69291 119.0236 cm
q
BT 1 0 0 1 0 28.82 Tm .539036 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the design goals of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is to make it dead easy to write a scriptable and testable interface for an) Tj T* 0 Tw .813876 Tw (application. You can use ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (whenever you have an API with strings in input and strings in output, and) Tj T* 0 Tw (that includes a ) Tj /F5 10 Tf (huge ) Tj /F1 10 Tf (domain of applications.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 131.0236 cm
-q
-BT 1 0 0 1 0 28.82 Tm 1.756651 Tw 12 TL /F1 10 Tf 0 0 0 rg (A string-oriented interface is a scriptable interface by construction. That means that you can define a) Tj T* 0 Tw .918735 Tw (command language for your application and that it is possible to write scripts which are interpretable by) Tj T* 0 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and can be run as batch scripts.) Tj T* ET
-Q
-Q
-q
1 0 0 1 62.69291 89.02362 cm
q
-BT 1 0 0 1 0 28.82 Tm .444987 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually, at the most general level, you can see ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (as a generic tool to write domain specific languages) Tj T* 0 Tw .107209 Tw (\(DSL\). With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (you can test your application interactively as well as with batch scripts, and even with the) Tj T* 0 Tw (analogous of Python doctests for your defined language.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.756651 Tw 12 TL /F1 10 Tf 0 0 0 rg (A string-oriented interface is a scriptable interface by construction. That means that you can define a ) Tj T* 0 Tw .918735 Tw (command language for your application and that it is possible to write scripts which are interpretable by) Tj T* 0 Tw ET
Q
Q
q
@@ -2593,44 +3123,56 @@ Q
endstream
endobj
-% 'R134': class PDFStream
-134 0 obj
+% 'R163': class PDFStream
+163 0 obj
% page stream
-<< /Length 4496 >>
+<< /Length 5305 >>
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 753.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm .694104 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can easily replace the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module of the standard library and you could easily write an application) Tj T* 0 Tw 2.731654 Tw (like ) Tj 0 0 .501961 rg (twill ) Tj 0 0 0 rg (with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Or you could use it to script your building procedure. Or any other thing, your) Tj T* 0 Tw (imagination is the only limit!) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and can be run as batch scripts.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 696.0236 cm
+1 0 0 1 62.69291 711.0236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (From scripts to interactive applications) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .444987 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually, at the most general level, you can see ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (as a generic tool to write domain specific languages) Tj T* 0 Tw .107209 Tw (\(DSL\). With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (you can test your application interactively as well as with batch scripts, and even with the) Tj T* 0 Tw (analogous of Python doctests for your defined language.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 642.0236 cm
+1 0 0 1 62.69291 645.0236 cm
q
-BT 1 0 0 1 0 40.82 Tm 3.215814 Tw 12 TL /F1 10 Tf 0 0 0 rg (Command-line scripts have many advantages, but are no substitute for interactive applications. In) Tj T* 0 Tw .088171 Tw (particular, if you have a script with a large startup time which must be run multiple times, it is best to turn it) Tj T* 0 Tw 4.582126 Tw (into an interactive application, so that the startup is performed only once. ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (provides an) Tj T* 0 Tw /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class just for this purpose.) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .694104 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can easily replace the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module of the standard library and you could easily write an application) Tj T* 0 Tw 2.271751 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. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (also supports parallel) Tj T* 0 Tw .907765 Tw (execution of multiple commands and can be used as task manager and monitor. It is also quite easy to) Tj T* 0 Tw 1.483488 Tw (build a GUI or a Web application on top of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. When speaking of things you can do with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (, your) Tj T* 0 Tw (imagination is the only limit!) Tj T* ET
Q
Q
q
1 0 0 1 62.69291 612.0236 cm
q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (From scripts to interactive applications) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 558.0236 cm
+q
+BT 1 0 0 1 0 40.82 Tm 1.300751 Tw 12 TL /F1 10 Tf 0 0 0 rg (Command-line scripts have many advantages, but they are no substitute for interactive applications. In) Tj T* 0 Tw .088171 Tw (particular, if you have a script with a large startup time which must be run multiple times, it is best to turn it) Tj T* 0 Tw 4.582126 Tw (into an interactive application, so that the startup is performed only once. ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (provides an) Tj T* 0 Tw /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class just for this purpose.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 528.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 /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class wraps the main function of a script and provides an ) Tj /F4 10 Tf (.interact ) Tj /F1 10 Tf (method to) Tj T* 0 Tw (start an interactive interpreter reading commands from the console.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 582.0236 cm
+1 0 0 1 62.69291 498.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.49436 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can define an interactive interpreter on top of the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (script introduced in the) Tj T* 0 Tw 0 0 .501961 rg (basic documentation ) Tj 0 0 0 rg (as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 332.8236 cm
+1 0 0 1 62.69291 248.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2651,13 +3193,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 288.8236 cm
+1 0 0 1 62.69291 204.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 2.200651 Tw 12 TL /F1 10 Tf 0 0 0 rg (A trick has been used here: the ishelve command-line interface has been hidden inside an external) Tj T* 0 Tw .917674 Tw (interface. They are distinct: for instance the external interface recognizes the ) Tj /F4 10 Tf (-h/--help ) Tj /F1 10 Tf (flag whereas) Tj T* 0 Tw (the internal interface only recognizes the ) Tj /F4 10 Tf (.help ) Tj /F1 10 Tf (command:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 255.6236 cm
+1 0 0 1 62.69291 171.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2688,11 +3230,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+n -6 -6 468.6898 72 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (usage: shelve_interpreter.py [-h] [-interactive]) Tj T* ( [subcommands [subcommands ...]]) Tj T* T* ( This script works both interactively and non-interactively.) Tj T* ( Use .help to see the internal commands.) Tj T* ( ) 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* ET
+BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL (usage: shelve_interpreter.py [-h] [-interactive]) Tj T* ( [subcommands [subcommands ...]]) Tj T* T* ( This script works both interactively and non-interactively.) Tj T* ( Use .help to see the internal commands.) Tj T* ET
Q
Q
Q
@@ -2709,14 +3251,14 @@ Q
endstream
endobj
-% 'R135': class PDFStream
-135 0 obj
+% 'R164': class PDFStream
+164 0 obj
% page stream
-<< /Length 4451 >>
+<< /Length 4183 >>
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 655.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2726,25 +3268,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
+n -6 -6 468.6898 108 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ( -interactive start interactive interface) Tj T* ET
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL ( ) 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 719.8236 cm
+1 0 0 1 62.69291 635.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (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 674.6236 cm
+1 0 0 1 62.69291 590.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2765,14 +3307,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 654.6236 cm
+1 0 0 1 62.69291 570.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is an usage session:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 369.4236 cm
+1 0 0 1 62.69291 285.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -2792,58 +3334,37 @@ Q
Q
Q
q
-1 0 0 1 62.69291 301.4236 cm
+1 0 0 1 62.69291 217.4236 cm
q
BT 1 0 0 1 0 52.82 Tm .256412 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (.interact ) Tj /F1 10 Tf (method reads commands from the console and send them to the underlying interpreter,) Tj T* 0 Tw .065984 Tw (until the user send a CTRL-D command \(CTRL-Z in Windows\). There is a default argument ) Tj /F4 10 Tf (prompt='i) Tj (>) Tj T* 0 Tw .41832 Tw (' ) Tj /F1 10 Tf (which can be used to change the prompt. The text displayed at the beginning of the interactive session) Tj T* 0 Tw 1.407126 Tw (is the docstring of the main function. ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (also understands command abbreviations: in this example) Tj T* 0 Tw /F4 10 Tf (del ) Tj /F1 10 Tf (is an abbreviation for ) Tj /F4 10 Tf (delete) Tj /F1 10 Tf (. In case of ambiguous abbreviations ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (raises a ) Tj /F4 10 Tf (NameError) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 271.4236 cm
+1 0 0 1 62.69291 187.4236 cm
q
BT 1 0 0 1 0 16.82 Tm .847045 Tw 12 TL /F1 10 Tf 0 0 0 rg (Finally I must notice that the ) Tj /F4 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is available only if you are using a recent version of) Tj T* 0 Tw (Python \() Tj (>) Tj (= 2.5\), because it is a context manager object which uses extended generators internally.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 238.4236 cm
+1 0 0 1 62.69291 154.4236 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 208.4236 cm
+1 0 0 1 62.69291 124.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 3.034269 Tw (You can conveniently test your application in interactive mode. However manual testing is a poor) Tj T* 0 Tw (substitute for automatic testing.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 190.4236 cm
+1 0 0 1 62.69291 106.4236 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 /F4 10 Tf (ishelve ) Tj /F1 10 Tf (application by using ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (directly:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 97.22362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# test_ishelve.py) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( assert plac.call\(ishelve.main, ['.clear']\) == ['cleared the shelve']) Tj T* ( assert plac.call\(ishelve.main, ['a=1']\) == ['setting a=1']) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -2854,14 +3375,14 @@ Q
endstream
endobj
-% 'R136': class PDFStream
-136 0 obj
+% 'R165': class PDFStream
+165 0 obj
% page stream
-<< /Length 6154 >>
+<< /Length 5294 >>
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 607.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2871,30 +3392,30 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 156 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ( assert plac.call\(ishelve.main, ['a']\) == ['1']) Tj T* ( assert plac.call\(ishelve.main, ['.delete=a']\) == ['deleted a']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['a: not found']) Tj T* T* (if __name__ == '__main__':) Tj T* ( test\(\)) Tj T* ET
+BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (# test_ishelve.py) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( assert plac.call\(ishelve.main, ['.clear']\) == ['cleared the shelve']) Tj T* ( assert plac.call\(ishelve.main, ['a=1']\) == ['setting a=1']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['1']) Tj T* ( assert plac.call\(ishelve.main, ['.delete=a']\) == ['deleted a']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['a: not found']) Tj T* T* (if __name__ == '__main__':) Tj T* ( test\(\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 635.8236 cm
+1 0 0 1 62.69291 563.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 /F4 10 Tf (plac.call ) Tj /F1 10 Tf (is not especially nice. The big issue is that ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (responds to invalid) Tj T* 0 Tw 1.249987 Tw (input by printing an error message on stderr and by raising a ) Tj /F4 10 Tf (SystemExit) Tj /F1 10 Tf (: this is certainly not a nice) Tj T* 0 Tw (thing to do in a test.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 593.8236 cm
+1 0 0 1 62.69291 521.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 /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (exception by hand each time \(a possibly you do something with the error message in stderr) Tj T* 0 Tw (too\). Luckily, ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (offers a better testing support through the ) Tj /F4 10 Tf (check ) Tj /F1 10 Tf (method of ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (objects:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 440.6236 cm
+1 0 0 1 62.69291 368.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2915,28 +3436,28 @@ Q
Q
Q
q
-1 0 0 1 62.69291 384.6236 cm
+1 0 0 1 62.69291 312.6236 cm
q
BT 1 0 0 1 0 40.82 Tm 6.299974 Tw 12 TL /F1 10 Tf 0 0 0 rg (The method ) Tj /F4 10 Tf (.check\(given_input, expected_output\) ) Tj /F1 10 Tf (works on strings and raises an) Tj T* 0 Tw .971318 Tw /F4 10 Tf (AssertionError ) Tj /F1 10 Tf (if the output produced by the interpreter is different from the expected output for the) Tj T* 0 Tw 2.186905 Tw (given input. Notice that ) Tj /F4 10 Tf (AssertionError ) Tj /F1 10 Tf (is catched by tools like ) Tj /F4 10 Tf (py.test ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (nosetests ) Tj /F1 10 Tf (and) Tj T* 0 Tw (actually ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (tests are intended to be run with such tools.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 330.6236 cm
+1 0 0 1 62.69291 258.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 /F4 10 Tf (plac.call ) Tj /F1 10 Tf (directly, but they offer a) Tj T* 0 Tw .96748 Tw /F5 10 Tf (major ) Tj /F1 10 Tf (semantic advantage when things go wrong \(read exceptions\): an ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (object internally) Tj T* 0 Tw 1.181318 Tw (invokes something like ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (, but it wraps all exceptions, so that ) Tj /F4 10 Tf (i.check ) Tj /F1 10 Tf (is guaranteed not to) Tj T* 0 Tw (raise any exception except ) Tj /F4 10 Tf (AssertionError) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 312.6236 cm
+1 0 0 1 62.69291 240.6236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Even the ) Tj /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (exception is captured and you can write your test as) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 306.6236 cm
+1 0 0 1 62.69291 234.6236 cm
Q
q
-1 0 0 1 62.69291 294.6236 cm
+1 0 0 1 62.69291 222.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2951,58 +3472,58 @@ q
Q
Q
q
-1 0 0 1 62.69291 294.6236 cm
+1 0 0 1 62.69291 222.6236 cm
Q
q
-1 0 0 1 62.69291 276.6236 cm
+1 0 0 1 62.69291 204.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 234.6236 cm
+1 0 0 1 62.69291 162.6236 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 /F4 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (functions\) they will be run only once at the beginning and) Tj T* 0 Tw (at the end of the interpreter loop. ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (instead ignores the initialization/finalization code.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 201.6236 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac easy tests) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (4) Tj T* -238.1649 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R166': class PDFStream
+166 0 obj
+% page stream
+<< /Length 6149 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 159.6236 cm
+1 0 0 1 62.69291 744.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 /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (is certainly an improvement over writing them in) Tj T* 0 Tw 1.807318 Tw (terms of ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (, but they are still too low-level for my taste. The ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class provides) Tj T* 0 Tw (support for doctest-style tests, a.k.a. ) Tj /F5 10 Tf (plac easy tests) Tj /F1 10 Tf (.) Tj T* ET
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac easy tests) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 117.6236 cm
+1 0 0 1 62.69291 702.0236 cm
q
-BT 1 0 0 1 0 28.82 Tm 2.142209 Tw 12 TL /F1 10 Tf 0 0 0 rg (By using plac easy tests you can cut and paste your interactive session and turn it into a runnable) Tj T* 0 Tw .519213 Tw (automatics test. Consider for instance the following file ) Tj /F4 10 Tf (ishelve.placet ) Tj /F1 10 Tf (\(the ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (extension is a) Tj T* 0 Tw (mnemonic for plac easy tests\):) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 1.517126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Writing your tests in terms of ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (is certainly an improvement over writing them in) Tj T* 0 Tw 1.807318 Tw (terms of ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (, but they are still too low-level for my taste. The ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class provides) Tj T* 0 Tw (support for doctest-style tests, a.k.a. ) Tj /F5 10 Tf (plac easy tests) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 660.0236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (4) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 28.82 Tm 2.142209 Tw 12 TL /F1 10 Tf 0 0 0 rg (By using plac easy tests you can cut and paste your interactive session and turn it into a runnable) Tj T* 0 Tw .519213 Tw (automatics test. Consider for instance the following file ) Tj /F4 10 Tf (ishelve.placet ) Tj /F1 10 Tf (\(the ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (extension is a) Tj T* 0 Tw (mnemonic for plac easy tests\):) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R137': class PDFStream
-137 0 obj
-% page stream
-<< /Length 5781 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 595.8236 cm
+1 0 0 1 62.69291 482.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3022,19 +3543,19 @@ Q
Q
Q
q
-1 0 0 1 62.69291 539.8236 cm
+1 0 0 1 62.69291 426.8236 cm
q
BT 1 0 0 1 0 40.82 Tm .697132 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the precence of the shebang line containing the name of the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (tool to test \(a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (tool is just a) Tj T* 0 Tw 1.511751 Tw (Python module with a function called ) Tj /F4 10 Tf (main) Tj /F1 10 Tf (\). The shebang is ignored by the interpreter \(it looks like a) Tj T* 0 Tw .487608 Tw (comment to it\) but it is there so that external tools \(say a test runner\) can infer the plac interpreter to use) Tj T* 0 Tw (to test the file.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 509.8236 cm
+1 0 0 1 62.69291 396.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.419984 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can test ) Tj /F4 10 Tf (ishelve.placet ) Tj /F1 10 Tf (file by calling the ) Tj /F4 10 Tf (.doctest ) Tj /F1 10 Tf (method of the interpreter, as in this) Tj T* 0 Tw (example:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 465.0393 cm
+1 0 0 1 62.69291 352.0393 cm
q
q
.988825 0 0 .988825 0 0 cm
@@ -3055,63 +3576,36 @@ Q
Q
Q
q
-1 0 0 1 62.69291 421.0393 cm
+1 0 0 1 62.69291 308.0393 cm
q
BT 1 0 0 1 0 28.82 Tm 4.007109 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F4 10 Tf (Interpreter.doctests ) Tj /F1 10 Tf (invokes something like ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (multiple times) Tj T* 0 Tw .226654 Tw (inside the same context and compare the output with the expected output: if even a check fails, the whole) Tj T* 0 Tw (test fail.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 379.0393 cm
+1 0 0 1 62.69291 266.0393 cm
q
BT 1 0 0 1 0 28.82 Tm .175868 Tw 12 TL /F1 10 Tf 0 0 0 rg (You should realize tha the easy tests supported by ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (are ) Tj /F5 10 Tf (not ) Tj /F1 10 Tf (unittests: they are functional tests. They) Tj T* 0 Tw 1.22936 Tw (model then user interaction and the order of the operations generally matters. The single subtests in a) Tj T* 0 Tw /F4 10 Tf (.placet ) Tj /F1 10 Tf (file are not independent and it makes sense to exit immediately at the first failure.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 325.0393 cm
+1 0 0 1 62.69291 188.0393 cm
q
-BT 1 0 0 1 0 40.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 .875984 Tw 0 0 .501961 rg (shlex) Tj 0 0 0 rg (, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize comments \(the default comment character is ) Tj /F4 10 Tf (#) Tj /F1 10 Tf (\), escape sequences and) Tj T* 0 Tw (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 interpreted.) Tj T* ET
+BT 1 0 0 1 0 64.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 .875984 Tw 0 0 .501961 rg (shlex) Tj 0 0 0 rg (, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize comments \(the default comment character is ) Tj /F4 10 Tf (#) Tj /F1 10 Tf (\), escape sequences and) Tj T* 0 Tw 1.50686 Tw (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 interpreted. For) Tj T* 0 Tw 2.794985 Tw (more flexibility, it is even possible to pass to the interpreter a custom split function with signature) Tj T* 0 Tw /F4 10 Tf (split\(line, commentchar\)) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 271.0393 cm
+1 0 0 1 62.69291 134.0393 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 .610898 Tw (files, even if plac easy tests does not need to be in a file: you can just pass to the ) Tj /F4 10 Tf (.doctest ) Tj /F1 10 Tf (method a) Tj T* 0 Tw (list of strings corresponding to the lines of the file.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 241.0393 cm
+1 0 0 1 62.69291 104.0393 cm
q
BT 1 0 0 1 0 16.82 Tm .653145 Tw 12 TL /F1 10 Tf 0 0 0 rg (At the present ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not use any code from the doctest module, but the situation may change in the) Tj T* 0 Tw (future \(it would be nice if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (could reuse doctests directives like ELLIPSIS\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 211.0393 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.447318 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is straighforward to integrate your ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (tests with standard testing tools. For instance, you can) Tj T* 0 Tw (integrate your doctests with ) Tj /F4 10 Tf (nose ) Tj /F1 10 Tf (or ) Tj /F4 10 Tf (py.test ) Tj /F1 10 Tf (as follow:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 93.83932 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (import os, shlex, plac) Tj T* T* (def test_doct\(\):) Tj T* ( """) Tj T* ( Find all the doctests in the current directory and run them with the) Tj T* ( corresponding plac interpreter \(the shebang rules!\)) Tj T* ( """) Tj T* ( placets = [f for f in os.listdir\('.'\) if f.endswith\('.placet'\)]) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -3122,14 +3616,20 @@ Q
endstream
endobj
-% 'R138': class PDFStream
-138 0 obj
+% 'R167': class PDFStream
+167 0 obj
% page stream
-<< /Length 4789 >>
+<< /Length 5253 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 680.7757 cm
+1 0 0 1 62.69291 741.0236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.447318 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is straighforward to integrate your ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (tests with standard testing tools. For instance, you can) Tj T* 0 Tw (integrate your doctests with ) Tj /F4 10 Tf (nose ) Tj /F1 10 Tf (or ) Tj /F4 10 Tf (py.test ) Tj /F1 10 Tf (as follow:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 553.8485 cm
q
q
.988825 0 0 .988825 0 0 cm
@@ -3139,48 +3639,48 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 474 84 re B*
+n -6 -6 474 180 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ( for placet in placets:) Tj T* ( lines = list\(open\(placet\)\)) Tj T* ( assert lines[0].startswith\('#!'\), 'Missing or incorrect shebang line!') Tj T* ( firstline = lines[0][2:] # strip the shebang) Tj T* ( main = plac.import_main\(*shlex.split\(firstline\)\)) Tj T* ( yield plac.Interpreter\(main\).doctest, lines[1:]) Tj T* ET
+BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL (import os, shlex, plac) Tj T* T* (def test_doct\(\):) Tj T* ( """) Tj T* ( Find all the doctests in the current directory and run them with the) Tj T* ( corresponding plac interpreter \(the shebang rules!\)) Tj T* ( """) Tj T* ( placets = [f for f in os.listdir\('.'\) if f.endswith\('.placet'\)]) Tj T* ( for placet in placets:) Tj T* ( lines = list\(open\(placet\)\)) Tj T* ( assert lines[0].startswith\('#!'\), 'Missing or incorrect shebang line!') Tj T* ( firstline = lines[0][2:] # strip the shebang) Tj T* ( main = plac.import_main\(*shlex.split\(firstline\)\)) Tj T* ( yield plac.Interpreter\(main\).doctest, lines[1:]) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 600.7757 cm
+1 0 0 1 62.69291 473.8485 cm
q
BT 1 0 0 1 0 64.82 Tm 1.44811 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here you should notice that usage of ) Tj /F4 10 Tf (plac.import_main) Tj /F1 10 Tf (, an utility which is able to import the main) Tj T* 0 Tw .775703 Tw (function of the script specified in the shebang line. You can use both the full path name of the tool, or a) Tj T* 0 Tw .87686 Tw (relative path name. In this case the runner look at the environment variable ) Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (and it searches) Tj T* 0 Tw 1.900651 Tw (the plac tool in the directories specified there \() Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (is just a string containing directory names) Tj T* 0 Tw .56332 Tw (separated by colons\). If the variable ) Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (is not defined, it just looks in the current directory. If the) Tj T* 0 Tw (plac tool is not found, an ) Tj /F4 10 Tf (ImportError ) Tj /F1 10 Tf (is raised.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 567.7757 cm
+1 0 0 1 62.69291 440.8485 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 525.7757 cm
+1 0 0 1 62.69291 398.8485 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 /F4 10 Tf (.execute ) Tj /F1 10 Tf (method to perform just that.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 459.7757 cm
+1 0 0 1 62.69291 332.8485 cm
q
BT 1 0 0 1 0 52.82 Tm .098935 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is just a subtle point to notice: whereas in an interactive loop one wants to manage all exceptions, a) Tj T* 0 Tw 3.866412 Tw (batch script should not in the background in case of unexpected errors. The implementation of) Tj T* 0 Tw .103059 Tw /F4 10 Tf (Interpreter.execute ) Tj /F1 10 Tf (makes sure that any error raised by ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (internally is re-raised. In other) Tj T* 0 Tw .407045 Tw (words, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreters ) Tj /F5 10 Tf (wrap the errors, but does not eat them) Tj /F1 10 Tf (: the errors are always accessible and can) Tj T* 0 Tw (be re-raised on demand.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 429.7757 cm
+1 0 0 1 62.69291 302.8485 cm
q
BT 1 0 0 1 0 16.82 Tm 1.239318 Tw 12 TL /F1 10 Tf 0 0 0 rg (The exception is the case of invalid commands, which are skipped. Consider for instance the following) Tj T* 0 Tw (batch file, which contains a mispelled command \() Tj /F4 10 Tf (.dl ) Tj /F1 10 Tf (instead of ) Tj /F4 10 Tf (.del) Tj /F1 10 Tf (\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 324.5757 cm
+1 0 0 1 62.69291 197.6485 cm
q
q
1 0 0 1 0 0 cm
@@ -3201,13 +3701,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 292.5757 cm
+1 0 0 1 62.69291 165.6485 cm
q
BT 1 0 0 1 0 16.82 Tm 1.939461 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you execute the batch file, the interpreter will print a ) Tj /F4 10 Tf (.dl: not found ) Tj /F1 10 Tf (at the ) Tj /F4 10 Tf (.dl ) Tj /F1 10 Tf (line and will) Tj T* 0 Tw (continue:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 91.37571 cm
+1 0 0 1 62.69291 96.44848 cm
q
q
1 0 0 1 0 0 cm
@@ -3217,10 +3717,10 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 192 re B*
+n -6 -6 468.6898 60 re B*
Q
q
-BT 1 0 0 1 0 173.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python -c "import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).execute\(open\('ishelve.plac'\), verbose=True\)") Tj T* (i) Tj (>) Tj ( .clear) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (a=1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( .dl b) Tj T* (2) Tj T* (.dl: not found) Tj T* ET
+BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python -c "import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).execute\(open\('ishelve.plac'\), verbose=True\)") Tj T* (i) Tj (>) Tj ( .clear) Tj T* (cleared the shelve) Tj T* ET
Q
Q
Q
@@ -3237,14 +3737,14 @@ Q
endstream
endobj
-% 'R139': class PDFStream
-139 0 obj
+% 'R168': class PDFStream
+168 0 obj
% page stream
-<< /Length 4588 >>
+<< /Length 4319 >>
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 595.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3254,50 +3754,50 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 168 re B*
Q
q
-BT 1 0 0 1 0 17.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* ET
+BT 1 0 0 1 0 149.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (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* (2) Tj T* (.dl: not found) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 683.8236 cm
+1 0 0 1 62.69291 551.8236 cm
q
BT 1 0 0 1 0 28.82 Tm .159988 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (verbose ) Tj /F1 10 Tf (flag is there to show the lines which are being interpreted \(prefixed by ) Tj /F4 10 Tf (i) Tj (>) Tj /F1 10 Tf (\). This is done on) Tj T* 0 Tw 1.359988 Tw (purpose, so that you can cut and paste the output of the batch script and turn it into a ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (test) Tj T* 0 Tw (\(cool, isn't it?\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 650.8236 cm
+1 0 0 1 62.69291 518.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Implementing subcommands) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 608.8236 cm
+1 0 0 1 62.69291 476.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.182485 Tw 12 TL /F1 10 Tf 0 0 0 rg (When I discussed the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (implementation in the ) Tj 0 0 .501961 rg (basic documentation) Tj 0 0 0 rg (, I said that it looked like a) Tj T* 0 Tw .116655 Tw (poor man implementation of an object system as a chain of elifs; I also said that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was able to do much) Tj T* 0 Tw (better than that. Here I will substantiate my claim.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 566.8236 cm
+1 0 0 1 62.69291 434.8236 cm
q
BT 1 0 0 1 0 28.82 Tm .89104 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is actually able to infer a set of subparsers from a generic container of commands. This is useful if) Tj T* 0 Tw 3.125814 Tw (you want to implement ) Tj /F5 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line application featuring) Tj T* 0 Tw (subcommands is subversion\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 500.8236 cm
+1 0 0 1 62.69291 368.8236 cm
q
BT 1 0 0 1 0 52.82 Tm .015868 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a container of commands is any object with a ) Tj /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or) Tj T* 0 Tw 2.550888 Tw (methods which are valid commands. A command container may have initialization/finalization hooks) Tj T* 0 Tw 2.55664 Tw (\() Tj /F4 10 Tf (__enter__/__exit__) Tj /F1 10 Tf (\) and dispatch hooks \() Tj /F4 10 Tf (__missing__) Tj /F1 10 Tf (, invoked for invalid command names\).) Tj T* 0 Tw 2.113828 Tw (Moreover, only when using command containers ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to provide automatic autocompletion of) Tj T* 0 Tw (commands.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 482.8236 cm
+1 0 0 1 62.69291 350.8236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Rhe shelve interface can be rewritten in an object-oriented way as follows:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (The shelve interface can be rewritten in an object-oriented way as follows:) Tj T* ET
Q
Q
q
@@ -3311,11 +3811,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 384 re B*
+n -6 -6 468.6898 252 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 365.71 Tm /F4 10 Tf 12 TL (# ishelve2.py) Tj T* (import shelve, os, sys, plac) Tj T* T* (class ShelveInterface\(object\):) Tj T* ( "A minimal interface over a shelve object.") Tj T* ( commands = 'set', 'show', 'showall', 'delete') Tj T* ( @plac.annotations\() Tj T* ( configfile=\('path name of the shelve', 'option'\)\)) Tj T* ( def __init__\(self, configfile='~/conf.shelve'\):) Tj T* ( self.fname = os.path.expanduser\(configfile\)) Tj T* ( self.__doc__ += '\\nOperating on %s.\\n.help to see '\\) Tj T* ( 'the available commands.\\n' % self.fname) Tj T* ( def __enter__\(self\):) Tj T* ( self.sh = shelve.open\(self.fname\)) Tj T* ( return self) Tj T* ( def __exit__\(self, etype, exc, tb\):) Tj T* ( self.sh.close\(\)) Tj T* ( def set\(self, name, value\):) Tj T* ( "set name value") Tj T* ( yield 'setting %s=%s' % \(name, value\)) Tj T* ( self.sh[name] = value) Tj T* ( def show\(self, *names\):) Tj T* ( "show given parameters") Tj T* ( for name in names:) Tj T* ( yield '%s = %s' % \(name, self.sh[name]\) # no error checking) Tj T* ( def showall\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s' % \(name, self.sh[name]\)) Tj T* ( def delete\(self, name=None\):) Tj T* ( "delete given parameter \(or everything\)") Tj T* ET
+BT 1 0 0 1 0 233.71 Tm /F4 10 Tf 12 TL (# ishelve2.py) Tj T* (import shelve, os, sys, plac) Tj T* T* (class ShelveInterface\(object\):) Tj T* ( "A minimal interface over a shelve object.") Tj T* ( commands = 'set', 'show', 'showall', 'delete') Tj T* ( @plac.annotations\() Tj T* ( configfile=\('path name of the shelve', 'option'\)\)) Tj T* ( def __init__\(self, configfile='~/conf.shelve'\):) Tj T* ( self.fname = os.path.expanduser\(configfile\)) Tj T* ( self.__doc__ += '\\nOperating on %s.\\n.help to see '\\) Tj T* ( 'the available commands.\\n' % self.fname) Tj T* ( def __enter__\(self\):) Tj T* ( self.sh = shelve.open\(self.fname\)) Tj T* ( return self) Tj T* ( def __exit__\(self, etype, exc, tb\):) Tj T* ( self.sh.close\(\)) Tj T* ( def set\(self, name, value\):) Tj T* ( "set name value") Tj T* ( yield 'setting %s=%s' % \(name, value\)) Tj T* ET
Q
Q
Q
@@ -3332,14 +3832,14 @@ Q
endstream
endobj
-% 'R140': class PDFStream
-140 0 obj
+% 'R169': class PDFStream
+169 0 obj
% page stream
-<< /Length 4077 >>
+<< /Length 4262 >>
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 487.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3349,30 +3849,30 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+n -6 -6 468.6898 276 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL ( if name is None:) Tj T* ( yield 'deleting everything') Tj T* ( self.sh.clear\(\)) Tj T* ( else:) Tj T* ( yield 'deleting %s' % name) Tj T* ( del self.sh[name] # no error checking) Tj T* T* (main = ShelveInterface # the main 'function' can also be a class!) Tj T* T* (if __name__ == '__main__':) Tj T* ( shelve_interface = plac.call\(main\)) Tj T* ( plac.Interpreter\(shelve_interface\).interact\(\)) Tj T* ET
+BT 1 0 0 1 0 257.71 Tm /F4 10 Tf 12 TL ( self.sh[name] = value) Tj T* ( def show\(self, *names\):) Tj T* ( "show given parameters") Tj T* ( for name in names:) Tj T* ( yield '%s = %s' % \(name, self.sh[name]\) # no error checking) Tj T* ( def showall\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s' % \(name, self.sh[name]\)) Tj T* ( def delete\(self, name=None\):) Tj T* ( "delete given parameter \(or everything\)") Tj T* ( if name is None:) Tj T* ( yield 'deleting everything') Tj T* ( self.sh.clear\(\)) Tj T* ( else:) Tj T* ( yield 'deleting %s' % name) Tj T* ( del self.sh[name] # no error checking) Tj T* T* (main = ShelveInterface # useful for the tests) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.Interpreter\(plac.call\(ShelveInterface\)\).interact\(\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 515.8236 cm
+1 0 0 1 62.69291 395.8236 cm
q
BT 1 0 0 1 0 76.82 Tm .885366 Tw 12 TL /F4 10 Tf 0 0 0 rg (plac.Interpreter ) Tj /F1 10 Tf (objects wrap context manager objects consistently. In other words, if you wrap an) Tj T* 0 Tw 2.323828 Tw (object with ) Tj /F4 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (methods, they are invoked in the right order \() Tj /F4 10 Tf (__enter__) Tj T* 0 Tw .23528 Tw /F1 10 Tf (before the interpreter loop starts and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (after the interpreter loop ends, both in the regular and in) Tj T* 0 Tw 1.916412 Tw (the exceptional case\). In our example, the methods ) Tj /F4 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (make sure the the) Tj T* 0 Tw .339398 Tw (shelve is opened and closed correctly even in the case of exceptions. Notice that I have not implemented) Tj T* 0 Tw .814104 Tw (any error checking in the ) Tj /F4 10 Tf (show ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (delete ) Tj /F1 10 Tf (methods on purpose, to verify that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (works correctly in) Tj T* 0 Tw (the presence of exceptions.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 437.8236 cm
+1 0 0 1 62.69291 317.8236 cm
q
BT 1 0 0 1 0 64.82 Tm 1.567126 Tw 12 TL /F1 10 Tf 0 0 0 rg (When working with command containers, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (automatically adds two special commands to the set of) Tj T* 0 Tw 1.176136 Tw (provided commands: ) Tj /F4 10 Tf (.help ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (.last_tb) Tj /F1 10 Tf (. The ) Tj /F4 10 Tf (.help ) Tj /F1 10 Tf (command is the easier to understand: when) Tj T* 0 Tw .39811 Tw (invoked without arguments it displays the list of available commands with the same formatting of the ) Tj 0 0 .501961 rg (cmd) Tj T* 0 Tw 1.19561 Tw 0 0 0 rg (module; when invoked with the name of a command it displays the usage message for that command.) Tj T* 0 Tw 2.33686 Tw (The ) Tj /F4 10 Tf (.last_tb ) Tj /F1 10 Tf (command is useful when debugging: in case of errors, it allows you to display the) Tj T* 0 Tw (traceback of the last executed command.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 419.8236 cm
+1 0 0 1 62.69291 299.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
@@ -3389,10 +3889,10 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 312 re B*
+n -6 -6 468.6898 192 re B*
Q
q
-BT 1 0 0 1 0 293.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python ishelve2.py) Tj T* (A minimal interface over a shelve object.) Tj T* (Operating on /home/micheles/conf.shelve.) Tj T* (.help to see the available commands.) Tj T* (i) Tj (>) Tj ( .help) Tj T* T* (special commands) Tj T* (================) Tj T* (.help .last_tb) Tj T* T* (custom commands) Tj T* (===============) Tj T* (delete set show showall) Tj T* T* (i) Tj (>) Tj ( delete) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a pippo) Tj T* (setting a=pippo) Tj T* (i) Tj (>) Tj ( set b lippo) Tj T* (setting b=lippo) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (a = pippo) Tj T* (i) Tj (>) Tj ( show a b) Tj T* (a = pippo) Tj T* ET
+BT 1 0 0 1 0 173.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python ishelve2.py) Tj T* (A minimal interface over a shelve object.) Tj T* (Operating on /home/micheles/conf.shelve.) Tj T* (.help to see the available commands.) Tj T* (i) Tj (>) Tj ( .help) Tj T* T* (special commands) Tj T* (================) Tj T* (.help .last_tb) Tj T* T* (custom commands) Tj T* (===============) Tj T* (delete set show showall) Tj T* T* (i) Tj (>) Tj ( delete) Tj T* ET
Q
Q
Q
@@ -3409,14 +3909,14 @@ Q
endstream
endobj
-% 'R141': class PDFStream
-141 0 obj
+% 'R170': class PDFStream
+170 0 obj
% page stream
-<< /Length 3815 >>
+<< /Length 3748 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 606.2269 cm
+1 0 0 1 62.69291 513.3633 cm
q
q
.773863 0 0 .773863 0 0 cm
@@ -3426,35 +3926,35 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 606 204 re B*
+n -6 -6 606 324 re B*
Q
q
-BT 1 0 0 1 0 185.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (b = lippo) Tj T* (i) Tj (>) Tj ( del a) Tj T* (deleting a) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( delete a) Tj T* (deleting a) Tj T* (KeyError: 'a') Tj T* (i) Tj (>) Tj ( .last_tb) Tj T* ( File "/usr/local/lib/python2.6/dist-packages/plac-0.6.0-py2.6.egg/plac_ext.py", line 190, in _wrap) Tj T* ( for value in genobj:) Tj T* ( File "./ishelve2.py", line 37, in delete) Tj T* ( del self.sh[name] # no error checking) Tj T* ( File "/usr/lib/python2.6/shelve.py", line 136, in __delitem__) Tj T* ( del self.dict[key]) Tj T* (i) Tj (>) Tj T* ET
+BT 1 0 0 1 0 305.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (deleting everything) Tj T* (i) Tj (>) Tj ( set a pippo) Tj T* (setting a=pippo) Tj T* (i) Tj (>) Tj ( set b lippo) Tj T* (setting b=lippo) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (a = pippo) Tj T* (i) Tj (>) Tj ( show a b) Tj T* (a = pippo) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( del a) Tj T* (deleting a) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( delete a) Tj T* (deleting a) Tj T* (KeyError: 'a') Tj T* (i) Tj (>) Tj ( .last_tb) Tj T* ( File "/usr/local/lib/python2.6/dist-packages/plac-0.6.0-py2.6.egg/plac_ext.py", line 190, in _wrap) Tj T* ( for value in genobj:) Tj T* ( File "./ishelve2.py", line 37, in delete) Tj T* ( del self.sh[name] # no error checking) Tj T* ( File "/usr/lib/python2.6/shelve.py", line 136, in __delitem__) Tj T* ( del self.dict[key]) Tj T* (i) Tj (>) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 574.2269 cm
+1 0 0 1 62.69291 481.3633 cm
q
BT 1 0 0 1 0 16.82 Tm 2.571235 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that in interactive mode the traceback is hidden, unless you pass the ) Tj /F4 10 Tf (verbose ) Tj /F1 10 Tf (flag to the) Tj T* 0 Tw /F4 10 Tf (Interpreter.interact ) Tj /F1 10 Tf (method.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 541.2269 cm
+1 0 0 1 62.69291 448.3633 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Readline support) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 463.2269 cm
+1 0 0 1 62.69291 370.3633 cm
q
-BT 1 0 0 1 0 64.82 Tm 1.022485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.6 ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (offers full readline support. That means that if your Python was compiled) Tj T* 0 Tw 2.120697 Tw (with readline support you get autocompletion and persistent command history for free. By default all) Tj T* 0 Tw .144104 Tw (commands are autocomplete in a case sensitive way. If you want to add new words to the autocompletion) Tj T* 0 Tw 1.14683 Tw (set, or you want to change the location of the ) Tj /F4 10 Tf (.history ) Tj /F1 10 Tf (file, or to change the case sensitivirt, or you) Tj T* 0 Tw .162209 Tw (want to change the prompt, the way to do it is to pass a ) Tj /F4 10 Tf (plac.ReadlineInput ) Tj /F1 10 Tf (object to the interpreter.) Tj T* 0 Tw (Here is an example, assuming you want to build a database interface understanding SQL commands:) Tj T* ET
+BT 1 0 0 1 0 64.82 Tm 1.022485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.6 ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (offers full readline support. That means that if your Python was compiled) Tj T* 0 Tw 2.120697 Tw (with readline support you get autocompletion and persistent command history for free. By default all) Tj T* 0 Tw .144104 Tw (commands are autocomplete in a case sensitive way. If you want to add new words to the autocompletion) Tj T* 0 Tw .116488 Tw (set, or you want to change the location of the ) Tj /F4 10 Tf (.history ) Tj /F1 10 Tf (file, or to change the case sensitivity, the way to) Tj T* 0 Tw .18436 Tw (go is to pass a ) Tj /F4 10 Tf (plac.ReadlineInput ) Tj /F1 10 Tf (object to the interpreter. Here is an example, assuming you want) Tj T* 0 Tw (to build a database interface understanding SQL commands:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 153.1549 cm
+1 0 0 1 62.69291 106.5859 cm
q
q
.96447 0 0 .96447 0 0 cm
@@ -3464,23 +3964,16 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 486 312 re B*
+n -6 -6 486 264 re B*
Q
q
-BT 1 0 0 1 0 293.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (import os, plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (SQLKEYWORDS = set\(['select', 'from', 'inner', 'join', 'outer', 'left', 'right']) Tj T* ( \) # and many others) Tj T* (DBTABLES = set\(['table1', 'table2']\) # you can read them from the db schema) Tj T* T* (COMPLETIONS = SQLKEYWORDS | DBTABLES) Tj T* T* (class SqlInterface\(object\):) Tj T* ( commands = ['SELECT']) Tj T* ( def __init__\(self, dsn\):) Tj T* ( self.soup = SqlSoup\(dsn\)) Tj T* ( def SELECT\(self, *args\):) Tj T* ( sql = 'SELECT ' + ' '.join\(args\)) Tj T* ( for row in self.soup.bind.execute\(sql\):) Tj T* ( yield str\(row\) # the formatting can be much improved) Tj T* T* (rl_input = plac.ReadlineInput\() Tj T* ( COMPLETIONS, prompt='sql) Tj (>) Tj ( ', ) Tj T* ( histfile=os.path.expanduser\('~/.sql_interface.history'\), ) Tj T* ( case_sensitive=False\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.Interpreter\(plac.call\(SqlInterface\)\).interact\(rl_input\)) Tj T* ET
+BT 1 0 0 1 0 245.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (import os, plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (SQLKEYWORDS = set\(['select', 'from', 'inner', 'join', 'outer', 'left', 'right']) Tj T* ( \) # and many others) Tj T* (DBTABLES = set\(['table1', 'table2']\) # you can read them from the db schema) Tj T* T* (COMPLETIONS = SQLKEYWORDS | DBTABLES) Tj T* T* (class SqlInterface\(object\):) Tj T* ( commands = ['SELECT']) Tj T* ( def __init__\(self, dsn\):) Tj T* ( self.soup = SqlSoup\(dsn\)) Tj T* ( def SELECT\(self, argstring\):) Tj T* ( sql = 'SELECT ' + argstring) Tj T* ( for row in self.soup.bind.execute\(sql\):) Tj T* ( yield str\(row\) # the formatting can be much improved) Tj T* T* (rl_input = plac.ReadlineInput\() Tj T* ( COMPLETIONS, histfile=os.path.expanduser\('~/.sql_interface.history'\), ) Tj T* ( case_sensitive=False\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 133.1549 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 usage:) Tj T* ET
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -3491,14 +3984,14 @@ Q
endstream
endobj
-% 'R142': class PDFStream
-142 0 obj
+% 'R171': class PDFStream
+171 0 obj
% page stream
-<< /Length 5061 >>
+<< /Length 4503 >>
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 655.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3508,10 +4001,10 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 108 re B*
Q
q
-BT 1 0 0 1 0 29.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python sql_interface.py ) Tj (<) Tj (some dsn) Tj (>) Tj T* (sql) Tj (>) Tj ( SELECT a.* FROM TABLE1 AS a INNER JOIN TABLE2 AS b ON a.id = b.id) Tj T* (...) Tj T* ET
+BT 1 0 0 1 0 89.71 Tm 12 TL /F4 10 Tf 0 0 0 rg T* (def split_on_first_space\(line, commentchar\):) Tj T* ( return line.strip\(\).split\(' ', 1\) # ignoring comments) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( si = plac.call\(SqlInterface\)) Tj T* ( i = plac.Interpreter\(si, split=split_on_first_space\)) Tj T* ( i.interact\(rl_input, prompt='sql) Tj (>) Tj ( '\)) Tj T* ET
Q
Q
Q
@@ -3520,23 +4013,12 @@ Q
q
1 0 0 1 62.69291 635.8236 cm
q
-BT 1 0 0 1 0 64.82 Tm 1.951318 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can check that entering just ) Tj /F4 10 Tf (sel ) Tj /F1 10 Tf (and pressing TAB the readline library completes the ) Tj /F4 10 Tf (SELECT) Tj T* 0 Tw .797356 Tw /F1 10 Tf (keyword for you and makes it upper case; idem for ) Tj /F4 10 Tf (FROM) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (INNER) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (JOIN ) Tj /F1 10 Tf (and even for the names of the) Tj T* 0 Tw .256235 Tw (tables. An obvious improvement is to read the names of the tables by introspecting the database: actually) Tj T* 0 Tw 1.616654 Tw (you can even read the names of the views and of the columns, and have full autocompletion. All the) Tj T* 0 Tw 2.047251 Tw (entered commands and recorded and saved in the file ) Tj /F4 10 Tf (~/.sql_interface.history ) Tj /F1 10 Tf (when exiting) Tj T* 0 Tw (from the command-line interface.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 557.8236 cm
-q
-BT 1 0 0 1 0 64.82 Tm 2.010574 Tw 12 TL /F1 10 Tf 0 0 0 rg (If the readline library is not available, my suggestion is to use the ) Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (tool which provides similar) Tj T* 0 Tw .22561 Tw (features, at least on Unix-like platforms. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (should also work fine on Windows with the pyreadline library) Tj T* 0 Tw .068488 Tw (\(I do not use Windows, so this part is very little tested\). For people worried about licenses, I will notice that) Tj T* 0 Tw 1.863322 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses the readline library only if available, it does not include it and it does not rely on it in any) Tj T* 0 Tw 3.716654 Tw (fundamental way, so that the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (licence does not need to be the GPL \(actually it is a BSD) Tj T* 0 Tw (do-whatever-you-want-with-it licence\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 515.8236 cm
-q
-BT 1 0 0 1 0 28.82 Tm .187882 Tw 12 TL /F1 10 Tf 0 0 0 rg (The interactive mode of ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (can be used as a replacement of the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module in the standard library. It) Tj T* 0 Tw 2.730651 Tw (is actually better than ) Tj 0 0 .501961 rg (cmd) Tj 0 0 0 rg (: for instance, the ) Tj /F4 10 Tf (.help ) Tj /F1 10 Tf (command is more powerful, since it provides) Tj T* 0 Tw (information about the arguments accepted by the given command:) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is an example of usage:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 206.6236 cm
+1 0 0 1 62.69291 578.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3546,31 +4028,51 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 300 re B*
+n -6 -6 468.6898 48 re B*
Q
q
-BT 1 0 0 1 0 281.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .help set) Tj T* (usage: set name value) Tj T* T* (set name value) Tj T* T* (positional arguments:) Tj T* ( name) Tj T* ( value) Tj T* T* (i) Tj (>) Tj ( .help delete) Tj T* (usage: delete [name]) Tj T* T* (delete given parameter \(or everything\)) Tj T* T* (positional arguments:) Tj T* ( name) Tj T* T* (i) Tj (>) Tj ( .help show) Tj T* (usage: show [names [names ...]]) Tj T* T* (show given parameters) Tj T* T* (positional arguments:) Tj T* ( names) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python sql_interface.py ) Tj (<) Tj (some dsn) Tj (>) Tj T* (sql) Tj (>) Tj ( SELECT a.* FROM TABLE1 AS a INNER JOIN TABLE2 AS b ON a.id = b.id) Tj T* (...) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 150.6236 cm
+1 0 0 1 62.69291 498.6236 cm
q
-BT 1 0 0 1 0 40.82 Tm 1.959985 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you can imagine, the help message is provided by the underlying ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (subparser \(there is a) Tj T* 0 Tw 2.954524 Tw (subparser for each command\). ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (commands accept options, flags, varargs, keyword arguments,) Tj T* 0 Tw .719318 Tw (arguments with defaults, arguments with a fixed number of choices, type conversion and all the features) Tj T* 0 Tw (provided of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (which should be reimplemented from scratch using ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
+BT 1 0 0 1 0 64.82 Tm 1.951318 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can check that entering just ) Tj /F4 10 Tf (sel ) Tj /F1 10 Tf (and pressing TAB the readline library completes the ) Tj /F4 10 Tf (SELECT) Tj T* 0 Tw .797356 Tw /F1 10 Tf (keyword for you and makes it upper case; idem for ) Tj /F4 10 Tf (FROM) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (INNER) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (JOIN ) Tj /F1 10 Tf (and even for the names of the) Tj T* 0 Tw .256235 Tw (tables. An obvious improvement is to read the names of the tables by introspecting the database: actually) Tj T* 0 Tw 1.616654 Tw (you can even read the names of the views and of the columns, and have full autocompletion. All the) Tj T* 0 Tw 2.047251 Tw (entered commands and recorded and saved in the file ) Tj /F4 10 Tf (~/.sql_interface.history ) Tj /F1 10 Tf (when exiting) Tj T* 0 Tw (from the command-line interface.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 108.6236 cm
+1 0 0 1 62.69291 420.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm .901647 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover at the moment ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (also understands command abbreviations. However, this feature should) Tj T* 0 Tw .256235 Tw (be considered deprecated and may disappear in future releases. It was meaningful in the past, when ) Tj 0 0 .501961 rg (plac) Tj T* 0 Tw 0 0 0 rg (did not support readline.) Tj T* ET
+BT 1 0 0 1 0 64.82 Tm 2.010574 Tw 12 TL /F1 10 Tf 0 0 0 rg (If the readline library is not available, my suggestion is to use the ) Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (tool which provides similar) Tj T* 0 Tw 1.869984 Tw (features, at least on Unix-like platforms. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (should also work fine on Windows with the ) Tj 1 0 0 rg (pyreadline_) Tj T* 0 Tw .74408 Tw 0 0 0 rg (library \(I do not use Windows, so this part is very little tested: I tried it only once and it worked, but your) Tj T* 0 Tw .441163 Tw (mileage may vary\). For people worried about licenses, I will notice that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses the readline library only) Tj T* 0 Tw .211353 Tw (if available, it does not include it and it does not rely on it in any fundamental way, so that the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (licence) Tj T* 0 Tw (does not need to be the GPL \(actually it is a BSD do-whatever-you-want-with-it licence\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 90.62362 cm
+1 0 0 1 62.69291 378.6236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that if an abbreviation is ambiguous, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (warns you:) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .187882 Tw 12 TL /F1 10 Tf 0 0 0 rg (The interactive mode of ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (can be used as a replacement of the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module in the standard library. It) Tj T* 0 Tw 2.730651 Tw (is actually better than ) Tj 0 0 .501961 rg (cmd) Tj 0 0 0 rg (: for instance, the ) Tj /F4 10 Tf (.help ) Tj /F1 10 Tf (command is more powerful, since it provides) Tj T* 0 Tw (information about the arguments accepted by the given command:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 93.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 276 re B*
+Q
+q
+BT 1 0 0 1 0 257.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .help set) Tj T* (usage: set name value) Tj T* T* (set name value) Tj T* T* (positional arguments:) Tj T* ( name) Tj T* ( value) Tj T* T* (i) Tj (>) Tj ( .help delete) Tj T* (usage: delete [name]) Tj T* T* (delete given parameter \(or everything\)) Tj T* T* (positional arguments:) Tj T* ( name) Tj T* T* (i) Tj (>) Tj ( .help show) Tj T* (usage: show [names [names ...]]) Tj T* T* (show given parameters) Tj T* T* ET
+Q
+Q
+Q
Q
Q
q
@@ -3584,10 +4086,10 @@ Q
endstream
endobj
-% 'R143': class PDFStream
-143 0 obj
+% 'R172': class PDFStream
+172 0 obj
% page stream
-<< /Length 5053 >>
+<< /Length 6026 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -3604,6 +4106,44 @@ q
n -6 -6 468.6898 36 re B*
Q
q
+BT 1 0 0 1 0 17.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (positional arguments:) Tj T* ( names) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 671.8236 cm
+q
+BT 1 0 0 1 0 40.82 Tm 1.959985 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you can imagine, the help message is provided by the underlying ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (subparser \(there is a) Tj T* 0 Tw 2.954524 Tw (subparser for each command\). ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (commands accept options, flags, varargs, keyword arguments,) Tj T* 0 Tw .719318 Tw (arguments with defaults, arguments with a fixed number of choices, type conversion and all the features) Tj T* 0 Tw (provided of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (which should be reimplemented from scratch using ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 641.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.78248 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover at the moment ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (also understands command abbreviations. However, this feature may) Tj T* 0 Tw (disappear in future releases. It was meaningful in the past, when ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (did not support readline.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 623.8236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that if an abbreviation is ambiguous, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (warns you:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 578.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
BT 1 0 0 1 0 17.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( sh) Tj T* (NameError: Ambiguous command 'sh': matching ['showall', 'show']) Tj T* ET
Q
Q
@@ -3611,25 +4151,25 @@ Q
Q
Q
q
-1 0 0 1 62.69291 694.8236 cm
+1 0 0 1 62.69291 545.6236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The plac runner) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 628.8236 cm
+1 0 0 1 62.69291 479.6236 cm
q
BT 1 0 0 1 0 52.82 Tm 1.531318 Tw 12 TL /F1 10 Tf 0 0 0 rg (The distribution of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (includes a runner script named ) Tj /F4 10 Tf (plac_runner.py) Tj /F1 10 Tf (, which will be installed in a) Tj T* 0 Tw .44748 Tw (suitable directory in your system by ) Tj 0 0 .501961 rg (distutils ) Tj 0 0 0 rg (\(say in ) Tj /F4 10 Tf (\\usr\\local\\bin\\plac_runner.py ) Tj /F1 10 Tf (in a Unix-like) Tj T* 0 Tw .680651 Tw (operative system\). The runner provides many facilities to run ) Tj /F4 10 Tf (.plac ) Tj /F1 10 Tf (scripts and ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (files, as well) Tj T* 0 Tw 1.47311 Tw (as Python modules containg a ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (object, which can be a function, a command container object or) Tj T* 0 Tw (even a command container class.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 598.8236 cm
+1 0 0 1 62.69291 449.6236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.994269 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, suppose you want to execute a script containing commands defined in the ) Tj /F4 10 Tf (ishelve2) Tj T* 0 Tw /F1 10 Tf (module like the following one:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 529.6236 cm
+1 0 0 1 62.69291 380.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3643,20 +4183,20 @@ 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 (#!ishelve2.py -c ~/conf.shelve) Tj T* (set a 1) Tj T* (del a) Tj T* (del a # intentional error) Tj T* ET
+BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL (#!ishelve2.py:ShelveInterface -c ~/conf.shelve) Tj T* (set a 1) Tj T* (del a) Tj T* (del a # intentional error) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 485.6236 cm
+1 0 0 1 62.69291 312.4236 cm
q
-BT 1 0 0 1 0 28.82 Tm .575868 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first line of the ) Tj /F4 10 Tf (.plac ) Tj /F1 10 Tf (script contains the name of the python module containing the plac interpreter) Tj T* 0 Tw 2.327209 Tw (and the arguments which must be passed to its main function in order to be able to instantiate an) Tj T* 0 Tw (interpreter object. The other lines contains commands. Then you can run the script as follows:) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .575868 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first line of the ) Tj /F4 10 Tf (.plac ) Tj /F1 10 Tf (script contains the name of the python module containing the plac interpreter) Tj T* 0 Tw 2.327209 Tw (and the arguments which must be passed to its main function in order to be able to instantiate an) Tj T* 0 Tw .202485 Tw (interpreter object. In this case I appended ) Tj /F4 10 Tf (:ShelveInterface ) Tj /F1 10 Tf (to the name of the module to specify the) Tj T* 0 Tw 1.030574 Tw (object that must be imported: if not specified, by default the object named 'main' is imported. The other) Tj T* 0 Tw (lines contains commands. You can run the script as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 396.4505 cm
+1 0 0 1 62.69291 223.2505 cm
q
q
.952737 0 0 .952737 0 0 cm
@@ -3677,24 +4217,24 @@ Q
Q
Q
q
-1 0 0 1 62.69291 364.4505 cm
+1 0 0 1 62.69291 191.2505 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 2.79186 Tw (The last command intentionally contained an error, to show that the plac runner does not eat the) Tj T* 0 Tw (traceback.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 334.4505 cm
+1 0 0 1 62.69291 161.2505 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .437633 Tw (The runner can also be used to run Python modules in interactive mode and non-interactive mode. If you) Tj T* 0 Tw (put this alias in your bashrc) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 328.4505 cm
+1 0 0 1 62.69291 155.2505 cm
Q
q
-1 0 0 1 62.69291 316.4505 cm
+1 0 0 1 62.69291 143.2505 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3709,16 +4249,33 @@ q
Q
Q
q
-1 0 0 1 62.69291 316.4505 cm
+1 0 0 1 62.69291 143.2505 cm
Q
q
-1 0 0 1 62.69291 286.4505 cm
+1 0 0 1 62.69291 113.2505 cm
q
BT 1 0 0 1 0 16.82 Tm 2.955318 Tw 12 TL /F1 10 Tf 0 0 0 rg (\(or you define a suitable ) Tj /F4 10 Tf (plac.bat ) Tj /F1 10 Tf (script in Windows\) you can run the ) Tj /F4 10 Tf (ishelve2.py ) Tj /F1 10 Tf (script in) Tj T* 0 Tw (interactive mode as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 109.2505 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 (11) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R173': class PDFStream
+173 0 obj
+% page stream
+<< /Length 4848 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 595.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3738,30 +4295,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 89.25045 cm
+1 0 0 1 62.69291 575.8236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Now you can cut and paste the interactive session an turns into into a ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (file like the following:) Tj T* ET
Q
Q
q
-1 0 0 1 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 (11) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R144': class PDFStream
-144 0 obj
-% page stream
-<< /Length 5476 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 643.8236 cm
+1 0 0 1 62.69291 446.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3774,27 +4314,27 @@ q
n -6 -6 468.6898 120 re B*
Q
q
-BT 1 0 0 1 0 101.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (#!ishelve2.py -configfile=~/test.shelve) Tj T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show a) Tj T* (a = 1) Tj T* ET
+BT 1 0 0 1 0 101.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (#!ishelve2.py:ShelveInterface -configfile=~/test.shelve) Tj T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show a) Tj T* (a = 1) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 611.8236 cm
+1 0 0 1 62.69291 414.6236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.145697 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the first line specifies a test database ) Tj /F4 10 Tf (~/test.shelve) Tj /F1 10 Tf (, to avoid clobbering your default) Tj T* 0 Tw (shelve. If you mispell the arguments in the first line plac will give you an ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (error message \(just try\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 593.8236 cm
+1 0 0 1 62.69291 396.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can run placets following the shebang convention directly with the plac runner:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 548.6236 cm
+1 0 0 1 62.69291 351.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3815,19 +4355,19 @@ Q
Q
Q
q
-1 0 0 1 62.69291 504.6236 cm
+1 0 0 1 62.69291 307.4236 cm
q
BT 1 0 0 1 0 28.82 Tm .32104 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you want to see the output of the tests, pass the ) Tj /F4 10 Tf (-v/--verbose ) Tj /F1 10 Tf (flag. Notice that he runner ignore the) Tj T* 0 Tw .24856 Tw (extension, so you can actually use any extension your like, but ) Tj /F5 10 Tf (it relies on the first line of the file to invoke) Tj T* 0 Tw (the corresponding plac tool with the given arguments) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 474.6236 cm
+1 0 0 1 62.69291 277.4236 cm
q
BT 1 0 0 1 0 16.82 Tm .537209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The plac runner does not provide any test discovery facility, but you can use standard Unix tools to help.) Tj T* 0 Tw (For instance, you can run all the ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (files into a directory and its subdirectories as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 441.4236 cm
+1 0 0 1 62.69291 244.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3848,23 +4388,23 @@ Q
Q
Q
q
-1 0 0 1 62.69291 409.4236 cm
+1 0 0 1 62.69291 212.2236 cm
q
BT 1 0 0 1 0 16.82 Tm .760988 Tw 12 TL /F1 10 Tf 0 0 0 rg (The plac runner expects the main function of your script to return a plac tool, i.e. a function or an object) Tj T* 0 Tw (with a ) Tj /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute. It this is not the case the runner gracefully exits.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 391.4236 cm
+1 0 0 1 62.69291 194.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (It also works in non-interactive mode, if you call it as) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 385.4236 cm
+1 0 0 1 62.69291 188.2236 cm
Q
q
-1 0 0 1 62.69291 373.4236 cm
+1 0 0 1 62.69291 176.2236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3879,17 +4419,17 @@ q
Q
Q
q
-1 0 0 1 62.69291 373.4236 cm
+1 0 0 1 62.69291 176.2236 cm
Q
q
-1 0 0 1 62.69291 355.4236 cm
+1 0 0 1 62.69291 158.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is an example:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 286.2236 cm
+1 0 0 1 62.69291 89.02362 cm
q
q
1 0 0 1 0 0 cm
@@ -3910,32 +4450,63 @@ Q
Q
Q
q
-1 0 0 1 62.69291 254.2236 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 16.82 Tm .18936 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that it non-interactive mode the runner just invokes ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (object of the Python) Tj T* 0 Tw (module.) Tj T* ET
+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
+% 'R174': class PDFStream
+174 0 obj
+% page stream
+<< /Length 5041 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 221.2236 cm
+1 0 0 1 62.69291 741.0236 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
+BT 1 0 0 1 0 16.82 Tm .01561 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that in non-interactive mode the runner just invokes ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (object of the Python) Tj T* 0 Tw (module.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 179.2236 cm
+1 0 0 1 62.69291 735.0236 cm
+Q
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 /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute and possibly ) Tj /F4 10 Tf (__enter__) Tj T* 0 Tw /F1 10 Tf (and/or ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (attributes.) Tj T* ET
+1 0 0 1 62.69291 88.86614 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 618.33 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Multiline support and Emacs integration) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 149.2236 cm
+1 0 0 1 20 504.33 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .327485 Tw (In particular, a Python module is a perfect container of commands. As an example, consider the following) Tj T* 0 Tw (module implementing a fake Version Control System:) Tj T* ET
+BT 1 0 0 1 0 100.82 Tm .36436 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is optimized for the simplest use case and by default it provide support for simple command-line) Tj T* 0 Tw .598409 Tw (languages where a command take a single line. This is the simplest case: it is easy to keep track of) Tj T* 0 Tw 1.096988 Tw (the line number and to print it in the error message, if there is some error in a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (script. Starting) Tj T* 0 Tw .08561 Tw (from release 0.7 ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is beginning to support multiline input: it is now possible to define command-line) Tj T* 0 Tw .970697 Tw (languages with commands spanning multiple lines. The topical use case is the implementation of a) Tj T* 0 Tw 2.41061 Tw (tool to interact with a relational database: the tool must be able to send complex SQL queries) Tj T* 0 Tw .636905 Tw (spanning multiple lines to the backend. To support multiline input the ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class provides) Tj T* 0 Tw .678294 Tw (a method ) Tj /F4 10 Tf (multiline\(stdin=sys.stdin, terminator=';', verbose=False\) ) Tj /F1 10 Tf (which reads) Tj T* 0 Tw (input from ) Tj /F4 10 Tf (stdin ) Tj /F1 10 Tf (until the terminator character \(by default a semicolon\) is reached.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 92.02362 cm
+1 0 0 1 20 426.33 cm
+q
+BT 1 0 0 1 0 64.82 Tm 1.842126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Since the Python readline module does not expose the multiline functionality of the underlying C) Tj T* 0 Tw .244692 Tw (library \(which is there\), ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (multiline mode does not have readline functionality. This is not a big deal) Tj T* 0 Tw 1.210574 Tw (really, because if you are writing multiple line commands you don't really want to type them at the) Tj T* 0 Tw 1.299988 Tw (command-line. It is much better to use a real editor to type them, and to call ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (from the editor.) Tj T* 0 Tw .962927 Tw (Since I use Emacs I will give the recipe to integrate Emacs with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (: something equivalent can be) Tj T* 0 Tw (done for vi and for other editors/IDEs.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 20 384.33 cm
+q
+BT 1 0 0 1 0 28.82 Tm .001163 Tw 12 TL /F1 10 Tf 0 0 0 rg (The multiline mode can be enabled by invoking the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (runner with the ) Tj /F4 10 Tf (-m ) Tj /F1 10 Tf (option. Since the multiline) Tj T* 0 Tw 2.052209 Tw (mode is intended for use with Emacs in inferior mode, it does not print any prompt. Here is an) Tj T* 0 Tw (example of usage:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 20 303.13 cm
q
q
1 0 0 1 0 0 cm
@@ -3945,68 +4516,131 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 442.6898 72 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* ET
+BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL ($ plac -m ishelve2.py) Tj T* (set a 1;) Tj T* (setting a=1) Tj T* (show a;) Tj T* (a = 1) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 20 277.13 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (To integrate ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (with Emacs, enters the following lines in your .emacs:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 20 0 cm
+q
+q
+.970887 0 0 .970887 0 0 cm
+q
+1 0 0 1 6.6 6.797904 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 456 276 re B*
+Q
+q
+BT 1 0 0 1 0 257.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (;;; Emacs-plac integration: add the following to your .emacs) Tj T* T* (\(define-generic-mode 'plac-mode) Tj T* ( '\("#"\) ; comment chars) Tj T* ( '\(\); highlighted commands) Tj T* ( nil) Tj T* ( '\(".plac\\\\'"\); file extensions) Tj T* ( nil\)) Tj T* ( ) Tj T* (\(add-hook 'plac-mode-hook \(lambda \(\) \(local-set-key [f4] 'plac-start\)\)\)) Tj T* (\(add-hook 'plac-mode-hook \(lambda \(\) \(local-set-key [f5] 'plac-send\)\)\)) Tj T* (\(add-hook 'plac-mode-hook \(lambda \(\) \(local-set-key [f6] 'plac-stop\)\)\)) Tj T* T* (\(defconst terminator 59\); ASCII code for the semicolon) Tj T* (\(defvar *plac-process* nil\)) Tj T* T* (\(defun plac-start \(\)) Tj T* ( "Start an inferior plac process by inferring the script to use from the ) Tj T* ( shebang line") Tj T* ( \(interactive\)) Tj T* ( \(let \(\(shebang-line ) Tj T* ( \(save-excursion) Tj T* ET
+Q
Q
Q
Q
Q
+q
+Q
Q
q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R145': class PDFStream
-145 0 obj
+% 'R175': class PDFStream
+175 0 obj
% page stream
-<< /Length 3414 >>
+<< /Length 3087 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 427.8236 cm
+1 0 0 1 47.6378 159.1303 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
-1 0 0 1 0 0 cm
q
-1 0 0 1 6.6 6.6 cm
+.90002 0 0 .90002 0 0 cm
+q
+1 0 0 1 6.6 7.333172 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 336 re B*
+n -6 -6 492 672 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 317.71 Tm /F4 10 Tf 12 TL T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\(url='url of the source code'\)) Tj T* (def checkout\(url\):) Tj T* ( "A fake checkout command") Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\(message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( "A fake commit command") Tj T* ( return \('commit ', message\)) Tj T* T* (@plac.annotations\(quiet=\('summary information', 'flag', 'q'\)\)) Tj T* (def status\(quiet\):) Tj T* ( "A fake status command") Tj T* ( return \('status ', quiet\)) Tj T* T* (def __missing__\(name\):) Tj T* ( return 'Command %r does not exist' % name) Tj T* T* (def __exit__\(etype, exc, tb\):) Tj T* ( "Will be called automatically at the end of the call/cmdloop") Tj T* ( if etype in \(None, GeneratorExit\): # success) Tj T* ( print\('ok'\)) Tj T* T* (main = __import__\(__name__\) # the module imports itself!) Tj T* ET
+BT 1 0 0 1 0 653.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( \(goto-line 1\) \(end-of-line\)) Tj T* ( \(buffer-substring-no-properties 3 \(point\)\)\)\)\)) Tj T* ( \(if *plac-process* \(princ "plac already started"\)) Tj T* ( \(setq *plac-process*) Tj T* ( \(start-process) Tj T* ( "plac" "*plac*" "plac_runner.py" "-m" shebang-line\)\)\)\)) Tj T* ( \(display-buffer "*plac*"\)\)) Tj T* T* (;\(defun plac-send \(\)) Tj T* (; "Send the current region to the inferior plac process") Tj T* (; \(interactive\)) Tj T* (; \(save-excursion \(set-buffer "*plac*"\) \(erase-buffer\)\)) Tj T* (; \(process-send-region *plac-process* \(region-beginning\) \(region-end\)\)\)) Tj T* T* (\(defun current-paragraph-beg-end \(\)) Tj T* ( "Returns the extrema of the current paragraph, delimited by semicolons") Tj T* ( \(interactive\)) Tj T* ( \(save-excursion) Tj T* ( \(let \(\(beg \(save-excursion \(goto-line 2\) \(point\)\)\); skip the shebang) Tj T* ( \(end \(point-max\)\)\)) Tj T* ( ;; go backward) Tj T* ( \(while \() Tj (>) Tj ( \(point\) beg\)) Tj T* ( \(goto-char \(1- \(point\)\)\)) Tj T* ( \(if \(= terminator \(following-char\)\)) Tj T* ( \(setq beg \(point\)\)\)\)) Tj T* ( \(if \(= terminator \(following-char\)\)) Tj T* ( \(setq beg \(1+ beg\)\)\)) Tj T* ( ;; go forward) Tj T* ( \(while \() Tj (<) Tj ( \(point\) end\)) Tj T* ( \(goto-char \(1+ \(point\)\)\)) Tj T* ( \(if \(= 59 \(following-char\)\)) Tj T* ( \(setq end \(point\)\)\)\)) Tj T* ( \(if \(= 59 \(following-char\)\)) Tj T* ( \(setq end \(1+ end\)\)\)) Tj T* ( \(list beg end\)\)\)\)) Tj T* ( ) Tj T* (\(defun plac-send \(\)) Tj T* ( "Send the current region to the inferior plac process") Tj T* ( \(interactive\)) Tj T* ( \(save-excursion \(set-buffer "*plac*"\) \(erase-buffer\)\)) Tj T* ( \(let \(\(p \(apply 'buffer-substring-no-properties \(current-paragraph-beg-end\)\)\)\)) Tj T* ( \(message p\)) Tj T* ( \(process-send-string *plac-process* \(concat p "\\n"\)\)\)\)) Tj T* ( ;\(switch-to-buffer-other-window "*plac*"\)\)\)) Tj T* ( ;\(save-excursion \(set-buffer "*plac*"\) ) Tj T* ( ; \(set-window-start \(selected-window\) 1 nil\)\)\)\)) Tj T* T* (\(defun plac-stop \(\)) Tj T* ( "Stop the inferior plac process by sending to it an EOF") Tj T* ( \(interactive\)) Tj T* ( \(process-send-eof *plac-process*\)) Tj T* ( \(setq *plac-process* nil\)) Tj T* ( "killed *plac-process*"\)) Tj T* T* (\(provide 'plac\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 383.8236 cm
+Q
+Q
q
-BT 1 0 0 1 0 28.82 Tm .431318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that I have defined both an ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (hook and a ) Tj /F4 10 Tf (__missing__ ) Tj /F1 10 Tf (hook, invoked for non-existing) Tj T* 0 Tw .592651 Tw (commands. The real trick here is the line ) Tj /F4 10 Tf (main = __import__\(__name__\)) Tj /F1 10 Tf (, which define ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (to be) Tj T* 0 Tw (an alias for the current module.) Tj T* ET
+1 0 0 1 62.69291 159.1303 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 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R176': class PDFStream
+176 0 obj
+% page stream
+<< /Length 3684 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 353.8236 cm
+1 0 0 1 62.69291 744.0236 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 /F4 10 Tf (vcs ) Tj /F1 10 Tf (module does not contain an ) Tj /F4 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block, but you can still run it) Tj T* 0 Tw (through the plac runner \(try ) Tj /F4 10 Tf (plac vcs.py -h) Tj /F1 10 Tf (\):) Tj T* ET
+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 702.0236 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 /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute and possibly ) Tj /F4 10 Tf (__enter__) Tj T* 0 Tw /F1 10 Tf (and/or ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (attributes.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 672.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .327485 Tw (In particular, a Python module is a perfect container of commands. As an example, consider the following) Tj T* 0 Tw (module implementing a fake Version Control System:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 212.6236 cm
+1 0 0 1 62.69291 290.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4016,24 +4650,30 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
+n -6 -6 468.6898 372 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F4 10 Tf 12 TL (usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* T* (subcommands:) Tj T* ( {status,commit,checkout}) Tj T* ( -h to get additional help) Tj T* ET
+BT 1 0 0 1 0 353.71 Tm /F4 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\(url='url of the source code'\)) Tj T* (def checkout\(url\):) Tj T* ( "A fake checkout command") Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\(message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( "A fake commit command") Tj T* ( 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 192.6236 cm
+1 0 0 1 62.69291 246.8236 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 /F4 10 Tf (-h ) Tj /F1 10 Tf (after the name of the command:) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm .431318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that I have defined both an ) Tj /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (hook and a ) Tj /F4 10 Tf (__missing__ ) Tj /F1 10 Tf (hook, invoked for non-existing) Tj T* 0 Tw .592651 Tw (commands. The real trick here is the line ) Tj /F4 10 Tf (main = __import__\(__name__\)) Tj /F1 10 Tf (, which define ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (to be) Tj T* 0 Tw (an alias for the current module.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 216.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 /F4 10 Tf (vcs ) Tj /F1 10 Tf (module does not contain an ) Tj /F4 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block, but you can still run it) Tj T* 0 Tw (through the plac runner \(try ) Tj /F4 10 Tf (plac vcs.py -h) Tj /F1 10 Tf (\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 99.42362 cm
+1 0 0 1 62.69291 99.62362 cm
q
q
1 0 0 1 0 0 cm
@@ -4043,11 +4683,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 108 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py status -h) Tj T* (usage: vcs.py status [-h] [-q]) Tj T* T* (A fake status command) Tj T* T* (optional arguments:) Tj T* ET
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* T* (subcommands:) Tj T* ET
Q
Q
Q
@@ -4057,17 +4697,17 @@ 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 (15) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R146': class PDFStream
-146 0 obj
+% 'R177': class PDFStream
+177 0 obj
% page stream
-<< /Length 5361 >>
+<< /Length 3422 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -4085,27 +4725,54 @@ n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ( -h, --help show this help message and exit) Tj T* ( -q, --quiet summary information) Tj T* ET
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ( {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 707.8236 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 /F4 10 Tf (-h ) Tj /F1 10 Tf (after the name of the command:) Tj T* ET
+Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
+1 0 0 1 62.69291 590.6236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 108 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py status -h) Tj T* (usage: vcs.py status [-h] [-q]) Tj T* T* (A fake status command) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -q, --quiet summary information) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 558.6236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.064985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice how the docstring of the command is automatically shown in usage message, as well as the) Tj T* 0 Tw (documentation for the sub flag ) Tj /F4 10 Tf (-q) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 677.8236 cm
+1 0 0 1 62.69291 540.6236 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 548.6236 cm
+1 0 0 1 62.69291 411.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4126,14 +4793,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 528.6236 cm
+1 0 0 1 62.69291 391.4236 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 327.4236 cm
+1 0 0 1 62.69291 190.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4153,38 +4820,55 @@ Q
Q
Q
q
-1 0 0 1 62.69291 295.4236 cm
+1 0 0 1 62.69291 158.2236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.986905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the invocation of the ) Tj /F4 10 Tf (__missing__ ) Tj /F1 10 Tf (hook for non-existing commands. Notice also that the) Tj T* 0 Tw /F4 10 Tf (__exit__ ) Tj /F1 10 Tf (hook gets called only in interactive mode.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 265.4236 cm
+1 0 0 1 62.69291 128.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.614104 Tw (If the commands are completely independent, a module is a good fit for a method container. In other) Tj T* 0 Tw (situations, it is best to use a custom class.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 232.4236 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 (16) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R178': class PDFStream
+178 0 obj
+% page stream
+<< /Length 5984 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 744.0236 cm
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 178.4236 cm
+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 50 lines of code\) so that you) Tj T* 0 Tw .081294 Tw (can study it and write your own runner if want to. If you need to go to such level of detail, you should know) Tj T* 0 Tw .42061 Tw (that the most important method of the ) Tj /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class is the ) Tj /F4 10 Tf (.send ) Tj /F1 10 Tf (method, which takes strings in) Tj T* 0 Tw (input and returns a four-tuple with attributes ) Tj /F4 10 Tf (.str) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (.etype) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (.exc ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (.tb) Tj /F1 10 Tf (:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 172.4236 cm
+1 0 0 1 62.69291 684.0236 cm
Q
q
-1 0 0 1 62.69291 172.4236 cm
+1 0 0 1 62.69291 684.0236 cm
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 666.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4204,13 +4888,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 666.0236 cm
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 666.0236 cm
Q
q
-1 0 0 1 62.69291 136.4236 cm
+1 0 0 1 62.69291 648.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4230,13 +4914,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 136.4236 cm
+1 0 0 1 62.69291 648.0236 cm
Q
q
-1 0 0 1 62.69291 136.4236 cm
+1 0 0 1 62.69291 648.0236 cm
Q
q
-1 0 0 1 62.69291 118.4236 cm
+1 0 0 1 62.69291 630.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4256,13 +4940,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 118.4236 cm
+1 0 0 1 62.69291 630.0236 cm
Q
q
-1 0 0 1 62.69291 118.4236 cm
+1 0 0 1 62.69291 630.0236 cm
Q
q
-1 0 0 1 62.69291 100.4236 cm
+1 0 0 1 62.69291 612.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4282,42 +4966,25 @@ q
Q
Q
q
-1 0 0 1 62.69291 100.4236 cm
-Q
-q
-1 0 0 1 62.69291 100.4236 cm
+1 0 0 1 62.69291 612.0236 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 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
-Q
+1 0 0 1 62.69291 612.0236 cm
Q
-
-endstream
-
-endobj
-% 'R147': class PDFStream
-147 0 obj
-% page stream
-<< /Length 4689 >>
-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 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 /F4 10 Tf (__str__ ) Tj /F1 10 Tf (representation of the output object is redefined to return the output string if the) Tj T* 0 Tw 2.686651 Tw (command was successful or the error message if the command failed \(actually it returns the error) Tj T* 0 Tw (message preceded by the name of the exception class\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 711.0236 cm
+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 /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (will be trapped:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 617.8236 cm
+1 0 0 1 62.69291 458.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4337,20 +5004,20 @@ Q
Q
Q
q
-1 0 0 1 62.69291 585.8236 cm
+1 0 0 1 62.69291 426.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.90561 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is important to invoke the ) Tj /F4 10 Tf (.send ) Tj /F1 10 Tf (method inside the context manager, otherwise you will get a) Tj T* 0 Tw /F4 10 Tf (RuntimeError) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 543.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 306.6236 cm
+1 0 0 1 62.69291 147.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4371,20 +5038,37 @@ Q
Q
Q
q
-1 0 0 1 62.69291 274.6236 cm
+1 0 0 1 62.69291 115.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .102765 Tw (You can adapt the pseudocode to your GUI toolkit of choice and you can also change the file associations) Tj T* 0 Tw (in such a way that clicking on a plac tool file the graphical user interface starts.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 220.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 235.3849 0 Td (17) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R179': class PDFStream
+179 0 obj
+% page stream
+<< /Length 4283 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 717.0236 cm
q
BT 1 0 0 1 0 40.82 Tm 2.090651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a final ) Tj /F5 10 Tf (caveat) Tj /F1 10 Tf (: since the plac interpreter loop is implemented via extended generators, plac) Tj T* 0 Tw .988651 Tw (interpreters are single threaded: you will get an error if you ) Tj /F4 10 Tf (.send ) Tj /F1 10 Tf (commands from separated threads.) Tj T* 0 Tw .947882 Tw (You can circumvent the problem by using a queue. If EXIT is a sentinel value to signal exiting from the) Tj T* 0 Tw (interpreter look, you can write code like this:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 163.4236 cm
+1 0 0 1 62.69291 659.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4405,42 +5089,25 @@ Q
Q
Q
q
-1 0 0 1 62.69291 131.4236 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R148': class PDFStream
-148 0 obj
-% page stream
-<< /Length 4115 >>
-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 594.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Long running commands) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 702.0236 cm
+1 0 0 1 62.69291 552.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.434431 Tw 12 TL /F1 10 Tf 0 0 0 rg (As we saw, by default a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreter blocks until the command terminates. This is an issue, in the) Tj T* 0 Tw 1.201318 Tw (sense that it makes the interactive experience quite painful for long running commands. An example is) Tj T* 0 Tw (better than a thousand words, so consider the following fake importer:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 440.8236 cm
+1 0 0 1 62.69291 291.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4461,13 +5128,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 408.8236 cm
+1 0 0 1 62.69291 259.6236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.466457 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you run the ) Tj /F4 10 Tf (import_file ) Tj /F1 10 Tf (command, you will have to wait for 200 seconds before entering a new) Tj T* 0 Tw (command:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 279.6236 cm
+1 0 0 1 62.69291 130.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4487,29 +5154,53 @@ Q
Q
Q
q
-1 0 0 1 62.69291 235.6236 cm
+1 0 0 1 62.69291 98.42362 cm
q
-BT 1 0 0 1 0 28.82 Tm .96832 Tw 12 TL /F1 10 Tf 0 0 0 rg (Being unable to enter any other command is quite annoying: in such situation one would like to run the) Tj T* 0 Tw .941318 Tw (long running commands in the background, to keep the interface responsive. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides two ways to) Tj T* 0 Tw (reach this goal: threads and processes.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .96832 Tw 12 TL /F1 10 Tf 0 0 0 rg (Being unable to enter any other command is quite annoying: in such situation one would like to run the ) Tj T* 0 Tw .941318 Tw (long running commands in the background, to keep the interface responsive. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides two ways to) Tj T* 0 Tw ET
Q
Q
q
-1 0 0 1 62.69291 202.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 235.3849 0 Td (18) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R180': class PDFStream
+180 0 obj
+% page stream
+<< /Length 5339 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 753.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (reach this goal: threads and processes.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 720.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Threaded commands) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 172.6236 cm
+1 0 0 1 62.69291 690.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .317988 Tw (The most familiar way to execute a task in the background \(even if not necessarily the best way\) is to run) Tj T* 0 Tw (it into a separated thread. In our example it is sufficient to replace the line) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 166.6236 cm
+1 0 0 1 62.69291 684.0236 cm
Q
q
-1 0 0 1 62.69291 154.6236 cm
+1 0 0 1 62.69291 672.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -4524,20 +5215,20 @@ q
Q
Q
q
-1 0 0 1 62.69291 154.6236 cm
+1 0 0 1 62.69291 672.0236 cm
Q
q
-1 0 0 1 62.69291 136.6236 cm
+1 0 0 1 62.69291 654.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (with) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 130.6236 cm
+1 0 0 1 62.69291 648.0236 cm
Q
q
-1 0 0 1 62.69291 118.6236 cm
+1 0 0 1 62.69291 636.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -4552,33 +5243,16 @@ q
Q
Q
q
-1 0 0 1 62.69291 118.6236 cm
+1 0 0 1 62.69291 636.0236 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 235.3849 0 Td (16) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R149': class PDFStream
-149 0 obj
-% page stream
-<< /Length 4990 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 741.0236 cm
+1 0 0 1 62.69291 606.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.38311 Tw 12 TL /F1 10 Tf 0 0 0 rg (to tell to the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (interpreter that the command ) Tj /F4 10 Tf (import_file ) Tj /F1 10 Tf (should be run into a separated thread.) Tj T* 0 Tw (Here is an example session:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
+1 0 0 1 62.69291 560.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4598,13 +5272,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 663.8236 cm
+1 0 0 1 62.69291 528.8236 cm
q
BT 1 0 0 1 0 16.82 Tm .595777 Tw 12 TL /F1 10 Tf 0 0 0 rg (The import task started in a separated thread. You can see the progress of the task by using the special) Tj T* 0 Tw (command ) Tj /F4 10 Tf (.output) Tj /F1 10 Tf (:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 594.6236 cm
+1 0 0 1 62.69291 459.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4624,14 +5298,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 574.6236 cm
+1 0 0 1 62.69291 439.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (If you look after a while, you will get more lines of output:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 481.4236 cm
+1 0 0 1 62.69291 346.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4651,14 +5325,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 461.4236 cm
+1 0 0 1 62.69291 326.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (If you look after a time long enough, the task will be finished:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 416.2236 cm
+1 0 0 1 62.69291 281.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -4678,14 +5352,20 @@ Q
Q
Q
q
-1 0 0 1 62.69291 396.2236 cm
+1 0 0 1 62.69291 249.2236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.045868 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can even skip the number argument: then ) Tj /F4 10 Tf (.output ) Tj /F1 10 Tf (will the return the output of the last launched) Tj T* 0 Tw (command \(the special commands like .output do not count\).) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 231.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can launch many tasks one after the other:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 327.0236 cm
+1 0 0 1 62.69291 162.0236 cm
q
q
1 0 0 1 0 0 cm
@@ -4705,13 +5385,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 307.0236 cm
+1 0 0 1 62.69291 142.0236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (.list ) Tj /F1 10 Tf (command displays all the running tasks:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 249.8236 cm
+1 0 0 1 62.69291 96.82362 cm
q
q
1 0 0 1 0 0 cm
@@ -4721,24 +5401,34 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 36 re B*
Q
q
-BT 1 0 0 1 0 29.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .list) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] RUNNING) Tj (>) Tj T* (<) Tj (ThreadedTask 6 [import_file file3] RUNNING) Tj (>) Tj T* ET
+BT 1 0 0 1 0 17.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .list) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] RUNNING) Tj (>) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 229.8236 cm
+1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (It is even possible to kill a task:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (19) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R181': class PDFStream
+181 0 obj
+% page stream
+<< /Length 4995 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 136.6236 cm
+1 0 0 1 62.69291 739.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4748,46 +5438,50 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 24 re B*
Q
q
-BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .kill 5) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] TOBEKILLED) Tj (>) Tj T* (# wait a bit ...) Tj T* (closing the file) Tj T* (i) Tj (>) Tj ( .output 5) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] KILLED) Tj (>) Tj T* ET
+BT 1 0 0 1 0 5.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (<) Tj (ThreadedTask 6 [import_file file3] RUNNING) Tj (>) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 92.62362 cm
+1 0 0 1 62.69291 719.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm 1.100542 Tw 12 TL /F1 10 Tf 0 0 0 rg (You should notice that since at the Python level it is impossible to kill a thread, the ) Tj /F4 10 Tf (.kill ) Tj /F1 10 Tf (commands ) Tj T* 0 Tw 1.793984 Tw (works by setting the status of the task to ) Tj /F4 10 Tf (TOBEKILLED) Tj /F1 10 Tf (. Internally the generator corresponding to the ) Tj T* 0 Tw .632927 Tw (command is executed in the thread and the status is checked at each iteration: when the status become) Tj T* 0 Tw ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (It is even possible to kill a task:) Tj T* ET
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 626.6236 cm
q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (17) Tj T* -235.3849 0 Td ET
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 re B*
+Q
+q
+BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .kill 5) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] TOBEKILLED) Tj (>) Tj T* (# wait a bit ...) Tj T* (closing the file) Tj T* (i) Tj (>) Tj ( .output 5) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] KILLED) Tj (>) Tj T* ET
+Q
+Q
+Q
Q
Q
-
-endstream
-
-endobj
-% 'R150': class PDFStream
-150 0 obj
-% page stream
-<< /Length 4477 >>
-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
+1 0 0 1 62.69291 534.6236 cm
q
-BT 1 0 0 1 0 40.82 Tm 2.578443 Tw 12 TL /F4 10 Tf 0 0 0 rg (TOBEKILLED ) Tj /F1 10 Tf (a ) Tj /F4 10 Tf (GeneratorExit ) Tj /F1 10 Tf (exception is raised and the thread terminates \(softly, so that the) Tj T* 0 Tw 2.328651 Tw /F4 10 Tf (finally ) Tj /F1 10 Tf (clause is honored\). In our example the generator is yielding back control once every 100) Tj T* 0 Tw 1.152619 Tw (iterations, i.e. every two seconds \(not much\). In order to get a responsive interface it is a good idea to) Tj T* 0 Tw (yield more often, for instance every 10 iterations \(i.e. 5 times per second\), as in the following code:) Tj T* ET
+BT 1 0 0 1 0 76.82 Tm 1.100542 Tw 12 TL /F1 10 Tf 0 0 0 rg (You should notice that since at the Python level it is impossible to kill a thread, the ) Tj /F4 10 Tf (.kill ) Tj /F1 10 Tf (commands) Tj T* 0 Tw 1.793984 Tw (works by setting the status of the task to ) Tj /F4 10 Tf (TOBEKILLED) Tj /F1 10 Tf (. Internally the generator corresponding to the) Tj T* 0 Tw .632927 Tw (command is executed in the thread and the status is checked at each iteration: when the status become) Tj T* 0 Tw 2.578443 Tw /F4 10 Tf (TOBEKILLED ) Tj /F1 10 Tf (a ) Tj /F4 10 Tf (GeneratorExit ) Tj /F1 10 Tf (exception is raised and the thread terminates \(softly, so that the) Tj T* 0 Tw 2.328651 Tw /F4 10 Tf (finally ) Tj /F1 10 Tf (clause is honored\). In our example the generator is yielding back control once every 100) Tj T* 0 Tw 1.152619 Tw (iterations, i.e. every two seconds \(not much\). In order to get a responsive interface it is a good idea to) Tj T* 0 Tw (yield more often, for instance every 10 iterations \(i.e. 5 times per second\), as in the following code:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 431.8236 cm
+1 0 0 1 62.69291 249.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4808,29 +5502,29 @@ Q
Q
Q
q
-1 0 0 1 62.69291 398.8236 cm
+1 0 0 1 62.69291 216.4236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Running commands as external processes) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 344.8236 cm
+1 0 0 1 62.69291 162.4236 cm
q
BT 1 0 0 1 0 40.82 Tm 2.30686 Tw 12 TL /F1 10 Tf 0 0 0 rg (Threads are not loved much in the Python world and actually most people prefer to use processes) Tj T* 0 Tw 3.350697 Tw (instead. For this reason ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides the option to execute long running commands as external) Tj T* 0 Tw .632706 Tw (processes. Unfortunately the current implementation only works in Unix-like operating systems \(including) Tj T* 0 Tw (Mac OS X\) because it relies on fork via 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 326.8236 cm
+1 0 0 1 62.69291 144.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (In our example, to enable the feature it is sufficient to replace the line) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 320.8236 cm
+1 0 0 1 62.69291 138.4236 cm
Q
q
-1 0 0 1 62.69291 308.8236 cm
+1 0 0 1 62.69291 126.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -4845,20 +5539,20 @@ q
Q
Q
q
-1 0 0 1 62.69291 308.8236 cm
+1 0 0 1 62.69291 126.4236 cm
Q
q
-1 0 0 1 62.69291 290.8236 cm
+1 0 0 1 62.69291 108.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (with) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 284.8236 cm
+1 0 0 1 62.69291 102.4236 cm
Q
q
-1 0 0 1 62.69291 272.8236 cm
+1 0 0 1 62.69291 90.42362 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -4872,17 +5566,34 @@ q
Q
Q
q
-1 0 0 1 62.69291 272.8236 cm
+1 0 0 1 62.69291 90.42362 cm
Q
q
-1 0 0 1 62.69291 242.8236 cm
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (20) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R182': class PDFStream
+182 0 obj
+% page stream
+<< /Length 5075 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 741.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .772619 Tw (The user experience is exactly the same as with threads and you will not see any difference at the user) Tj T* 0 Tw (interface level:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 101.6236 cm
+1 0 0 1 62.69291 599.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4902,62 +5613,379 @@ Q
Q
Q
q
+1 0 0 1 62.69291 543.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 40.82 Tm /F1 10 Tf 12 TL 1.201318 Tw (Still, using processes is quite different than using threads: in particular, when using processes you can) Tj T* 0 Tw 2.313318 Tw (only yield pickleable values and you cannot re-raise an exception first raised in a different process,) Tj T* 0 Tw 1.445697 Tw (because traceback objects are not pickleable. Moreover, you cannot rely on automatic sharing of your) Tj T* 0 Tw (objects.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 489.8236 cm
+q
+BT 1 0 0 1 0 40.82 Tm .128935 Tw 12 TL /F1 10 Tf 0 0 0 rg (On the plus side, when using processes you do not need to worry about killing a command: they are killed) Tj T* 0 Tw .466412 Tw (immediately using a SIGTERM signal, and there is not a ) Tj /F4 10 Tf (TOBEKILLED ) Tj /F1 10 Tf (mechanism. Moreover, the killing) Tj T* 0 Tw 1.50229 Tw (is guaranteed to be soft: internally a command receiving a SIGTERM raises a ) Tj /F4 10 Tf (TerminatedProcess) Tj T* 0 Tw /F1 10 Tf (exception which is trapped in the generator loop, so that the command is closed properly.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 459.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .242927 Tw (Using processes allows to take full advantage of multicore machines and it is safer than using threads, so) Tj T* 0 Tw (it is the recommended approach unless you are working on Windows.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 426.8236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Managing the output of concurrent commands) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 324.8236 cm
+q
+BT 1 0 0 1 0 88.82 Tm 1.895542 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (acts as a command-line task launcher and can be used as the base to build a GUI-based task) Tj T* 0 Tw .38561 Tw (launcher and task monitor. To this aim the interpreter class provides a ) Tj /F4 10 Tf (.submit ) Tj /F1 10 Tf (method which returns a) Tj T* 0 Tw 1.792339 Tw (task object and a ) Tj /F4 10 Tf (.tasks ) Tj /F1 10 Tf (method returning the list of all the tasks submitted to the interpreter. The) Tj T* 0 Tw .373516 Tw /F4 10 Tf (submit ) Tj /F1 10 Tf (method does not start the task and thus it is nonblocking. Each task has an ) Tj /F4 10 Tf (.outlist ) Tj /F1 10 Tf (attribute) Tj T* 0 Tw .106098 Tw (which is a list storing the value yielded by the generator underlying the task \(the ) Tj /F4 10 Tf (None ) Tj /F1 10 Tf (values are skipped) Tj T* 0 Tw .633318 Tw (though\): the ) Tj /F4 10 Tf (.outlist ) Tj /F1 10 Tf (grows as the task runs and more values are yielded. Accessing the ) Tj /F4 10 Tf (.outlist) Tj T* 0 Tw 1.051654 Tw /F1 10 Tf (is nonblocking and can be done freely. Finally there is a ) Tj /F4 10 Tf (.result ) Tj /F1 10 Tf (property which waits for the task to) Tj T* 0 Tw (finish and returns the last yielded value or raises an exception.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 294.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .014692 Tw (Here is some example code to visualize the output of the FakeImporter in Tkinter \(I chose Tkinter because) Tj T* 0 Tw (it is easy to use and it is in the standard library, but you can use any GUI\):) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 93.62362 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 192 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 173.71 Tm /F4 10 Tf 12 TL (from Tkinter import *) Tj T* (from importer3 import FakeImporter) Tj T* T* (def taskwidget\(root, task, tick=500\):) Tj T* ( "A Label widget showing the output of a task every 500 ms") Tj T* ( sv = StringVar\(root\)) Tj T* ( lb = Label\(root, textvariable=sv\)) Tj T* ( def show_outlist\(\):) Tj T* ( try:) Tj T* ( out = task.outlist[-1]) Tj T* ( except IndexError: # no output yet) Tj T* ( out = '') Tj T* ( sv.set\('%s %s' % \(task, out\)\)) Tj T* ( root.after\(tick, show_outlist\)) Tj T* ( root.after\(0, show_outlist\)) 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 (18) 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 (21) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R151': class PDFStream
-151 0 obj
+% 'R183': class PDFStream
+183 0 obj
% page stream
-<< /Length 7386 >>
+<< /Length 4632 >>
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
+1 0 0 1 62.69291 583.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 180 re B*
+Q
q
0 0 0 rg
-BT 1 0 0 1 0 40.82 Tm /F1 10 Tf 12 TL 1.201318 Tw (Still, using processes is quite different than using threads: in particular, when using processes you can) Tj T* 0 Tw 2.313318 Tw (only yield pickleable values and you cannot re-raise an exception first raised in a different process,) Tj T* 0 Tw 1.445697 Tw (because traceback objects are not pickleable. Moreover, you cannot rely on automatic sharing of your) Tj T* 0 Tw (objects.) Tj T* ET
+BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL ( return lb) Tj T* T* (def monitor\(tasks\):) Tj T* ( root = Tk\(\)) Tj T* ( for task in tasks:) Tj T* ( task.run\(\)) Tj T* ( taskwidget\(root, task\).pack\(\)) Tj T* ( root.mainloop\(\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac) Tj T* ( with plac.Interpreter\(plac.call\(FakeImporter\)\) as i:) Tj T* ( tasks = [i.submit\('import_file f1'\), i.submit\('import_file f2'\)]) Tj T* ( monitor\(tasks\)) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 663.0236 cm
+1 0 0 1 62.69291 550.8236 cm
q
-BT 1 0 0 1 0 40.82 Tm .128935 Tw 12 TL /F1 10 Tf 0 0 0 rg (On the plus side, when using processes you do not need to worry about killing a command: they are killed) Tj T* 0 Tw .466412 Tw (immediately using a SIGTERM signal, and there is not a ) Tj /F4 10 Tf (TOBEKILLED ) Tj /F1 10 Tf (mechanism. Moreover, the killing) Tj T* 0 Tw 1.50229 Tw (is guaranteed to be soft: internally a command receiving a SIGTERM raises a ) Tj /F4 10 Tf (TerminatedProcess) Tj T* 0 Tw /F1 10 Tf (exception which is trapped in the generator loop, so that the command is closed properly.) Tj T* ET
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Parallel computing with plac) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 436.8236 cm
+q
+BT 1 0 0 1 0 100.82 Tm 1.174751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is certainly not intended as a tool for parallel computing, but still you can use it to launch a set of) Tj T* 0 Tw .494269 Tw (commands and to collect the results, similarly to the MapReduce pattern recently popularized by Google.) Tj T* 0 Tw .329431 Tw (In order to give an example, I will consider the "Hello World" of parallel computing, i.e. the computation of) Tj T* 0 Tw .229431 Tw (pi with independent processes. There is a huge number of algorithms to compute pi; here I will describe a) Tj T* 0 Tw .30683 Tw (trivial one chosen for simplicity, not per efficienty. The trick is to consider the first quadrant of a circle with) Tj T* 0 Tw .102488 Tw (radius 1 and to extract a number of points ) Tj /F4 10 Tf (\(x, y\) ) Tj /F1 10 Tf (with ) Tj /F4 10 Tf (x ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (y ) Tj /F1 10 Tf (random variables in the interval ) Tj /F4 10 Tf ([0,1]) Tj /F1 10 Tf (.) Tj T* 0 Tw .743876 Tw (The probability of extracting a number inside the quadrant \(i.e. with ) Tj /F4 10 Tf (x^2 + y^2 < 1) Tj /F1 10 Tf (\) is proportional to) Tj T* 0 Tw .725251 Tw (the area of the quadrant \(i.e. ) Tj /F4 10 Tf (pi/4) Tj /F1 10 Tf (\). The value of ) Tj /F4 10 Tf (pi ) Tj /F1 10 Tf (therefore can be extracted by multiplying by 4 the) Tj T* 0 Tw (ratio between the number of points in the quadrant versus the total number of points ) Tj /F4 10 Tf (N) Tj /F1 10 Tf (, for ) Tj /F4 10 Tf (N ) Tj /F1 10 Tf (large:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 331.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 96 re B*
+Q
+q
+BT 1 0 0 1 0 77.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (def calc_pi\(N\):) Tj T* ( inside = 0) Tj T* ( for j in xrange\(N\):) Tj T* ( x, y = random\(\), random\(\)) Tj T* ( if x*x + y*y ) Tj (<) Tj ( 1:) Tj T* ( inside += 1) Tj T* ( return \(4.0 * inside\) / N) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 633.0236 cm
+1 0 0 1 62.69291 263.6236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .242927 Tw (Using processes allows to take full advantage of multicore machines and it is safer than using threads, so) Tj T* 0 Tw (it is the recommended approach unless you are working on Windows.) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm /F1 10 Tf 12 TL .046654 Tw (The algorithm is trivially parallelizable: if you have n CPUs, you can compute pi n times with N/n iterations,) Tj T* 0 Tw 1.122488 Tw (sum the results and divide the total by n. I have a Macbook with two cores, therefore I would expect a) Tj T* 0 Tw 2.347984 Tw (speedup factor of 2 with respect to a sequential computation. Moreover, I would expect a threaded) Tj T* 0 Tw 2.827984 Tw (computation to be even slower than a sequential computation, due to the GIL and the scheduling) Tj T* 0 Tw (overhead.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 233.6236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .313984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a script implementing the algorithm and working in three different modes \(parallel mode, threaded) Tj T* 0 Tw (mode and sequential mode\) depending on a ) Tj /F4 10 Tf (mode ) Tj /F1 10 Tf (option:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 92.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 132 re B*
+Q
+q
+BT 1 0 0 1 0 113.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (from __future__ import with_statement) Tj T* (from random import random) Tj T* (import multiprocessing) Tj T* (import plac) Tj T* T* (class PiCalculator\(object\):) Tj T* ( """Compute pi in parallel with threads or processes""") Tj T* ( ) Tj T* ( @plac.annotations\() Tj T* ( npoints=\('number of integration points', 'positional', None, int\),) 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 (22) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R184': class PDFStream
+184 0 obj
+% page stream
+<< /Length 2492 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 115.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 648 re B*
+Q
+q
+BT 1 0 0 1 0 629.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( mode=\('sequential|parallel|threaded', 'option', 'm', str, 'SPT'\)\)) Tj T* ( def __init__\(self, npoints, mode='S'\):) Tj T* ( self.npoints = npoints) Tj T* ( if mode == 'P':) Tj T* ( self.mpcommands = ['calc_pi']) Tj T* ( elif mode == 'T':) Tj T* ( self.thcommands = ['calc_pi']) Tj T* ( elif mode == 'S':) Tj T* ( self.commands = ['calc_pi']) Tj T* ( self.n_cpu = multiprocessing.cpu_count\(\)) Tj T* ( ) Tj T* ( def submit_tasks\(self\):) Tj T* ( self.i = plac.Interpreter\(self\).__enter__\(\) ) Tj T* ( return [self.i.submit\('calc_pi %d' % \(self.npoints / self.n_cpu\)\)) Tj T* ( for _ in range\(self.n_cpu\)]) Tj T* T* ( def close\(self\):) Tj T* ( self.i.close\(\)) Tj T* T* ( @plac.annotations\() Tj T* ( npoints=\('npoints', 'positional', None, int\)\)) Tj T* ( def calc_pi\(self, npoints\):) Tj T* ( counts = 0) Tj T* ( for j in xrange\(npoints\):) Tj T* ( n, r = divmod\(j, 1000000\)) Tj T* ( if r == 0:) Tj T* ( yield '%dM iterations' % n) Tj T* ( x, y = random\(\), random\(\)) Tj T* ( if x*x + y*y ) Tj (<) Tj ( 1:) Tj T* ( counts += 1) Tj T* ( yield \(4.0 * counts\)/npoints) Tj T* T* ( def run\(self\):) Tj T* ( tasks = self.i.tasks\(\)) Tj T* ( for t in tasks:) Tj T* ( t.run\(\)) Tj T* ( try:) Tj T* ( total = 0) Tj T* ( for task in tasks:) Tj T* ( total += task.result) Tj T* ( except: # the task was killed) Tj T* ( print tasks) Tj T* ( return) Tj T* ( return total / self.n_cpu) Tj T* T* (if __name__ == '__main__':) Tj T* ( pc = plac.call\(PiCalculator\)) Tj T* ( pc.submit_tasks\(\)) Tj T* ( try:) Tj T* ( import time; t0 = time.time\(\)) Tj T* ( print '%f in %f seconds ' % \(pc.run\(\), time.time\(\) - t0\)) Tj T* ( finally:) Tj T* ( pc.close\(\)) 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 (23) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R185': class PDFStream
+185 0 obj
+% page stream
+<< /Length 4788 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 693.0236 cm
+q
+BT 1 0 0 1 0 64.82 Tm .381797 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the ) Tj /F4 10 Tf (submit_tasks ) Tj /F1 10 Tf (method, which instantiates and initializes a ) Tj /F4 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (object and) Tj T* 0 Tw 3.38152 Tw (submits a number of commands corresponding to the number of available CPUs. The ) Tj /F4 10 Tf (calc_pi) Tj T* 0 Tw 3.796651 Tw /F1 10 Tf (command yield a log message every million of interactions, just to monitor the progress of the) Tj T* 0 Tw 1.751318 Tw (computation. The ) Tj /F4 10 Tf (run ) Tj /F1 10 Tf (method starts all the submitted commands in parallel and sums the results. It) Tj T* 0 Tw 1.17104 Tw (returns the average value of ) Tj /F4 10 Tf (pi ) Tj /F1 10 Tf (after the slowest CPU has finished its job \(if the CPUs are equal and) Tj T* 0 Tw (equally busy they should finish more or less at the same time\).) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 675.0236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here are the results on my old Macbook with Ubuntu 10.04 and Python 2.6, for 10 million of iterations:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 581.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ($ python picalculator.py -mP 10000000) Tj T* (3.141904 in 5.744545 seconds) Tj T* ($ python picalculator.py -mT 10000000) Tj T* (3.141272 in 13.875645 seconds) Tj T* ($ python picalculator.py -mS 10000000) Tj T* (3.141586 in 11.353841 seconds) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 549.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.711751 Tw (As you see using processes one gets a 2x speedup indeed, where the threaded mode is some 20%) Tj T* 0 Tw (slower than the sequential mode.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 516.8236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The plac server) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 402.8236 cm
+q
+BT 1 0 0 1 0 100.82 Tm 1.258443 Tw 12 TL /F1 10 Tf 0 0 0 rg (A command-line oriented interface can be easily converted into a socket-based interface. Starting from) Tj T* 0 Tw .930574 Tw (release 0.7 plac features a builtin server which is able to accept commands from multiple clients and to) Tj T* 0 Tw .994692 Tw (execute them. The server works by instantiating a separate interpreter for each client, so that if a client) Tj T* 0 Tw 1.08784 Tw (interpreter dies for any reason the other interpreters keep working. To avoid external dependencies the) Tj T* 0 Tw .872209 Tw (server is based on the ) Tj /F4 10 Tf (asynchat ) Tj /F1 10 Tf (module in the standard library, but it would not be difficult to replace) Tj T* 0 Tw 2.386412 Tw (the server with a different one \(for instance, a Twisted server\). Since ) Tj /F4 10 Tf (asynchat) Tj /F1 10 Tf (-based servers are) Tj T* 0 Tw .755984 Tw (asynchronous, any blocking command in the interpreter should be run in a separated process or thread.) Tj T* 0 Tw 1.157633 Tw (The default port for the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (server is 2199, and the command to signal end-of-connection is EOF. For) Tj T* 0 Tw (instance, here is how you could manage remote import on a database:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 273.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 /F4 10 Tf 12 TL (import plac) Tj T* (from importer2 import FakeImporter) Tj T* T* (def main\(port=2199\):) Tj T* ( main = FakeImporter\('dsn'\)) Tj T* ( plac.Interpreter\(main\).start_server\(port\)) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 253.6236 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can connect to the server with ) Tj /F4 10 Tf (telnet ) Tj /F1 10 Tf (on port 2199, as follows:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 100.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 144 re B*
+Q
+q
+BT 1 0 0 1 0 125.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ telnet localhost 2199) Tj T* (Trying ::1...) Tj T* (Trying 127.0.0.1...) Tj T* (Connected to localhost.) Tj T* (Escape character is '^]'.) Tj T* (i) Tj (>) Tj ( import_file f1) Tj T* (i) Tj (>) Tj ( .list) Tj T* (<) Tj (ThreadedTask 1 [import_file f1] RUNNING) Tj (>) Tj T* (i) Tj (>) Tj ( .out) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) 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 (24) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R186': class PDFStream
+186 0 obj
+% page stream
+<< /Length 7197 >>
+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
+BT 1 0 0 1 0 17.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( EOF) Tj T* (Connection closed by foreign host.) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 600.0236 cm
+1 0 0 1 62.69291 694.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 558.0236 cm
+1 0 0 1 62.69291 652.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 552.0236 cm
+1 0 0 1 62.69291 646.8236 cm
Q
q
-1 0 0 1 62.69291 552.0236 cm
+1 0 0 1 62.69291 646.8236 cm
Q
q
-1 0 0 1 62.69291 534.0236 cm
+1 0 0 1 62.69291 628.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4977,13 +6005,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 534.0236 cm
+1 0 0 1 62.69291 628.8236 cm
Q
q
-1 0 0 1 62.69291 534.0236 cm
+1 0 0 1 62.69291 628.8236 cm
Q
q
-1 0 0 1 62.69291 474.0236 cm
+1 0 0 1 62.69291 568.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5061,13 +6089,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 474.0236 cm
+1 0 0 1 62.69291 568.8236 cm
Q
q
-1 0 0 1 62.69291 474.0236 cm
+1 0 0 1 62.69291 568.8236 cm
Q
q
-1 0 0 1 62.69291 444.0236 cm
+1 0 0 1 62.69291 538.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5087,13 +6115,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 444.0236 cm
+1 0 0 1 62.69291 538.8236 cm
Q
q
-1 0 0 1 62.69291 444.0236 cm
+1 0 0 1 62.69291 538.8236 cm
Q
q
-1 0 0 1 62.69291 414.0236 cm
+1 0 0 1 62.69291 508.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5113,13 +6141,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 414.0236 cm
+1 0 0 1 62.69291 508.8236 cm
Q
q
-1 0 0 1 62.69291 414.0236 cm
+1 0 0 1 62.69291 508.8236 cm
Q
q
-1 0 0 1 62.69291 384.0236 cm
+1 0 0 1 62.69291 478.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5139,38 +6167,64 @@ q
Q
Q
q
-1 0 0 1 62.69291 384.0236 cm
+1 0 0 1 62.69291 478.8236 cm
Q
q
-1 0 0 1 62.69291 384.0236 cm
+1 0 0 1 62.69291 478.8236 cm
Q
q
-1 0 0 1 62.69291 366.0236 cm
+1 0 0 1 62.69291 448.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 (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 16.82 Tm 2.171647 Tw 12 TL /F1 10 Tf 0 0 0 rg (the ) Tj /F4 10 Tf (.start_server ) Tj /F1 10 Tf (method starts an asynchronous server on the given port number \(default) Tj T* 0 Tw (2199\)) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 448.8236 cm
+Q
+q
+1 0 0 1 62.69291 448.8236 cm
+Q
+q
+1 0 0 1 62.69291 430.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 /F4 10 Tf (plac_runner.py ) Tj /F1 10 Tf (is your friend.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 333.0236 cm
+1 0 0 1 62.69291 397.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 303.0236 cm
+1 0 0 1 62.69291 367.8236 cm
q
BT 1 0 0 1 0 16.82 Tm .578651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses an ) Tj /F4 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the tuples in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F4 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 273.0236 cm
+1 0 0 1 62.69291 337.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 143.8236 cm
+1 0 0 1 62.69291 208.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -5191,31 +6245,52 @@ Q
Q
Q
q
-1 0 0 1 62.69291 123.8236 cm
+1 0 0 1 62.69291 188.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
Q
Q
q
+1 0 0 1 62.69291 95.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 /F4 10 Tf 12 TL (# example11.py) Tj T* (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* 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 (19) 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 (25) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R152': class PDFStream
-152 0 obj
+% 'R187': class PDFStream
+187 0 obj
% page stream
-<< /Length 2033 >>
+<< /Length 1862 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 595.8236 cm
+1 0 0 1 62.69291 667.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -5225,25 +6300,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 96 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 149.71 Tm /F4 10 Tf 12 TL (# example11.py) Tj T* (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* ( n=Positional\("This is a float", float\),) Tj T* ( rest=Positional\("Other arguments"\)\)) Tj T* (def main\(i, n, *rest\):) Tj T* ( print\(i, n, rest\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL ( 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 575.8236 cm
+1 0 0 1 62.69291 647.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 you get:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 446.6236 cm
+1 0 0 1 62.69291 518.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -5264,7 +6339,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 402.6236 cm
+1 0 0 1 62.69291 474.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 /F4 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (Flag ) Tj /F1 10 Tf (classes, if you like. Using custom annotation objects you) Tj T* 0 Tw .17528 Tw (could do advanced things like extracting the annotations from a configuration file or from a database, but I) Tj T* 0 Tw (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
Q
@@ -5273,179 +6348,227 @@ 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 (20) 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 (26) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R153': class PDFPageLabels
-153 0 obj
+% 'R188': class PDFPageLabels
+188 0 obj
% Document Root
<< /Nums [ 0
- 154 0 R
+ 189 0 R
1
- 155 0 R
+ 190 0 R
2
- 156 0 R
+ 191 0 R
3
- 157 0 R
+ 192 0 R
4
- 158 0 R
+ 193 0 R
5
- 159 0 R
+ 194 0 R
6
- 160 0 R
+ 195 0 R
7
- 161 0 R
+ 196 0 R
8
- 162 0 R
+ 197 0 R
9
- 163 0 R
+ 198 0 R
10
- 164 0 R
+ 199 0 R
11
- 165 0 R
+ 200 0 R
12
- 166 0 R
+ 201 0 R
13
- 167 0 R
+ 202 0 R
14
- 168 0 R
+ 203 0 R
15
- 169 0 R
+ 204 0 R
16
- 170 0 R
+ 205 0 R
17
- 171 0 R
+ 206 0 R
18
- 172 0 R
+ 207 0 R
19
- 173 0 R ] >>
-endobj
-% 'R154': class PDFPageLabel
-154 0 obj
+ 208 0 R
+ 20
+ 209 0 R
+ 21
+ 210 0 R
+ 22
+ 211 0 R
+ 23
+ 212 0 R
+ 24
+ 213 0 R
+ 25
+ 214 0 R ] >>
+endobj
+% 'R189': class PDFPageLabel
+189 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R155': class PDFPageLabel
-155 0 obj
+% 'R190': class PDFPageLabel
+190 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R156': class PDFPageLabel
-156 0 obj
+% 'R191': class PDFPageLabel
+191 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R157': class PDFPageLabel
-157 0 obj
+% 'R192': class PDFPageLabel
+192 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R158': class PDFPageLabel
-158 0 obj
+% 'R193': class PDFPageLabel
+193 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R159': class PDFPageLabel
-159 0 obj
+% 'R194': class PDFPageLabel
+194 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R160': class PDFPageLabel
-160 0 obj
+% 'R195': class PDFPageLabel
+195 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R161': class PDFPageLabel
-161 0 obj
+% 'R196': class PDFPageLabel
+196 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R162': class PDFPageLabel
-162 0 obj
+% 'R197': class PDFPageLabel
+197 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R163': class PDFPageLabel
-163 0 obj
+% 'R198': class PDFPageLabel
+198 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R164': class PDFPageLabel
-164 0 obj
+% 'R199': class PDFPageLabel
+199 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R165': class PDFPageLabel
-165 0 obj
+% 'R200': class PDFPageLabel
+200 0 obj
% None
<< /S /D
/St 12 >>
endobj
-% 'R166': class PDFPageLabel
-166 0 obj
+% 'R201': class PDFPageLabel
+201 0 obj
% None
<< /S /D
/St 13 >>
endobj
-% 'R167': class PDFPageLabel
-167 0 obj
+% 'R202': class PDFPageLabel
+202 0 obj
% None
<< /S /D
/St 14 >>
endobj
-% 'R168': class PDFPageLabel
-168 0 obj
+% 'R203': class PDFPageLabel
+203 0 obj
% None
<< /S /D
/St 15 >>
endobj
-% 'R169': class PDFPageLabel
-169 0 obj
+% 'R204': class PDFPageLabel
+204 0 obj
% None
<< /S /D
/St 16 >>
endobj
-% 'R170': class PDFPageLabel
-170 0 obj
+% 'R205': class PDFPageLabel
+205 0 obj
% None
<< /S /D
/St 17 >>
endobj
-% 'R171': class PDFPageLabel
-171 0 obj
+% 'R206': class PDFPageLabel
+206 0 obj
% None
<< /S /D
/St 18 >>
endobj
-% 'R172': class PDFPageLabel
-172 0 obj
+% 'R207': class PDFPageLabel
+207 0 obj
% None
<< /S /D
/St 19 >>
endobj
-% 'R173': class PDFPageLabel
-173 0 obj
+% 'R208': class PDFPageLabel
+208 0 obj
% None
<< /S /D
/St 20 >>
endobj
+% 'R209': class PDFPageLabel
+209 0 obj
+% None
+<< /S /D
+ /St 21 >>
+endobj
+% 'R210': class PDFPageLabel
+210 0 obj
+% None
+<< /S /D
+ /St 22 >>
+endobj
+% 'R211': class PDFPageLabel
+211 0 obj
+% None
+<< /S /D
+ /St 23 >>
+endobj
+% 'R212': class PDFPageLabel
+212 0 obj
+% None
+<< /S /D
+ /St 24 >>
+endobj
+% 'R213': class PDFPageLabel
+213 0 obj
+% None
+<< /S /D
+ /St 25 >>
+endobj
+% 'R214': class PDFPageLabel
+214 0 obj
+% None
+<< /S /D
+ /St 26 >>
+endobj
xref
-0 174
+0 215
0000000000 65535 f
0000000113 00000 n
0000000257 00000 n
@@ -5471,163 +6594,204 @@ xref
0000004804 00000 n
0000005047 00000 n
0000005290 00000 n
-0000005533 00000 n
-0000005776 00000 n
-0000006019 00000 n
-0000006262 00000 n
-0000006505 00000 n
-0000006748 00000 n
-0000006992 00000 n
-0000007236 00000 n
-0000007480 00000 n
-0000007724 00000 n
-0000007968 00000 n
-0000008212 00000 n
-0000008456 00000 n
-0000008700 00000 n
-0000008944 00000 n
-0000009187 00000 n
-0000009439 00000 n
-0000009691 00000 n
-0000009943 00000 n
-0000010195 00000 n
-0000010432 00000 n
-0000011080 00000 n
-0000011323 00000 n
-0000011575 00000 n
-0000011832 00000 n
-0000012168 00000 n
-0000012405 00000 n
-0000012708 00000 n
-0000013005 00000 n
-0000013257 00000 n
-0000013509 00000 n
-0000013761 00000 n
-0000014021 00000 n
-0000014273 00000 n
-0000014534 00000 n
-0000014785 00000 n
-0000015046 00000 n
-0000015298 00000 n
-0000015535 00000 n
-0000015934 00000 n
-0000016185 00000 n
-0000016421 00000 n
-0000016748 00000 n
-0000017020 00000 n
-0000017272 00000 n
-0000017524 00000 n
-0000017761 00000 n
-0000018106 00000 n
-0000018358 00000 n
-0000018610 00000 n
-0000018854 00000 n
-0000019190 00000 n
-0000019427 00000 n
-0000019745 00000 n
-0000020001 00000 n
-0000020253 00000 n
-0000020505 00000 n
-0000020757 00000 n
-0000021016 00000 n
-0000021275 00000 n
-0000021525 00000 n
-0000021776 00000 n
-0000022026 00000 n
-0000022278 00000 n
-0000022530 00000 n
-0000022768 00000 n
-0000023185 00000 n
-0000023437 00000 n
-0000023676 00000 n
-0000024003 00000 n
-0000024253 00000 n
-0000024491 00000 n
-0000024804 00000 n
-0000025101 00000 n
-0000025338 00000 n
-0000025656 00000 n
-0000025914 00000 n
-0000026234 00000 n
-0000026487 00000 n
-0000026726 00000 n
-0000027056 00000 n
-0000027295 00000 n
-0000027615 00000 n
-0000027868 00000 n
-0000028126 00000 n
-0000028456 00000 n
-0000028709 00000 n
-0000028948 00000 n
-0000029264 00000 n
-0000029549 00000 n
-0000029713 00000 n
-0000029967 00000 n
-0000030096 00000 n
-0000030272 00000 n
-0000030492 00000 n
-0000030698 00000 n
-0000030893 00000 n
-0000031091 00000 n
-0000031295 00000 n
-0000031491 00000 n
-0000031686 00000 n
-0000031891 00000 n
-0000032100 00000 n
-0000032303 00000 n
-0000032502 00000 n
-0000032722 00000 n
-0000032911 00000 n
-0000033096 00000 n
-0000033384 00000 n
-0000042533 00000 n
-0000047132 00000 n
-0000051686 00000 n
-0000057943 00000 n
-0000063827 00000 n
-0000068719 00000 n
-0000073410 00000 n
-0000077590 00000 n
-0000081508 00000 n
-0000086672 00000 n
-0000091828 00000 n
-0000097407 00000 n
-0000100924 00000 n
-0000106388 00000 n
-0000111180 00000 n
-0000115398 00000 n
-0000120491 00000 n
-0000125071 00000 n
-0000132560 00000 n
-0000134700 00000 n
-0000135072 00000 n
-0000135151 00000 n
-0000135230 00000 n
-0000135309 00000 n
-0000135388 00000 n
-0000135467 00000 n
-0000135546 00000 n
-0000135625 00000 n
-0000135704 00000 n
-0000135783 00000 n
-0000135863 00000 n
-0000135943 00000 n
-0000136023 00000 n
-0000136103 00000 n
-0000136183 00000 n
-0000136263 00000 n
-0000136343 00000 n
-0000136423 00000 n
-0000136503 00000 n
-0000136583 00000 n
+0000005534 00000 n
+0000005778 00000 n
+0000006022 00000 n
+0000006266 00000 n
+0000006510 00000 n
+0000006754 00000 n
+0000006998 00000 n
+0000007242 00000 n
+0000007486 00000 n
+0000007730 00000 n
+0000007974 00000 n
+0000008218 00000 n
+0000008462 00000 n
+0000008706 00000 n
+0000008950 00000 n
+0000009194 00000 n
+0000009438 00000 n
+0000009682 00000 n
+0000009926 00000 n
+0000010170 00000 n
+0000010414 00000 n
+0000010657 00000 n
+0000010909 00000 n
+0000011146 00000 n
+0000011821 00000 n
+0000012073 00000 n
+0000012325 00000 n
+0000012577 00000 n
+0000012820 00000 n
+0000013072 00000 n
+0000013324 00000 n
+0000013576 00000 n
+0000013828 00000 n
+0000014085 00000 n
+0000014475 00000 n
+0000014712 00000 n
+0000015015 00000 n
+0000015312 00000 n
+0000015564 00000 n
+0000015816 00000 n
+0000016068 00000 n
+0000016328 00000 n
+0000016580 00000 n
+0000016841 00000 n
+0000017092 00000 n
+0000017353 00000 n
+0000017605 00000 n
+0000017842 00000 n
+0000018241 00000 n
+0000018492 00000 n
+0000018728 00000 n
+0000019055 00000 n
+0000019327 00000 n
+0000019579 00000 n
+0000019831 00000 n
+0000020068 00000 n
+0000020413 00000 n
+0000020665 00000 n
+0000020917 00000 n
+0000021161 00000 n
+0000021497 00000 n
+0000021734 00000 n
+0000022052 00000 n
+0000022308 00000 n
+0000022560 00000 n
+0000022811 00000 n
+0000023063 00000 n
+0000023322 00000 n
+0000023567 00000 n
+0000023930 00000 n
+0000024180 00000 n
+0000024431 00000 n
+0000024681 00000 n
+0000024933 00000 n
+0000025185 00000 n
+0000025437 00000 n
+0000025690 00000 n
+0000025930 00000 n
+0000026314 00000 n
+0000026551 00000 n
+0000026871 00000 n
+0000027124 00000 n
+0000027377 00000 n
+0000027630 00000 n
+0000027883 00000 n
+0000028136 00000 n
+0000028387 00000 n
+0000028640 00000 n
+0000028879 00000 n
+0000029255 00000 n
+0000029553 00000 n
+0000029792 00000 n
+0000030098 00000 n
+0000030396 00000 n
+0000030634 00000 n
+0000030954 00000 n
+0000031226 00000 n
+0000031479 00000 n
+0000031718 00000 n
+0000032059 00000 n
+0000032298 00000 n
+0000032619 00000 n
+0000032873 00000 n
+0000033131 00000 n
+0000033462 00000 n
+0000033701 00000 n
+0000034022 00000 n
+0000034261 00000 n
+0000034567 00000 n
+0000034866 00000 n
+0000035105 00000 n
+0000035426 00000 n
+0000035680 00000 n
+0000035919 00000 n
+0000036235 00000 n
+0000036520 00000 n
+0000036684 00000 n
+0000036938 00000 n
+0000037067 00000 n
+0000037243 00000 n
+0000037463 00000 n
+0000037669 00000 n
+0000037864 00000 n
+0000038062 00000 n
+0000038266 00000 n
+0000038462 00000 n
+0000038658 00000 n
+0000038864 00000 n
+0000039074 00000 n
+0000039277 00000 n
+0000039476 00000 n
+0000039696 00000 n
+0000039920 00000 n
+0000040130 00000 n
+0000040327 00000 n
+0000040516 00000 n
+0000040701 00000 n
+0000041053 00000 n
+0000050435 00000 n
+0000055843 00000 n
+0000060129 00000 n
+0000065526 00000 n
+0000071778 00000 n
+0000077134 00000 n
+0000081556 00000 n
+0000085921 00000 n
+0000089772 00000 n
+0000094378 00000 n
+0000100507 00000 n
+0000105458 00000 n
+0000110602 00000 n
+0000113792 00000 n
+0000117579 00000 n
+0000121104 00000 n
+0000127191 00000 n
+0000131577 00000 n
+0000137019 00000 n
+0000142117 00000 n
+0000147295 00000 n
+0000152030 00000 n
+0000154625 00000 n
+0000159516 00000 n
+0000166816 00000 n
+0000168785 00000 n
+0000169247 00000 n
+0000169326 00000 n
+0000169405 00000 n
+0000169484 00000 n
+0000169563 00000 n
+0000169642 00000 n
+0000169721 00000 n
+0000169800 00000 n
+0000169879 00000 n
+0000169958 00000 n
+0000170038 00000 n
+0000170118 00000 n
+0000170198 00000 n
+0000170278 00000 n
+0000170358 00000 n
+0000170438 00000 n
+0000170518 00000 n
+0000170598 00000 n
+0000170678 00000 n
+0000170758 00000 n
+0000170838 00000 n
+0000170918 00000 n
+0000170998 00000 n
+0000171078 00000 n
+0000171158 00000 n
+0000171238 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\317\011\333\344\342}}2\233\022:\30301\344\211) (\317\011\333\344\342}}2\233\022:\30301\344\211)]
+ [(\373.sKQ\210\237\313\200B\274O\370\202\343\022) (\373.sKQ\210\237\313\200B\274O\370\202\343\022)]
- /Info 115 0 R
- /Root 114 0 R
- /Size 174 >>
+ /Info 141 0 R
+ /Root 140 0 R
+ /Size 215 >>
startxref
-136632
+171287
%%EOF
diff --git a/plac/doc/plac_adv.txt b/plac/doc/plac_adv.txt
index 3508464..633c445 100644
--- a/plac/doc/plac_adv.txt
+++ b/plac/doc/plac_adv.txt
@@ -3,7 +3,7 @@ Advanced usages of plac
:Author: Michele Simionato
:E-mail: michele.simionato@gmail.com
-:Date: July 2010
+:Date: August 2010
:Download page: http://pypi.python.org/pypi/plac
:Project page: http://micheles.googlecode.com/hg/plac/doc/plac.html
:Installation: ``easy_install -U plac``
@@ -39,13 +39,16 @@ 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!
+could use it to script your building procedure. plac_ also supports
+parallel execution of multiple commands and can be used as
+task manager and monitor. It is also quite easy to build a GUI
+or a Web application on top of plac_. When speaking of things
+you can do with plac_, your imagination is the only limit!
From scripts to interactive applications
------------------------------------------------------------
-Command-line scripts have many advantages, but are no substitute
+Command-line scripts have many advantages, but they are no substitute
for interactive applications.
In particular, if you have a script with a large startup time which must be run
multiple times, it is best to turn it into an interactive application,
@@ -328,7 +331,7 @@ and dispatch hooks (``__missing__``, invoked for invalid command names).
Moreover, only when using command containers plac_ is able to provide
automatic autocompletion of commands.
-Rhe shelve interface can be rewritten in an object-oriented way as follows:
+The shelve interface can be rewritten in an object-oriented way as follows:
.. include:: ishelve2.py
:literal:
@@ -406,14 +409,13 @@ Readline support
Starting from release 0.6 plac_ offers full readline support. That
means that if your Python was compiled with readline support you get
-autocompletion and persistent command history for free.
-By default all commands are autocomplete in a case sensitive way.
-If you want to add new words to the autocompletion set, or you want
-to change the location of the ``.history`` file, or to change the
-case sensitivirt, or you want to change the prompt, the way to do
-it is to pass a ``plac.ReadlineInput`` object to the interpreter.
-Here is an example, assuming you want to build a database interface
-understanding SQL commands:
+autocompletion and persistent command history for free. By default
+all commands are autocomplete in a case sensitive way. If you want to
+add new words to the autocompletion set, or you want to change the
+location of the ``.history`` file, or to change the case sensitivity,
+the way to go is to pass a ``plac.ReadlineInput`` object to the
+interpreter. Here is an example, assuming you want to build a
+database interface understanding SQL commands:
.. include:: sql_interface.py
:literal:
@@ -435,8 +437,9 @@ exiting from the command-line interface.
If the readline library is not available, my suggestion is to use the
rlwrap_ tool which provides similar features, at least on Unix-like
-platforms. plac_ should also work fine on Windows with the pyreadline
-library (I do not use Windows, so this part is very little tested).
+platforms. plac_ should also work fine on Windows with the pyreadline_
+library (I do not use Windows, so this part is very little tested: I
+tried it only once and it worked, but your mileage may vary).
For people worried about licenses, I will notice that plac_ uses the
readline library only if available, it does not include it and it does
not rely on it in any fundamental way, so that the plac_ licence does
@@ -481,7 +484,7 @@ features provided of argparse_ which should be reimplemented from scratch
using plac_.
Moreover at the moment ``plac`` also understands command abbreviations.
-However, this feature should be considered deprecated and may disappear in
+However, this feature may disappear in
future releases. It was meaningful in the past, when plac_ did not support
readline.
@@ -510,8 +513,12 @@ defined in the ``ishelve2`` module like the following one:
The first line of the ``.plac`` script contains the name of the
python module containing the plac interpreter and the arguments
which must be passed to its main function in order to be able
-to instantiate an interpreter object. The other lines contains
-commands. Then you can run the script as follows::
+to instantiate an interpreter object. In this case I appended
+``:ShelveInterface`` to the name of the module to specify the
+object that must be imported: if not specified, by default the
+object named 'main' is imported.
+The other lines contains commands.
+You can run the script as follows::
$ plac_runner.py --batch ishelve2.plac
setting a=1
@@ -590,9 +597,55 @@ Here is an example::
$ plac ishelve.py .show
a=1
-Notice that it non-interactive mode the runner just invokes ``plac.call``
+Notice that in non-interactive mode the runner just invokes ``plac.call``
on the ``main`` object of the Python module.
+..
+
+ Multiline support and Emacs integration
+
+ plac_ is optimized for the simplest use case and by default it provide
+ support for simple command-line languages where a command take
+ a single line. This is the simplest case: it is easy to keep
+ track of the line number and to print it in the error message, if
+ there is some error in a plac_ script. Starting from release 0.7
+ plac_ is beginning to support multiline input: it is now possible
+ to define command-line languages with commands spanning multiple
+ lines. The topical use case is the implementation of a tool
+ to interact with a relational database: the tool must be able to send
+ complex SQL queries spanning multiple lines to the backend.
+ To support multiline input the ``Interpreter`` class provides
+ a method ``multiline(stdin=sys.stdin, terminator=';', verbose=False)``
+ which reads input from ``stdin`` until the terminator character
+ (by default a semicolon) is reached.
+
+ Since the Python readline module does not expose the
+ multiline functionality of the underlying C library (which is there),
+ plac_ multiline mode does not have readline functionality. This is
+ not a big deal really, because if you are writing multiple line
+ commands you don't really want to type them at the command-line. It is
+ much better to use a real editor to type them, and to call plac_ from
+ the editor. Since I use Emacs I will give the recipe to integrate
+ Emacs with plac_: something equivalent can be done for vi and for
+ other editors/IDEs.
+
+ The multiline mode can be enabled by invoking the plac_ runner with
+ the ``-m`` option. Since the multiline mode is intended for use with
+ Emacs in inferior mode, it does not print any prompt. Here is an example
+ of usage::
+
+ $ plac -m ishelve2.py
+ set a 1;
+ setting a=1
+ show a;
+ a = 1
+
+ To integrate plac_ with Emacs, enters the following lines in your
+ .emacs:
+
+ .. include:: plac.el
+ :literal:
+
A non class-based example
--------------------------------------------------------
@@ -822,6 +875,10 @@ If you look after a time long enough, the task will be finished::
i> .output 1
<ThreadedTask 1 [import_file file1] FINISHED>
+You can even skip the number argument: then ``.output`` will the return
+the output of the last launched command (the special commands like .output
+do not count).
+
You can launch many tasks one after the other::
i> import_file file2
@@ -908,6 +965,131 @@ Using processes allows to take full advantage of multicore machines
and it is safer than using threads, so it is the recommended approach
unless you are working on Windows.
+Managing the output of concurrent commands
+---------------------------------------------
+
+plac_ acts as a command-line task launcher and can be used as the base
+to build a GUI-based task launcher and task monitor. To this aim the
+interpreter class provides a ``.submit`` method which returns a task
+object and a ``.tasks`` method returning the list of all the tasks
+submitted to the interpreter. The ``submit`` method does not start the task
+and thus it is nonblocking.
+Each task has an ``.outlist`` attribute which is a list
+storing the value yielded by the generator underlying the task (the
+``None`` values are skipped though): the ``.outlist`` grows as the
+task runs and more values are yielded. Accessing the ``.outlist`` is
+nonblocking and can be done freely.
+Finally there is a ``.result``
+property which waits for the task to finish and returns the last yielded
+value or raises an exception.
+
+Here is some example code to visualize the output of the FakeImporter
+in Tkinter (I chose Tkinter because it is easy to use and it is
+in the standard library, but you can use any GUI):
+
+.. include:: importer_ui.py
+ :literal:
+
+Parallel computing with plac
+---------------------------------------------
+
+plac_ is certainly not intended as a tool for parallel computing, but
+still you can use it to launch a set of commands and to collect the
+results, similarly to the MapReduce pattern recently popularized by
+Google. In order to give an example, I will consider the "Hello
+World" of parallel computing, i.e. the computation of pi with
+independent processes. There is a huge number of algorithms to
+compute pi; here I will describe a trivial one chosen for simplicity,
+not per efficienty. The trick is to consider the first quadrant of a
+circle with radius 1 and to extract a number of points ``(x, y)`` with
+``x`` and ``y`` random variables in the interval ``[0,1]``. The
+probability of extracting a number inside the quadrant (i.e. with
+``x^2 + y^2 < 1``) is proportional to the area of the quadrant
+(i.e. ``pi/4``). The value of ``pi`` therefore can be extracted by
+multiplying by 4 the ratio between the number of points in the
+quadrant versus the total number of points ``N``, for ``N`` large::
+
+ def calc_pi(N):
+ inside = 0
+ for j in xrange(N):
+ x, y = random(), random()
+ if x*x + y*y < 1:
+ inside += 1
+ return (4.0 * inside) / N
+
+The algorithm is trivially parallelizable: if you have n CPUs, you can
+compute pi n times with N/n iterations, sum the results and divide the total
+by n. I have a Macbook with two cores, therefore I would expect a speedup
+factor of 2 with respect to a sequential computation. Moreover, I would
+expect a threaded computation to be even slower than a sequential
+computation, due to the GIL and the scheduling overhead.
+
+Here is a script implementing the algorithm and working in three different
+modes (parallel mode, threaded mode and sequential mode) depending on a
+``mode`` option:
+
+.. include:: picalculator.py
+ :literal:
+
+Notice the ``submit_tasks`` method, which instantiates and initializes a
+``plac.Interpreter`` object and submits a number of commands corresponding
+to the number of available CPUs. The ``calc_pi`` command yield a log
+message every million of interactions, just to monitor the progress of
+the computation. The ``run`` method starts all the submitted commands
+in parallel and sums the results. It returns the average value of ``pi``
+after the slowest CPU has finished its job (if the CPUs are equal and
+equally busy they should finish more or less at the same time).
+
+Here are the results on my old Macbook with Ubuntu 10.04 and Python 2.6,
+for 10 million of iterations::
+
+ $ python picalculator.py -mP 10000000
+ 3.141904 in 5.744545 seconds
+ $ python picalculator.py -mT 10000000
+ 3.141272 in 13.875645 seconds
+ $ python picalculator.py -mS 10000000
+ 3.141586 in 11.353841 seconds
+
+As you see using processes one gets a 2x speedup indeed, where the threaded
+mode is some 20% slower than the sequential mode.
+
+The plac server
+-------------------------------------------------------
+
+A command-line oriented interface can be easily converted into a
+socket-based interface. Starting from release 0.7 plac features
+a builtin server which is able to accept commands from multiple
+clients and to execute them. The server works by instantiating
+a separate interpreter for each client, so that if a client interpreter
+dies for any reason the other interpreters keep working.
+To avoid external dependencies the server is based on the ``asynchat``
+module in the standard library, but it would not be difficult to
+replace the server with a different one (for instance, a Twisted server).
+Since ``asynchat``-based servers are asynchronous, any blocking command
+in the interpreter should be run in a separated process or thread.
+The default port for the plac_ server is 2199, and the command to
+signal end-of-connection is EOF.
+For instance, here is how you could manage remote import on a database:
+
+.. include:: server_ex.py
+ :literal:
+
+You can connect to the server with ``telnet`` on port 2199, as follows::
+
+ $ telnet localhost 2199
+ Trying ::1...
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ i> import_file f1
+ i> .list
+ <ThreadedTask 1 [import_file f1] RUNNING>
+ i> .out
+ Imported 100 lines
+ Imported 200 lines
+ i> EOF
+ Connection closed by foreign host.
+
Summary
-------------------------------------------------------
@@ -935,6 +1117,9 @@ rules are quite simple:
processes: just declare them in the lists ``thcommands`` and ``mpcommands``
respectively.
+6. the ``.start_server`` method starts an asynchronous server on the
+ given port number (default 2199)
+
Moreover, remember that ``plac_runner.py`` is your friend.
Appendix: custom annotation objects
diff --git a/plac/doc/server_ex.py b/plac/doc/server_ex.py
new file mode 100644
index 0000000..e3d76ec
--- /dev/null
+++ b/plac/doc/server_ex.py
@@ -0,0 +1,9 @@
+import plac
+from importer2 import FakeImporter
+
+def main(port=2199):
+ main = FakeImporter('dsn')
+ plac.Interpreter(main).start_server(port)
+
+if __name__ == '__main__':
+ plac.call(main)
diff --git a/plac/doc/sql_interface.py b/plac/doc/sql_interface.py
index ccedb97..16f0216 100644
--- a/plac/doc/sql_interface.py
+++ b/plac/doc/sql_interface.py
@@ -17,8 +17,7 @@ class SqlInterface(object):
yield str(row) # the formatting can be much improved
rl_input = plac.ReadlineInput(
- COMPLETIONS, prompt='sql> ',
- histfile=os.path.expanduser('~/.sql_interface.history'),
+ COMPLETIONS, histfile=os.path.expanduser('~/.sql_interface.history'),
case_sensitive=False)
def split_on_first_space(line, commentchar):
@@ -27,4 +26,4 @@ def split_on_first_space(line, commentchar):
if __name__ == '__main__':
si = plac.call(SqlInterface)
i = plac.Interpreter(si, split=split_on_first_space)
- i.interact(rl_input)
+ i.interact(rl_input, prompt='sql> ')
diff --git a/plac/doc/test_pi.py b/plac/doc/test_pi.py
new file mode 100644
index 0000000..9f1fbd0
--- /dev/null
+++ b/plac/doc/test_pi.py
@@ -0,0 +1,11 @@
+import time
+from picalculator import PiCalculator
+
+def test():
+ pc = PiCalculator(10, 'T')
+ tasks = pc.submit_tasks()
+ for task in tasks:
+ task.run()
+ print(sum(task.result for task in tasks)/pc.n_cpu)
+ pc.close()
+
diff --git a/plac/doc/test_server.py b/plac/doc/test_server.py
new file mode 100644
index 0000000..dd14e5c
--- /dev/null
+++ b/plac/doc/test_server.py
@@ -0,0 +1,35 @@
+import multiprocessing, subprocess, time, random
+import plac
+from ishelve2 import ShelveInterface
+
+i = plac.Interpreter(ShelveInterface())
+
+COMMANDS = ['''\
+.help
+set a 1
+''',
+'''\
+set b 1
+wrong command
+showall
+''']
+
+def client_send(commands, port):
+ time.sleep(.5) # wait a bit for the server to start
+ po = subprocess.Popen(['telnet', 'localhost', str(port)],
+ stdin=subprocess.PIPE)
+ for cmd in commands.splitlines():
+ po.stdin.write(cmd + '\n')
+ time.sleep(.1) # wait a bit for the server to answer
+
+def test():
+ port = random.choice(range(2000, 20000))
+ clients = []
+ for cmds in COMMANDS:
+ cl = multiprocessing.Process(target=client_send, args=(cmds, port))
+ clients.append(cl)
+ cl.start()
+ i.stop_server(wait=1)
+ i.start_server(port, timeout=.1)
+ for cl in clients:
+ cl.join()
diff --git a/plac/plac.py b/plac/plac.py
index 0dfc67e..9aed3a7 100644
--- a/plac/plac.py
+++ b/plac/plac.py
@@ -27,9 +27,9 @@
See doc/plac.pdf, doc/plac_adv.pdf for the documentation.
"""
-__version__ = '0.6.1'
+__version__ = '0.7.0'
from plac_core import *
if sys.version >= '2.5':
- from plac_ext import Interpreter, import_main, ReadlineInput
+ from plac_ext import Interpreter, import_main, ReadlineInput, stdout
diff --git a/plac/plac_core.py b/plac/plac_core.py
index 89cb9a7..2237cab 100644
--- a/plac/plac_core.py
+++ b/plac/plac_core.py
@@ -94,6 +94,8 @@ def parser_from(obj, **confparams):
conf = pconf(obj).copy()
conf.update(confparams)
parser_registry[obj] = parser = ArgumentParser(**conf)
+ parser.case_sensitive = confparams.get(
+ 'case_sensitive', getattr(obj, 'case_sensitive', True))
if hasattr(obj, 'commands') and obj.commands and not inspect.isclass(obj):
# a command container instance
parser.addsubcommands(obj.commands, obj, 'subcommands')
@@ -117,8 +119,10 @@ def _extract_kwargs(args):
arglist.append(arg)
return arglist, kwargs
-def _match_cmd(abbrev, commands):
+def _match_cmd(abbrev, commands, case_sensitive=True):
"Extract the command name from an abbreviation or raise a NameError"
+ if not case_sensitive:
+ abbrev = abbrev.upper(); commands = [c.upper() for c in commands]
perfect_matches = [name for name in commands if name == abbrev]
if len(perfect_matches) == 1:
return perfect_matches[0]
@@ -135,6 +139,8 @@ class ArgumentParser(argparse.ArgumentParser):
An ArgumentParser with .func and .argspec attributes, and possibly
.commands and .subparsers.
"""
+ case_sensitive = True
+
def consume(self, args):
"""Call the underlying function with the args. Works also for
command containers, by dispatching to the right subparser."""
@@ -169,7 +175,7 @@ class ArgumentParser(argparse.ArgumentParser):
name_parser_map = self.subparsers._name_parser_map
for i, arg in enumerate(arglist):
if not arg.startswith(optprefix):
- cmd = _match_cmd(arg, name_parser_map)
+ cmd = _match_cmd(arg, name_parser_map, self.case_sensitive)
del arglist[i]
return name_parser_map.get(cmd), cmd or arg
return None, None
diff --git a/plac/plac_ext.py b/plac/plac_ext.py
index 4249e4d..2a38c4e 100644
--- a/plac/plac_ext.py
+++ b/plac/plac_ext.py
@@ -9,12 +9,13 @@ import plac_core
try:
import readline
except ImportError:
- readline = None
+ readline = False
############################# generic utils ################################
@contextmanager
def stdout(fileobj):
+ "usage: with stdout(file('out.txt', 'a')): do_something()"
orig_stdout = sys.stdout
sys.stdout = fileobj
try:
@@ -57,23 +58,50 @@ def terminatedProcess(signum, frame):
########################### readline support #############################
+def read_line(stdin, prompt=''):
+ "Read a line from stdin, using readline when possible"
+ if isinstance(stdin, ReadlineInput):
+ return stdin.readline(prompt)
+ else:
+ write(prompt)
+ return stdin.readline()
+
+def read_long_line(stdin, terminator):
+ """
+ Read multiple lines from stdin until the terminator character is found, then
+ yield a single space-separated long line.
+ """
+ while True:
+ lines = []
+ while True:
+ line = stdin.readline() # ends with \n
+ if not line: # EOF
+ return
+ line = line.strip()
+ if not line:
+ continue
+ elif line[-1] == terminator:
+ lines.append(line[:-1])
+ break
+ else:
+ lines.append(line)
+ yield ' '.join(lines)
+
class ReadlineInput(object):
"""
- An iterable with a .readline method reading from stdin with readline
- features enabled, if possible.
+ An iterable with a .readline method reading from stdin.
"""
- def __init__(self, completions, prompt='', case_sensitive=True,
- histfile=None):
+ def __init__(self, completions, case_sensitive=True, histfile=None):
self.completions = completions
self.case_sensitive = case_sensitive
self.histfile = histfile
- self.prompt = prompt
if not case_sensitive:
self.completions = map(str.upper, completions)
readline.parse_and_bind("tab: complete")
readline.set_completer(self.complete)
def __enter__(self):
+ self.old_completer = readline.get_completer()
try:
if self.histfile:
readline.read_history_file(self.histfile)
@@ -82,6 +110,7 @@ class ReadlineInput(object):
return self
def __exit__(self, etype, exc, tb):
+ readline.set_completer(self.old_completer)
if self.histfile:
readline.write_history_file(self.histfile)
@@ -94,9 +123,9 @@ class ReadlineInput(object):
except IndexError: # no completions
return # exit
- def readline(self):
+ def readline(self, prompt=''):
try:
- return raw_input(self.prompt) + '\n'
+ return raw_input(prompt) + '\n'
except EOFError:
return ''
@@ -115,6 +144,10 @@ def import_main(path, *args, **pconf):
An utility to import the main function of a plac tool. It also
works with tool factories, if you pass the arguments.
"""
+ if ':' in path:
+ path, main_name = path.split(':')
+ else:
+ main_name = 'main'
if not os.path.isabs(path): # relative path, look at PLACDIRS
for placdir in PLACDIRS:
fullpath = os.path.join(placdir, path)
@@ -125,7 +158,8 @@ def import_main(path, *args, **pconf):
else:
fullpath = path
name, ext = os.path.splitext(os.path.basename(fullpath))
- main = imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1)).main
+ module = imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1))
+ main = getattr(module, main_name)
if args:
cmd, tool = plac_core.parser_from(main).consume(args)
else:
@@ -149,7 +183,6 @@ class BaseTask(object):
.exc
.tb
.status
- .synchronous
and methods .run and .kill.
"""
STATES = ('SUBMITTED', 'RUNNING', 'TOBEKILLED', 'KILLED', 'FINISHED',
@@ -177,7 +210,8 @@ class BaseTask(object):
if value is not None: # add output
self.outlist.append(value)
yield
- except (GeneratorExit, TerminatedProcess): # soft termination
+ except (GeneratorExit, TerminatedProcess, KeyboardInterrupt):
+ # soft termination
self.status = 'KILLED'
except: # unexpected exception
self.etype, self.exc, tb = sys.exc_info()
@@ -185,7 +219,10 @@ class BaseTask(object):
self.status = 'ABORTED'
else: # regular exit
self.status = 'FINISHED'
- self.str = '\n'.join(map(str, self.outlist))
+ try:
+ self.str = str(self.outlist[-1])
+ except IndexError:
+ self.str = 'no result'
def run(self):
"Run the inner generator"
@@ -209,7 +246,14 @@ class BaseTask(object):
else:
return ''.join(traceback.format_tb(self.tb))
- def __str__(self):
+ @property
+ def result(self):
+ self.wait()
+ if self.exc:
+ raise self.etype, self.exc, self.tb or None
+ return self.outlist[-1]
+
+ def __repr__(self):
"String representation containing class name, number, arglist, status"
return '<%s %d [%s] %s>' % (
self.__class__.__name__, self.no,
@@ -217,42 +261,22 @@ class BaseTask(object):
########################## synchronous tasks ###############################
-class Outlist(object):
- "A list wrapper displaying each appended value on stdout"
- def __init__(self):
- self._ls = []
- def append(self, value):
- self._ls.append(value)
- print(value)
- def __iter__(self):
- return iter(self._ls)
- def __len__(self):
- return len(self._ls)
-
class SynTask(BaseTask):
"""
Synchronous task running in the interpreter loop and displaying its
output as soon as available.
- """
- synchronous = True
-
- def __init__(self, no, arglist, genobj):
- BaseTask.__init__(self, no, arglist, genobj)
- self.outlist = Outlist()
-
+ """
def __str__(self):
"Return the output string or the error message"
if self.etype: # there was an error
return '%s: %s' % (self.etype.__name__, self.exc)
else:
- return self.str
+ return '\n'.join(map(str, self.outlist))
class ThreadedTask(BaseTask):
"""
A task running in a separated thread.
"""
- synchronous = False
-
def __init__(self, no, arglist, genobj):
BaseTask.__init__(self, no, arglist, genobj)
self.thread = threading.Thread(target=super(ThreadedTask, self).run)
@@ -267,12 +291,18 @@ class ThreadedTask(BaseTask):
######################### multiprocessing tasks ##########################
-def sharedattr(name):
- "Return a property to be attached to an object with a .ns attribute"
+def sharedattr(name, on_error):
+ "Return a property to be attached to an MPTask"
def get(self):
- return getattr(self.ns, name)
+ try:
+ return getattr(self.ns, name)
+ except: # the process was killed or died hard
+ return on_error
def set(self, value):
- setattr(self.ns, name, value)
+ try:
+ setattr(self.ns, name, value)
+ except: # the process was killed or died hard
+ pass
return property(get, set)
class MPTask(BaseTask):
@@ -280,26 +310,29 @@ class MPTask(BaseTask):
A task running as an external process. The current implementation
only works on Unix-like systems, where multiprocessing use forks.
"""
+ str = sharedattr('str', '')
+ etype = sharedattr('etype', None)
+ exc = sharedattr('exc', None)
+ tb = sharedattr('tb', None)
+ status = sharedattr('status', 'ABORTED')
- synchronous = False
- _mp_manager = None
-
- str = sharedattr('str')
- etype = sharedattr('etype')
- exc = sharedattr('exc')
- tb = sharedattr('tb')
- status = sharedattr('status')
+ @property
+ def outlist(self):
+ try:
+ return self._outlist
+ except: # the process died hard
+ return []
- def __init__(self, no, arglist, genobj):
- if self.__class__._mp_manager is None: # the first time
- self.__class__._mp_manager = multiprocessing.Manager()
+ def __init__(self, no, arglist, genobj, mp_manager):
self.no = no
self.arglist = arglist
- self.outlist = self._mp_manager.list()
- self.ns = self._mp_manager.Namespace()
- self.str, self.etype, self.exc, self.tb = '*', None, None, None
- self.status = 'SUBMITTED'
self._genobj = self._wrap(genobj, stringify_tb=True)
+ self.mp_manager = mp_manager
+ self._outlist = self.mp_manager.list()
+ self.ns = self.mp_manager.Namespace()
+ self.status = 'SUBMITTED'
+ self.etype, self.exc, self.tb = None, None, None
+ self.str = repr(self)
self.proc = multiprocessing.Process(target=super(MPTask, self).run)
def run(self):
@@ -307,7 +340,7 @@ class MPTask(BaseTask):
self.proc.start()
def wait(self):
- "Block until the external process ends"
+ "Block until the external process ends or is killed"
self.proc.join()
def kill(self):
@@ -353,23 +386,20 @@ class TaskManager(object):
if obj.mpcommands or obj.thcommands:
self.specialcommands.update(['.kill', '.list', '.output'])
self.helpsummary = HelpSummary.make(obj, self.specialcommands)
+ self.mp_manager = multiprocessing.Manager() if obj.mpcommands else None
signal.signal(signal.SIGTERM, terminatedProcess)
- def run_task(self, task):
- "Run the task and update the registry"
- if not task.arglist:
- return
- cmd = task.arglist[0]
- if cmd not in self.specialcommands:
- self.registry[task.no] = task
- task.run()
-
def close(self):
"Kill all the running tasks"
for task in self.registry.itervalues():
- if task.status == 'RUNNING':
- task.kill()
- task.wait()
+ try:
+ if task.status == 'RUNNING':
+ task.kill()
+ task.wait()
+ except: # task killed, nothing to wait
+ pass
+ if self.mp_manager:
+ self.mp_manager.shutdown()
def _get_latest(self, taskno=-1, status=None):
"Get the latest submitted task from the registry"
@@ -427,7 +457,7 @@ class TaskManager(object):
return
else:
task = self.registry[taskno]
- outstr = '\n'.join(task.outlist)
+ outstr = '\n'.join(map(str, task.outlist))
yield task
if len(task.outlist) > 20 and use_less:
less(outstr)
@@ -484,6 +514,74 @@ plac.Interpreter(plac.import_main(*%s)).interact(prompt='i>\\n')
self.stdin.write(line + os.linesep)
return self.recv()
+########################## plac server ##############################
+
+import asyncore, asynchat, socket
+
+class _AsynHandler(asynchat.async_chat):
+ "asynchat handler starting a new interpreter loop for each connection"
+
+ terminator = '\r\n' # the standard one for telnet
+ prompt = 'i> '
+
+ def __init__(self, socket, interpreter):
+ asynchat.async_chat.__init__(self, socket)
+ self.set_terminator(self.terminator)
+ self.i = interpreter
+ self.i.__enter__()
+ self.data = []
+ self.write(self.prompt)
+
+ def write(self, data, *args):
+ "Push a string back to the client"
+ if args:
+ data %= args
+ if data.endswith('\n') and not data.endswith(self.terminator):
+ data = data[:-1] + self.terminator # fix newlines
+ self.push(data)
+
+ def collect_incoming_data(self, data):
+ "Collect one character at the time"
+ self.data.append(data)
+
+ def found_terminator(self):
+ "Put in the queue the line received from the client"
+ line = ''.join(self.data)
+ self.log('Received line %r from %s' % (line, self.addr))
+ if line == 'EOF':
+ self.i.__exit__()
+ self.handle_close()
+ else:
+ task = self.i.submit(line)
+ task.run() # synchronous or not
+ if task.etype: # manage exception
+ error = '%s: %s\nReceived: %s' % (
+ task.etype.__name__, task.exc, ' '.join(task.arglist))
+ self.log_info(task.traceback + error) # on the server
+ self.write(error + self.terminator) # back to the client
+ else: # no exception
+ self.write(task.str + self.terminator)
+ self.data = []
+ self.write(self.prompt)
+
+class _AsynServer(asyncore.dispatcher):
+ "asyncore-based server spawning AsynHandlers"
+
+ def __init__(self, interpreter, newhandler, port, listen=5):
+ self.interpreter = interpreter
+ self.newhandler = newhandler
+ self.port = port
+ asyncore.dispatcher.__init__(self)
+ self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.bind(('', port))
+ self.listen(listen)
+
+ def handle_accept(self):
+ clientsock, clientaddr = self.accept()
+ self.log('Connected from %s' % str(clientaddr))
+ i = self.interpreter.__class__(self.interpreter.obj) # new interpreter
+ self.newhandler(clientsock, i) # spawn a new handler
+
########################### the Interpreter #############################
class Interpreter(object):
@@ -532,14 +630,16 @@ class Interpreter(object):
self.commands.update(obj.thcommands)
def __enter__(self):
+ "Start the inner interpreter loop"
self._interpreter = self._make_interpreter()
self._interpreter.send(None)
return self
def __exit__(self, *exc):
+ "Close the inner interpreter and the task manager"
self.close()
- def make_task(self, line):
+ def submit(self, line):
"Send a line to the underlying interpreter and return a task object"
if self._interpreter is None:
raise RuntimeError(_('%r not initialized: probably you forgot to '
@@ -548,14 +648,22 @@ class Interpreter(object):
arglist = self.split(line, self.commentchar)
else: # expects a list of strings
arglist = line
- return self._interpreter.send(arglist)
-
+ task = self._interpreter.send(arglist) # nonblocking
+ if arglist and not plac_core._match_cmd(
+ arglist[0], self.tm.specialcommands):
+ self.tm.registry[task.no] = task
+ return task
+
def send(self, line):
- "Send a line to the underlying interpreter and return the result"
- task = self.make_task(line)
+ "Send a line to the underlying interpreter and return the finished task"
+ task = self.submit(line)
BaseTask.run(task) # blocking
return task
+ def tasks(self):
+ "The full lists of the submitted tasks"
+ return self.tm.registry.values()
+
def close(self):
"Can be called to close the interpreter prematurely"
self.tm.close()
@@ -578,7 +686,7 @@ class Interpreter(object):
if not plac_core.iterable(result): # atomic result
task = SynTask(no, arglist, gen_val(result))
elif cmd in self.obj.mpcommands:
- task = MPTask(no, arglist, result)
+ task = MPTask(no, arglist, result, self.tm.mp_manager)
elif cmd in self.obj.thcommands:
task = ThreadedTask(no, arglist, result)
else: # blocking task
@@ -599,13 +707,8 @@ class Interpreter(object):
given_input, output, expected_output)
raise AssertionError(msg)
- def _getoutputs(self, 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])
-
def _parse_doctest(self, lineiter):
+ "Returns the lines of input, the lines of output, and the line number"
lines = [line.strip() for line in lineiter]
inputs = []
positions = []
@@ -614,7 +717,11 @@ class Interpreter(object):
inputs.append(line[3:])
positions.append(i)
positions.append(len(lines) + 1) # last position
- return zip(inputs, self._getoutputs(lines, positions), positions)
+ outputs = []
+ for i, start in enumerate(positions[:-1]):
+ end = positions[i + 1]
+ outputs.append('\n'.join(lines[start+1:end]))
+ return zip(inputs, outputs, positions)
def doctest(self, lineiter, verbose=False):
"""
@@ -635,9 +742,7 @@ class Interpreter(object):
raise task.etype, task.exc, task.tb
def execute(self, lineiter, verbose=False):
- """
- Execute a lineiter of commands in a context and print the output.
- """
+ "Execute a lineiter of commands in a context and print the output"
with self:
for line in lineiter:
if verbose:
@@ -645,19 +750,26 @@ class Interpreter(object):
task = self.send(line) # finished task
if task.etype: # there was an error
raise task.etype, task.exc, task.tb
- if not task.synchronous:
- write('%s\n' % task.str)
+ write('%s\n' % task.str)
+
+ def multiline(self, stdin=sys.stdin, terminator=';', verbose=False):
+ "The multiline mode is especially suited for usage with emacs"
+ with self:
+ for line in read_long_line(stdin, terminator):
+ task = self.submit(line)
+ task.run()
+ write('%s\n' % task.str)
+ if verbose and task.traceback:
+ write(task.traceback)
def interact(self, stdin=sys.stdin, prompt='i> ', verbose=False):
- """
- Starts an interactive command loop reading commands from the
- consolle. Using rlwrap is recommended.
- """
+ "Starts an interactive command loop reading commands from the consolle"
if stdin is sys.stdin and readline: # use readline
histfile = os.path.expanduser('~/.%s.history' % self.name)
- stdin = ReadlineInput(self.commands, prompt, histfile=histfile)
- self.stdin = stdin
- self.prompt = getattr(stdin, 'prompt', prompt)
+ self.stdin = ReadlineInput(self.commands, histfile=histfile)
+ else:
+ self.stdin = stdin
+ self.prompt = prompt
self.verbose = verbose
intro = self.obj.__doc__ or ''
write(intro + '\n')
@@ -665,19 +777,32 @@ class Interpreter(object):
if self.stdin is sys.stdin: # do not close stdin automatically
self._manage_input()
else:
- with self.stdin:
+ with self.stdin: # close stdin automatically
self._manage_input()
def _manage_input(self):
- while True: # using 'for' would not work well with unbuffered mode
- if not isinstance(self.stdin, ReadlineInput):
- write(self.prompt) # else the prompt is already there
- line = self.stdin.readline() # including \n
+ "Convert input lines into task which are then executed"
+ for line in iter(lambda : read_line(self.stdin, self.prompt), ''):
+ line = line.strip()
if not line:
- break
- task = self.make_task(line)
- self.tm.run_task(task)
- if self.verbose and task.synchronous and task.etype:
+ continue
+ task = self.submit(line)
+ task.run() # synchronous or not
+ write(str(task) + '\n')
+ if self.verbose and task.etype:
write(task.traceback)
- if task.etype or not task.synchronous:
- write(str(task) + '\n')
+
+ def start_server(self, port=2199, **kw):
+ """Starts an asyncore server reading commands for clients and opening
+ a new interpreter for each connection."""
+ _AsynServer(self, _AsynHandler, port) # register the server
+ try:
+ asyncore.loop(**kw)
+ except KeyboardInterrupt:
+ pass
+ finally:
+ asyncore.close_all()
+
+ def stop_server(self, wait=0.0):
+ "Stops the asyncore server, possibly after a given number of seconds"
+ threading.Timer(wait, asyncore.socket_map.clear).start()
diff --git a/plac/plac_runner.py b/plac/plac_runner.py
index c9b7cef..57cd85b 100644
--- a/plac/plac_runner.py
+++ b/plac/plac_runner.py
@@ -21,12 +21,15 @@ def run(fnames, cmd, verbose):
@plac.annotations(
verbose=('verbose mode', 'flag', 'v'),
interactive=('run plac tool in interactive mode', 'flag', 'i'),
+ multiline=('run plac tool in multiline mode', 'flag', 'm'),
+ server=('run plac server', 'flag', 's'),
batch=('run plac batch files', 'flag', 'b'),
test=('run plac test files', 'flag', 't'),
fname='script to run (.py or .plac or .placet)',
extra='additional arguments',
)
-def main(verbose, interactive, batch, test, fname=None, *extra):
+def main(verbose, interactive, multiline, server, batch, test, fname=None,
+ *extra):
"Runner for plac tools, plac batch files and plac tests"
baseparser = plac.parser_from(main)
if fname is None:
@@ -40,11 +43,17 @@ def main(verbose, interactive, batch, test, fname=None, *extra):
print(output)
else:
print(out)
- elif interactive:
+ elif interactive or multiline or server:
plactool = plac.import_main(fname, *extra, **{'prog': ''})
if inspect.isclass(plactool): # special case
plactool = plactool()
- plac.Interpreter(plactool).interact(verbose=verbose)
+ i = plac.Interpreter(plactool)
+ if interactive:
+ i.interact(verbose=verbose)
+ elif multiline:
+ i.multiline(verbose=verbose)
+ elif server:
+ i.serve()
elif batch:
run((fname,) + extra, 'execute', verbose)
elif test: