diff options
author | Michele Simionato <michele.simionato@gmail.com> | 2010-08-02 07:21:07 +0200 |
---|---|---|
committer | Michele Simionato <michele.simionato@gmail.com> | 2010-08-02 07:21:07 +0200 |
commit | de277c015f8a816dba98963b3ffc6cb19c69319d (patch) | |
tree | 73b5959b56f7c33dff4e5aa29b116bac3c6c9c82 | |
parent | 39670ca6b766a1c576c68f5a80729b6fde4d0e06 (diff) | |
parent | c90ae5e83fbf0d47c87de6267e04e0d3435bca66 (diff) | |
download | micheles-de277c015f8a816dba98963b3ffc6cb19c69319d.tar.gz |
Merged from main branch
-rw-r--r-- | plac/CHANGES.txt | 9 | ||||
-rw-r--r-- | plac/Makefile | 4 | ||||
-rw-r--r-- | plac/doc/importer_ui.py | 29 | ||||
-rw-r--r-- | plac/doc/ishelve2.plac | 2 | ||||
-rw-r--r-- | plac/doc/ishelve2.placet | 2 | ||||
-rw-r--r-- | plac/doc/ishelve2.py | 5 | ||||
-rw-r--r-- | plac/doc/picalculator.py | 63 | ||||
-rw-r--r-- | plac/doc/plac.el | 77 | ||||
-rw-r--r-- | plac/doc/plac.html | 176 | ||||
-rw-r--r-- | plac/doc/plac.pdf | 230 | ||||
-rw-r--r-- | plac/doc/plac.txt | 2 | ||||
-rw-r--r-- | plac/doc/plac_adv.html | 435 | ||||
-rw-r--r-- | plac/doc/plac_adv.pdf | 3828 | ||||
-rw-r--r-- | plac/doc/plac_adv.txt | 223 | ||||
-rw-r--r-- | plac/doc/server_ex.py | 9 | ||||
-rw-r--r-- | plac/doc/sql_interface.py | 5 | ||||
-rw-r--r-- | plac/doc/test_pi.py | 11 | ||||
-rw-r--r-- | plac/doc/test_server.py | 35 | ||||
-rw-r--r-- | plac/plac.py | 4 | ||||
-rw-r--r-- | plac/plac_core.py | 10 | ||||
-rw-r--r-- | plac/plac_ext.py | 333 | ||||
-rw-r--r-- | plac/plac_runner.py | 15 |
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@gmail.com">michele.simionato@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">("SQL</span> <span class="pre">query",</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">("SQL query", '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="select * from table" 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="select" 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 "more features" 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"> @plac.annotations( dsn="Database dsn", @@ -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 >= 4 and I will use the <tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator. Notice however +X >= 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">"flag"</span></tt>, <tt class="docutils literal"><span class="pre">"option"</span></tt>, <tt class="docutils literal"><span class="pre">"positional"</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">"flag"</tt>, <tt class="docutils literal">"option"</tt>, <tt class="docutils literal">"positional"</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"> >>> import plac, example10 >>> 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"> >>> def main(n): ... for i in range(int(n)): @@ -1014,12 +1014,12 @@ example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', >>> 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 "listify" 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">"name=value"</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">"name=value"</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 "/" 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"> >>> import plac >>> def main(arg): @@ -1371,17 +1371,17 @@ invoking the <tt class="docutils literal"><span class="pre">plac.parser_from</sp >>> 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@gmail.com">michele.simionato@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> ', - 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> ') </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> del deleting everything i> 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 + '("#") ; 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) + +</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> .output 1 <ThreadedTask 1 [import_file file1] FINISHED> </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> 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): + "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) + +</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 "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 <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 < 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 < 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): + """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() + +</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> 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. +</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: "pyreadline".</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: |