summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2010-07-10 08:33:55 +0200
committerMichele Simionato <michele.simionato@gmail.com>2010-07-10 08:33:55 +0200
commit5e4bdc93b22e41ba39e871d30f646a34ac7475e0 (patch)
tree82d34c1c18a07f803147c82c4424485e79896d40
parentecf1d3f604d3b68e318924560b76b9dae7d41f16 (diff)
downloadmicheles-5e4bdc93b22e41ba39e871d30f646a34ac7475e0.tar.gz
A lot of work for plac 0.6
-rw-r--r--plac/CHANGES.txt3
-rw-r--r--plac/Makefile4
-rw-r--r--plac/doc/example13.help1
-rw-r--r--plac/doc/example13.py2
-rw-r--r--plac/doc/example14.help8
-rw-r--r--plac/doc/example14.py15
-rw-r--r--plac/doc/importer1.py20
-rw-r--r--plac/doc/importer2.py22
-rw-r--r--plac/doc/importer3.py20
-rw-r--r--plac/doc/ishelve.placet2
-rw-r--r--plac/doc/ishelve.py2
-rw-r--r--plac/doc/ishelve2.help8
-rw-r--r--plac/doc/ishelve2.placet4
-rw-r--r--plac/doc/plac.html14
-rw-r--r--plac/doc/plac.pdf997
-rw-r--r--plac/doc/plac.txt16
-rw-r--r--plac/doc/plac_adv.html547
-rw-r--r--plac/doc/plac_adv.pdf3606
-rw-r--r--plac/doc/plac_adv.txt461
-rw-r--r--plac/doc/shelve_interpreter.help5
-rw-r--r--plac/doc/sql_interface.py25
-rw-r--r--plac/doc/test_plac.py29
-rw-r--r--plac/plac.py4
-rw-r--r--plac/plac_core.py134
-rw-r--r--plac/plac_ext.py674
25 files changed, 4185 insertions, 2438 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt
index fc42bf1..dfb8c23 100644
--- a/plac/CHANGES.txt
+++ b/plac/CHANGES.txt
@@ -1,6 +1,9 @@
HISTORY
----------
+0.6.0 Improvement the interactive experience with full readline support and
+ custom help. Added support for long running command, via threads and
+ processes.
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.
diff --git a/plac/Makefile b/plac/Makefile
index 52bd70c..7e119b6 100644
--- a/plac/Makefile
+++ b/plac/Makefile
@@ -6,3 +6,7 @@ doc/plac_adv.pdf: doc/plac_adv.txt
cd doc; rst2pdf --footer=###Page### plac_adv.txt; rst2html --stylesheet=$(HOME)/gcode/df.css plac_adv.txt plac_adv.html
upload:
python3 setup.py register sdist upload
+2:
+ python setup.py build; sudo python setup.py install
+3:
+ python3 setup.py build; sudo python3 setup.py install
diff --git a/plac/doc/example13.help b/plac/doc/example13.help
index 91216a3..94401ad 100644
--- a/plac/doc/example13.help
+++ b/plac/doc/example13.help
@@ -7,4 +7,3 @@ optional arguments:
subcommands:
{status,commit,checkout,help}
- -h to get additional help
diff --git a/plac/doc/example13.py b/plac/doc/example13.py
index 3c91f07..715f2e1 100644
--- a/plac/doc/example13.py
+++ b/plac/doc/example13.py
@@ -7,7 +7,7 @@ class FVCS(object):
@plac.annotations(
name=('a recognized command', 'positional', None, str, commands))
def help(self, name):
- self.p.subp[name].print_help()
+ print(plac.parser_from(self).help_cmd(name))
@plac.annotations(
url=('url of the source code', 'positional'))
diff --git a/plac/doc/example14.help b/plac/doc/example14.help
deleted file mode 100644
index 50c600b..0000000
--- a/plac/doc/example14.help
+++ /dev/null
@@ -1,8 +0,0 @@
-usage: example14.py [-h] {status,commit,checkout,help} ...
-
-optional arguments:
- -h, --help show this help message and exit
-
-subcommands:
- {status,commit,checkout,help}
- -h to get additional help
diff --git a/plac/doc/example14.py b/plac/doc/example14.py
deleted file mode 100644
index 18a2931..0000000
--- a/plac/doc/example14.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import plac
-from example13 import FVCS
-
-class VCS_with_help(FVCS):
- commands = FVCS.commands + ('help',)
-
- @plac.annotations(
- name=('a recognized command', 'positional', None, str, commands))
- def help(self, name):
- self.p.subp[name].print_help()
-
-main = VCS_with_help()
-
-if __name__ == '__main__':
- plac.call(main)
diff --git a/plac/doc/importer1.py b/plac/doc/importer1.py
new file mode 100644
index 0000000..43e6833
--- /dev/null
+++ b/plac/doc/importer1.py
@@ -0,0 +1,20 @@
+import time
+import plac
+
+class FakeImporter(object):
+ "A fake importer with an import_file command"
+ commands = ['import_file']
+ def __init__(self, dsn):
+ self.dsn = dsn
+ def import_file(self, fname):
+ "Import a file into the database"
+ try:
+ for n in range(10000):
+ time.sleep(.01)
+ if n % 100 == 99:
+ yield 'Imported %d lines' % (n+1)
+ finally:
+ print('closing the file')
+
+if __name__ == '__main__':
+ plac.Interpreter(plac.call(FakeImporter)).interact()
diff --git a/plac/doc/importer2.py b/plac/doc/importer2.py
new file mode 100644
index 0000000..b448aa6
--- /dev/null
+++ b/plac/doc/importer2.py
@@ -0,0 +1,22 @@
+import time
+import plac
+
+class FakeImporter(object):
+ "A fake importer with an import_file command"
+ thcommands = ['import_file']
+ def __init__(self, dsn):
+ self.dsn = dsn
+ def import_file(self, fname):
+ "Import a file into the database"
+ try:
+ for n in range(10000):
+ time.sleep(.02)
+ if n % 100 == 99: # every two seconds
+ yield 'Imported %d lines' % (n+1)
+ if n % 10 == 9: # every 0.2 seconds
+ yield # go back and check the TOBEKILLED status
+ finally:
+ print('closing the file')
+
+if __name__ == '__main__':
+ plac.Interpreter(plac.call(FakeImporter)).interact()
diff --git a/plac/doc/importer3.py b/plac/doc/importer3.py
new file mode 100644
index 0000000..fb3aee5
--- /dev/null
+++ b/plac/doc/importer3.py
@@ -0,0 +1,20 @@
+import time
+import plac
+
+class FakeImporter(object):
+ "A fake importer with an import_file command"
+ mpcommands = ['import_file']
+ def __init__(self, dsn):
+ self.dsn = dsn
+ def import_file(self, fname):
+ "Import a file into the database"
+ try:
+ for n in range(10000):
+ time.sleep(.02)
+ if n % 100 == 99:
+ yield 'Imported %d lines' % (n+1)
+ finally:
+ print('closing the file')
+
+if __name__ == '__main__':
+ plac.Interpreter(plac.call(FakeImporter)).interact()
diff --git a/plac/doc/ishelve.placet b/plac/doc/ishelve.placet
index 87c1b32..2637223 100644
--- a/plac/doc/ishelve.placet
+++ b/plac/doc/ishelve.placet
@@ -10,4 +10,4 @@ deleted a
i> a
a: not found
i> .cler # spelling error
-SystemExit: unrecognized arguments: .cler
+.cler: not found
diff --git a/plac/doc/ishelve.py b/plac/doc/ishelve.py
index 343b4ed..4f22ba2 100644
--- a/plac/doc/ishelve.py
+++ b/plac/doc/ishelve.py
@@ -13,7 +13,7 @@ DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
setters='setters param=value')
def main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
*params, **setters):
- "A simple interface to a shelve"
+ "A simple interface to a shelve. Use .help to see the available commands."
sh = shelve.open(filename)
try:
if not any([help, showall, clear, delete, params, setters]):
diff --git a/plac/doc/ishelve2.help b/plac/doc/ishelve2.help
new file mode 100644
index 0000000..2ce64db
--- /dev/null
+++ b/plac/doc/ishelve2.help
@@ -0,0 +1,8 @@
+usage: ishelve2.py [-h] [-configfile ~/conf.shelve]
+
+A minimal interface over a shelve object.
+
+optional arguments:
+ -h, --help show this help message and exit
+ -configfile ~/conf.shelve
+ path name of the shelve
diff --git a/plac/doc/ishelve2.placet b/plac/doc/ishelve2.placet
index a41fc8d..f954932 100644
--- a/plac/doc/ishelve2.placet
+++ b/plac/doc/ishelve2.placet
@@ -5,5 +5,5 @@ i> set a 1
setting a=1
i> set b 2
setting b=2
-i> show b
-b = 2
+i> show a
+a = 1
diff --git a/plac/doc/plac.html b/plac/doc/plac.html
index da6cbad..c63e131 100644
--- a/plac/doc/plac.html
+++ b/plac/doc/plac.html
@@ -1027,12 +1027,18 @@ try:
except:
# do something
</pre>
-<p>Without the <tt class="docutils literal">listify</tt> functionality, a main function returning a
+<p>Without the &quot;listify&quot; functionality, a main function returning a
generator object would not raise any exception until the generator
is iterated over.</p>
-<p>Moreover you can rely on type checks like <tt class="docutils literal">isinstance(result, list)</tt>
-for the output of <tt class="docutils literal">plac.call</tt>, to check if the output is an iterable
-or not.</p>
+<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">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>
<div class="section" id="a-realistic-example">
<h1><a class="toc-backref" href="#id8">A realistic example</a></h1>
diff --git a/plac/doc/plac.pdf b/plac/doc/plac.pdf
index 5e15047..836a8d6 100644
--- a/plac/doc/plac.pdf
+++ b/plac/doc/plac.pdf
@@ -351,7 +351,7 @@ endobj
/Dest [ 94 0 R
/XYZ
62.69291
- 765.0236
+ 683.8236
0 ]
/Rect [ 62.69291
422.5936
@@ -369,7 +369,7 @@ endobj
/Dest [ 94 0 R
/XYZ
62.69291
- 765.0236
+ 683.8236
0 ]
/Rect [ 521.4627
422.5936
@@ -387,7 +387,7 @@ endobj
/Dest [ 97 0 R
/XYZ
62.69291
- 707.8236
+ 623.8236
0 ]
/Rect [ 62.69291
404.5936
@@ -405,7 +405,7 @@ endobj
/Dest [ 97 0 R
/XYZ
62.69291
- 707.8236
+ 623.8236
0 ]
/Rect [ 521.4627
404.5936
@@ -423,7 +423,7 @@ endobj
/Dest [ 99 0 R
/XYZ
62.69291
- 683.8236
+ 594.6236
0 ]
/Rect [ 62.69291
386.5936
@@ -441,7 +441,7 @@ endobj
/Dest [ 99 0 R
/XYZ
62.69291
- 683.8236
+ 594.6236
0 ]
/Rect [ 521.4627
386.5936
@@ -456,10 +456,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 125 0 R
+ /Dest [ 124 0 R
/XYZ
62.69291
- 595.8236
+ 511.8236
0 ]
/Rect [ 62.69291
368.5936
@@ -474,10 +474,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 125 0 R
+ /Dest [ 124 0 R
/XYZ
62.69291
- 595.8236
+ 511.8236
0 ]
/Rect [ 521.4627
368.5936
@@ -492,10 +492,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 140 0 R
+ /Dest [ 133 0 R
/XYZ
62.69291
- 259.4236
+ 161.4236
0 ]
/Rect [ 62.69291
350.5936
@@ -510,10 +510,10 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 140 0 R
+ /Dest [ 133 0 R
/XYZ
62.69291
- 259.4236
+ 161.4236
0 ]
/Rect [ 521.4627
350.5936
@@ -531,7 +531,7 @@ endobj
/Dest [ 162 0 R
/XYZ
62.69291
- 741.0236
+ 645.0236
0 ]
/Rect [ 62.69291
332.5936
@@ -549,7 +549,7 @@ endobj
/Dest [ 162 0 R
/XYZ
62.69291
- 741.0236
+ 645.0236
0 ]
/Rect [ 521.4627
332.5936
@@ -567,7 +567,7 @@ endobj
/Dest [ 162 0 R
/XYZ
62.69291
- 534.0236
+ 438.0236
0 ]
/Rect [ 62.69291
314.5936
@@ -585,7 +585,7 @@ endobj
/Dest [ 162 0 R
/XYZ
62.69291
- 534.0236
+ 438.0236
0 ]
/Rect [ 521.4627
314.5936
@@ -1521,9 +1521,9 @@ endobj
0
0 ]
/Rect [ 338.1568
- 729.5936
+ 648.3936
360.5113
- 741.5936 ]
+ 660.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1536,9 +1536,9 @@ endobj
0
0 ]
/Rect [ 110.6843
- 717.5936
+ 636.3936
169.0343
- 729.5936 ]
+ 648.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1551,9 +1551,9 @@ endobj
0
0 ]
/Rect [ 168.3029
- 705.5936
+ 624.3936
208.8829
- 717.5936 ]
+ 636.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1588,9 +1588,9 @@ endobj
0
0 ]
/Rect [ 185.0709
- 672.3936
+ 588.3936
208.0228
- 684.3936 ]
+ 600.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1603,9 +1603,9 @@ endobj
0
0 ]
/Rect [ 220.5998
- 660.3936
+ 576.3936
243.819
- 672.3936 ]
+ 588.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1639,9 +1639,9 @@ endobj
0
0 ]
/Rect [ 374.4929
- 624.3936
+ 535.1936
395.6129
- 636.3936 ]
+ 547.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1674,9 +1674,9 @@ endobj
0
0 ]
/Rect [ 304.0655
- 570.3936
+ 486.3936
348.3808
- 582.3936 ]
+ 498.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1689,9 +1689,9 @@ endobj
0
0 ]
/Rect [ 293.7749
- 396.3936
+ 312.3936
316.2402
- 408.3936 ]
+ 324.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1725,9 +1725,9 @@ endobj
0
0 ]
/Rect [ 62.69291
- 560.3936
+ 476.3936
84.8789
- 572.3936 ]
+ 488.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1740,9 +1740,9 @@ endobj
0
0 ]
/Rect [ 466.5307
- 560.3936
+ 476.3936
509.8367
- 572.3936 ]
+ 488.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1755,9 +1755,9 @@ endobj
0
0 ]
/Rect [ 124.3929
- 536.3936
+ 452.3936
163.8529
- 548.3936 ]
+ 464.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1770,9 +1770,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 455.3936
+ 371.3936
127.9329
- 467.3936 ]
+ 383.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1785,9 +1785,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 437.3936
+ 353.3936
107.9337
- 449.3936 ]
+ 365.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1800,9 +1800,9 @@ endobj
0
0 ]
/Rect [ 308.5389
- 437.3936
+ 353.3936
351.8997
- 449.3936 ]
+ 365.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1815,9 +1815,9 @@ endobj
0
0 ]
/Rect [ 380.6856
- 413.3936
+ 329.3936
423.7999
- 425.3936 ]
+ 341.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1830,9 +1830,9 @@ endobj
0
0 ]
/Rect [ 494.4684
- 413.3936
+ 329.3936
516.4627
- 425.3936 ]
+ 341.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1845,9 +1845,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 383.3936
+ 299.3936
108.3529
- 395.3936 ]
+ 311.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1860,9 +1860,9 @@ endobj
0
0 ]
/Rect [ 277.2428
- 383.3936
+ 299.3936
321.0228
- 395.3936 ]
+ 311.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1875,9 +1875,9 @@ endobj
0
0 ]
/Rect [ 404.5839
- 371.3936
+ 287.3936
426.0657
- 383.3936 ]
+ 299.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1890,9 +1890,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 317.3936
+ 233.3936
108.61
- 329.3936 ]
+ 245.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1905,9 +1905,9 @@ endobj
0
0 ]
/Rect [ 459.2622
- 305.3936
+ 221.3936
481.289
- 317.3936 ]
+ 233.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1920,9 +1920,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 275.3936
+ 191.3936
108.9242
- 287.3936 ]
+ 203.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1935,9 +1935,9 @@ endobj
0
0 ]
/Rect [ 340.9248
- 275.3936
+ 191.3936
470.1087
- 287.3936 ]
+ 203.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1950,9 +1950,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 245.3936
+ 161.3936
107.9247
- 257.3936 ]
+ 173.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1965,9 +1965,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 233.3936
+ 149.3936
104.0329
- 245.3936 ]
+ 161.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1980,9 +1980,9 @@ endobj
0
0 ]
/Rect [ 489.2227
- 233.3936
+ 149.3936
532.176
- 245.3936 ]
+ 161.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1995,9 +1995,9 @@ endobj
0
0 ]
/Rect [ 85.69291
- 221.3936
+ 137.3936
159.6229
- 233.3936 ]
+ 149.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2010,9 +2010,9 @@ endobj
0
0 ]
/Rect [ 62.69291
- 200.3936
+ 116.3936
83.81291
- 212.3936 ]
+ 128.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2025,29 +2025,14 @@ endobj
0
0 ]
/Rect [ 219.4229
- 200.3936
+ 116.3936
261.6629
- 212.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER104': class PDFDictionary
-124 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 455.2227
- 170.3936
- 534.3667
- 182.3936 ]
+ 128.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page15': class PDFPage
-125 0 obj
+124 0 obj
% Page dictionary
<< /Annots [ 103 0 R
104 0 R
@@ -2069,8 +2054,7 @@ endobj
120 0 R
121 0 R
122 0 R
- 123 0 R
- 124 0 R ]
+ 123 0 R ]
/Contents 195 0 R
/MediaBox [ 0
0
@@ -2087,6 +2071,21 @@ endobj
/Trans << >>
/Type /Page >>
endobj
+% 'Annot.NUMBER104': class PDFDictionary
+125 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 455.2227
+ 744.5936
+ 534.3667
+ 756.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
% 'Annot.NUMBER105': class PDFDictionary
126 0 obj
<< /A << /S /URI
@@ -2096,9 +2095,9 @@ endobj
0
0 ]
/Rect [ 325.7268
- 675.3936
+ 577.3936
347.4138
- 687.3936 ]
+ 589.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2111,9 +2110,9 @@ endobj
0
0 ]
/Rect [ 327.2261
- 508.1936
+ 410.1936
410.5152
- 520.1936 ]
+ 422.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2126,9 +2125,9 @@ endobj
0
0 ]
/Rect [ 275.5829
- 328.9936
+ 230.9936
317.8229
- 340.9936 ]
+ 242.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2141,9 +2140,9 @@ endobj
0
0 ]
/Rect [ 307.9178
- 310.9936
+ 212.9936
351.5999
- 322.9936 ]
+ 224.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2156,9 +2155,9 @@ endobj
0
0 ]
/Rect [ 329.8034
- 286.9936
+ 188.9936
352.1804
- 298.9936 ]
+ 200.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2171,9 +2170,9 @@ endobj
0
0 ]
/Rect [ 109.0098
- 223.9936
+ 125.9936
131.9967
- 235.9936 ]
+ 137.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2186,14 +2185,41 @@ endobj
0
0 ]
/Rect [ 397.2929
- 199.9936
+ 101.9936
415.6329
- 211.9936 ]
+ 113.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER112': class PDFDictionary
+% 'Page16': class PDFPage
133 0 obj
+% Page dictionary
+<< /Annots [ 125 0 R
+ 126 0 R
+ 127 0 R
+ 128 0 R
+ 129 0 R
+ 130 0 R
+ 131 0 R
+ 132 0 R ]
+ /Contents 196 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 180 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER112': class PDFDictionary
+134 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/opterator) >>
@@ -2201,14 +2227,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 178.9936
+ 753.5936
128.4929
- 190.9936 ]
+ 765.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER113': class PDFDictionary
-134 0 obj
+135 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/CLIArgs) >>
@@ -2216,14 +2242,14 @@ endobj
0
0 ]
/Rect [ 85.69291
- 160.9936
+ 735.5936
124.5929
- 172.9936 ]
+ 747.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER114': class PDFDictionary
-135 0 obj
+136 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2231,14 +2257,14 @@ endobj
0
0 ]
/Rect [ 464.3898
- 139.9936
+ 714.5936
503.8498
- 151.9936 ]
+ 726.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER115': class PDFDictionary
-136 0 obj
+137 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2246,14 +2272,14 @@ endobj
0
0 ]
/Rect [ 305.0429
- 127.9936
+ 702.5936
323.3829
- 139.9936 ]
+ 714.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER116': class PDFDictionary
-137 0 obj
+138 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2261,14 +2287,14 @@ endobj
0
0 ]
/Rect [ 455.0104
- 109.9936
+ 684.5936
479.9015
- 121.9936 ]
+ 696.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER117': class PDFDictionary
-138 0 obj
+139 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -2276,14 +2302,14 @@ endobj
0
0 ]
/Rect [ 303.707
- 97.99362
+ 672.5936
322.047
- 109.9936 ]
+ 684.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER118': class PDFDictionary
-139 0 obj
+140 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2291,45 +2317,12 @@ endobj
0
0 ]
/Rect [ 328.8186
- 97.99362
+ 672.5936
353.3701
- 109.9936 ]
+ 684.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page16': class PDFPage
-140 0 obj
-% Page dictionary
-<< /Annots [ 126 0 R
- 127 0 R
- 128 0 R
- 129 0 R
- 130 0 R
- 131 0 R
- 132 0 R
- 133 0 R
- 134 0 R
- 135 0 R
- 136 0 R
- 137 0 R
- 138 0 R
- 139 0 R ]
- /Contents 196 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 180 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
% 'Annot.NUMBER119': class PDFDictionary
141 0 obj
<< /A << /S /URI
@@ -2339,9 +2332,9 @@ endobj
0
0 ]
/Rect [ 62.69291
- 756.5936
+ 660.5936
81.03291
- 768.5936 ]
+ 672.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2354,9 +2347,9 @@ endobj
0
0 ]
/Rect [ 156.6051
- 705.5936
+ 609.5936
177.8606
- 717.5936 ]
+ 621.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2369,9 +2362,9 @@ endobj
0
0 ]
/Rect [ 186.6535
- 681.5936
+ 585.5936
226.1135
- 693.5936 ]
+ 597.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2384,9 +2377,9 @@ endobj
0
0 ]
/Rect [ 493.1227
- 681.5936
+ 585.5936
532.4646
- 693.5936 ]
+ 597.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2399,9 +2392,9 @@ endobj
0
0 ]
/Rect [ 72.7804
- 669.5936
+ 573.5936
96.20788
- 681.5936 ]
+ 585.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2414,9 +2407,9 @@ endobj
0
0 ]
/Rect [ 149.2229
- 639.5936
+ 543.5936
171.2704
- 651.5936 ]
+ 555.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2429,9 +2422,9 @@ endobj
0
0 ]
/Rect [ 128.0309
- 597.5936
+ 501.5936
149.4369
- 609.5936 ]
+ 513.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2444,9 +2437,9 @@ endobj
0
0 ]
/Rect [ 502.8367
- 597.5936
+ 501.5936
524.2427
- 609.5936 ]
+ 513.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2459,9 +2452,9 @@ endobj
0
0 ]
/Rect [ 187.4797
- 573.5936
+ 477.5936
209.0991
- 585.5936 ]
+ 489.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2474,9 +2467,9 @@ endobj
0
0 ]
/Rect [ 301.6965
- 573.5936
+ 477.5936
426.0446
- 585.5936 ]
+ 489.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2489,9 +2482,9 @@ endobj
0
0 ]
/Rect [ 83.64556
- 498.5936
+ 402.5936
105.7082
- 510.5936 ]
+ 414.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2504,9 +2497,9 @@ endobj
0
0 ]
/Rect [ 446.6
- 498.5936
+ 402.5936
502.5727
- 510.5936 ]
+ 414.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2519,9 +2512,9 @@ endobj
0
0 ]
/Rect [ 275.6828
- 486.5936
+ 390.5936
297.3688
- 498.5936 ]
+ 402.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2534,9 +2527,9 @@ endobj
0
0 ]
/Rect [ 77.19665
- 474.5936
+ 378.5936
139.4904
- 486.5936 ]
+ 390.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2549,9 +2542,9 @@ endobj
0
0 ]
/Rect [ 96.54131
- 462.5936
+ 366.5936
139.0255
- 474.5936 ]
+ 378.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2564,9 +2557,9 @@ endobj
0
0 ]
/Rect [ 203.5016
- 429.5936
+ 333.5936
245.8453
- 441.5936 ]
+ 345.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2579,9 +2572,9 @@ endobj
0
0 ]
/Rect [ 62.69291
- 354.5936
+ 258.5936
138.7898
- 366.5936 ]
+ 270.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2594,9 +2587,9 @@ endobj
0
0 ]
/Rect [ 114.6649
- 342.5936
+ 246.5936
154.1249
- 354.5936 ]
+ 258.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2609,9 +2602,9 @@ endobj
0
0 ]
/Rect [ 191.6329
- 330.5936
+ 234.5936
233.8729
- 342.5936 ]
+ 246.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2624,9 +2617,9 @@ endobj
0
0 ]
/Rect [ 263.3429
- 300.5936
+ 204.5936
286.6829
- 312.5936 ]
+ 216.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -2639,16 +2632,23 @@ endobj
0
0 ]
/Rect [ 258.5629
- 252.5936
+ 156.5936
276.9029
- 264.5936 ]
+ 168.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page17': class PDFPage
162 0 obj
% Page dictionary
-<< /Annots [ 141 0 R
+<< /Annots [ 134 0 R
+ 135 0 R
+ 136 0 R
+ 137 0 R
+ 138 0 R
+ 139 0 R
+ 140 0 R
+ 141 0 R
142 0 R
143 0 R
144 0 R
@@ -2697,7 +2697,7 @@ endobj
% 'R164': class PDFInfo
164 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100620102407-01'00')
+ /CreationDate (D:20100703163715-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
@@ -2798,7 +2798,7 @@ endobj
<< /Dest [ 94 0 R
/XYZ
62.69291
- 765.0236
+ 683.8236
0 ]
/Next 174 0 R
/Parent 165 0 R
@@ -2810,7 +2810,7 @@ endobj
<< /Dest [ 97 0 R
/XYZ
62.69291
- 707.8236
+ 623.8236
0 ]
/Next 175 0 R
/Parent 165 0 R
@@ -2822,7 +2822,7 @@ endobj
<< /Dest [ 99 0 R
/XYZ
62.69291
- 683.8236
+ 594.6236
0 ]
/Next 176 0 R
/Parent 165 0 R
@@ -2831,10 +2831,10 @@ endobj
endobj
% 'Outline.10': class OutlineEntryObject
176 0 obj
-<< /Dest [ 125 0 R
+<< /Dest [ 124 0 R
/XYZ
62.69291
- 595.8236
+ 511.8236
0 ]
/Next 177 0 R
/Parent 165 0 R
@@ -2843,10 +2843,10 @@ endobj
endobj
% 'Outline.11': class OutlineEntryObject
177 0 obj
-<< /Dest [ 140 0 R
+<< /Dest [ 133 0 R
/XYZ
62.69291
- 259.4236
+ 161.4236
0 ]
/Next 178 0 R
/Parent 165 0 R
@@ -2858,7 +2858,7 @@ endobj
<< /Dest [ 162 0 R
/XYZ
62.69291
- 741.0236
+ 645.0236
0 ]
/Next 179 0 R
/Parent 165 0 R
@@ -2870,7 +2870,7 @@ endobj
<< /Dest [ 162 0 R
/XYZ
62.69291
- 534.0236
+ 438.0236
0 ]
/Parent 165 0 R
/Prev 178 0 R
@@ -2894,8 +2894,8 @@ endobj
97 0 R
99 0 R
102 0 R
- 125 0 R
- 140 0 R
+ 124 0 R
+ 133 0 R
162 0 R ]
/Type /Pages >>
endobj
@@ -4474,7 +4474,7 @@ endobj
% 'R190': class PDFStream
190 0 obj
% page stream
-<< /Length 4567 >>
+<< /Length 4511 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -4613,13 +4613,14 @@ Q
q
1 0 0 1 62.69291 152.8849 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.986412 Tw 12 TL /F1 10 Tf 0 0 0 rg (Without the ) Tj /F4 10 Tf (listify ) Tj /F1 10 Tf (functionality, a main function returning a generator object would not raise any) Tj T* 0 Tw (exception until the generator is iterated over.) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 3.122126 Tw (Without the "listify" functionality, a main function returning a generator object would not raise any) Tj T* 0 Tw (exception until the generator is iterated over.) Tj T* ET
Q
Q
q
1 0 0 1 62.69291 122.8849 cm
q
-BT 1 0 0 1 0 16.82 Tm .094269 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover you can rely on type checks like ) Tj /F4 10 Tf (isinstance\(result, list\) ) Tj /F1 10 Tf (for the output of ) Tj /F4 10 Tf (plac.call) Tj /F1 10 Tf (,) Tj T* 0 Tw (to check if the output is an iterable or not.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .647262 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you are a fan of lazyness, you can still have it by setting the ) Tj /F4 10 Tf (eager ) Tj /F1 10 Tf (flag to ) Tj /F4 10 Tf (False) Tj /F1 10 Tf (, as in the following) Tj T* 0 Tw (example:) Tj T* ET
Q
Q
q
@@ -4636,23 +4637,50 @@ endobj
% 'R191': class PDFStream
191 0 obj
% page stream
-<< /Length 3839 >>
+<< /Length 4236 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 744.0236 cm
+1 0 0 1 62.69291 727.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 36 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (for line in plac.call\(main, args, eager=False\):) Tj T* ( print\(line\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 695.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 1.35528 Tw 12 TL /F1 10 Tf 0 0 0 rg (If ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (returns a generator object this example will print each line as soon as available, whereas the) Tj T* 0 Tw (default behaviour is to print all the lines together and the end of the computation.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 662.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A realistic example) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 702.0236 cm
+1 0 0 1 62.69291 620.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.234488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a more realistic script using most of the features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to run SQL queries on a database by) Tj T* 0 Tw .930697 Tw (relying on ) Tj 0 0 .501961 rg (SQLAlchemy) Tj 0 0 0 rg (. Notice the usage of the ) Tj /F4 10 Tf (type ) Tj /F1 10 Tf (feature to automagically convert a SQLAlchemy) Tj T* 0 Tw (connection string into a ) Tj 0 0 .501961 rg (SqlSoup ) Tj 0 0 0 rg (object:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 332.8236 cm
+1 0 0 1 62.69291 251.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4673,20 +4701,20 @@ Q
Q
Q
q
-1 0 0 1 62.69291 276.8236 cm
+1 0 0 1 62.69291 195.6236 cm
q
BT 1 0 0 1 0 40.82 Tm .049987 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can see the ) Tj /F5 10 Tf (yield-is-print ) Tj /F1 10 Tf (pattern here: instead of using ) Tj /F4 10 Tf (print ) Tj /F1 10 Tf (in the main function, I use ) Tj /F4 10 Tf (yield) Tj /F1 10 Tf (, and) Tj T* 0 Tw 3.55061 Tw (I perform the print in the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block. The advantage of the pattern is that tests invoking) Tj T* 0 Tw .52936 Tw /F4 10 Tf (plac.call ) Tj /F1 10 Tf (and checking the result become trivial: had I performed the printing in the main function, the) Tj T* 0 Tw (test would have involved an ugly hack like redirecting ) Tj /F4 10 Tf (sys.stdout ) Tj /F1 10 Tf (to a ) Tj /F4 10 Tf (StringIO ) Tj /F1 10 Tf (object.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 258.8236 cm
+1 0 0 1 62.69291 177.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 93.62362 cm
+1 0 0 1 62.69291 96.42362 cm
q
q
1 0 0 1 0 0 cm
@@ -4696,11 +4724,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: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]) Tj T* T* (A script to run queries and SQL scripts on a database) Tj T* T* (positional arguments:) Tj T* ( db Connection string) Tj T* ( scripts SQL scripts) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -H, --header Header) Tj T* ( -c SQL, --sqlcmd SQL SQL command) Tj T* ET
+BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL (usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]) Tj T* T* (A script to run queries and SQL scripts on a database) Tj T* T* (positional arguments:) Tj T* ET
Q
Q
Q
@@ -4720,11 +4748,11 @@ endobj
% 'R192': class PDFStream
192 0 obj
% page stream
-<< /Length 3848 >>
+<< /Length 3423 >>
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
@@ -4734,37 +4762,37 @@ 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 ( -d |, --delimiter | Column separator) Tj T* ET
+BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL ( db Connection string) Tj T* ( scripts SQL scripts) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -H, --header Header) Tj T* ( -c SQL, --sqlcmd SQL SQL command) Tj T* ( -d |, --delimiter | Column separator) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 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 (You can check for yourself that the script works.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 686.8236 cm
+1 0 0 1 62.69291 602.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Keyword arguments) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 644.8236 cm
+1 0 0 1 62.69291 560.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.831984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.4, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (supports keyword arguments. In practice that means that if your main) Tj T* 0 Tw 2.099213 Tw (function has keyword arguments, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (treats specially arguments of the form ) Tj /F4 10 Tf ("name=value" ) Tj /F1 10 Tf (in the) Tj T* 0 Tw (command line. Here is an example:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 407.6236 cm
+1 0 0 1 62.69291 323.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4785,14 +4813,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 387.6236 cm
+1 0 0 1 62.69291 303.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the generated usage message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 258.4236 cm
+1 0 0 1 62.69291 174.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -4813,14 +4841,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 238.4236 cm
+1 0 0 1 62.69291 154.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is how you call the script:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 169.2236 cm
+1 0 0 1 62.69291 97.22362 cm
q
q
1 0 0 1 0 0 cm
@@ -4830,24 +4858,35 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ($ python example12.py -o X a1 a2 name=value) Tj T* (opt=X) Tj T* (args=\('a1', 'a2'\)) Tj T* (kw={'name': 'value'}) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ python example12.py -o X a1 a2 name=value) Tj T* (opt=X) Tj T* (args=\('a1', 'a2'\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 137.2236 cm
+1 0 0 1 56.69291 56.69291 cm
q
-BT 1 0 0 1 0 16.82 Tm 2.133735 Tw 12 TL /F1 10 Tf 0 0 0 rg (When using keyword arguments, one must be careful to use names which are not alreay taken; for) Tj T* 0 Tw (instance in this examples the name ) Tj /F4 10 Tf (opt ) Tj /F1 10 Tf (is taken:) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R193': class PDFStream
+193 0 obj
+% page stream
+<< /Length 4047 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 92.02362 cm
+1 0 0 1 62.69291 739.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4857,35 +4896,24 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
+n -6 -6 468.6898 24 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL ($ python example12.py 1 2 kw1=1 kw2=2 opt=0) Tj T* (usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]) Tj T* ET
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (kw={'name': 'value'}) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 707.8236 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 16.82 Tm 2.133735 Tw 12 TL /F1 10 Tf 0 0 0 rg (When using keyword arguments, one must be careful to use names which are not alreay taken; for) Tj T* 0 Tw (instance in this examples the name ) Tj /F4 10 Tf (opt ) Tj /F1 10 Tf (is taken:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R193': class PDFStream
-193 0 obj
-% page stream
-<< /Length 3629 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 739.8236 cm
+1 0 0 1 62.69291 650.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4895,37 +4923,37 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (example12.py: error: colliding keyword arguments: opt) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ python example12.py 1 2 kw1=1 kw2=2 opt=0) Tj T* (usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]) Tj T* (example12.py: error: colliding keyword arguments: opt) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
+1 0 0 1 62.69291 606.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 28.82 Tm /F1 10 Tf 12 TL 1.024104 Tw (The names taken are the names of the flags, of the options, and of the positional arguments, excepted) Tj T* 0 Tw .60561 Tw (varargs and keywords. This limitation is a consequence of the way the argument names are managed in) Tj T* 0 Tw (function calls by the Python language.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 662.8236 cm
+1 0 0 1 62.69291 573.6236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Final example: a shelve interface) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 620.8236 cm
+1 0 0 1 62.69291 531.6236 cm
q
BT 1 0 0 1 0 28.82 Tm .603516 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a less trivial example for the keyword arguments feature. The use case is the following: suppose) Tj T* 0 Tw .82881 Tw (we have stored the configuration parameters of a given application into a Python shelve and we need a) Tj T* 0 Tw (command-line tool to edit the shelve. A possible implementation using ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (could be the following:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 120.0682 cm
+1 0 0 1 62.69291 110.8981 cm
q
q
.952737 0 0 .952737 0 0 cm
@@ -4935,10 +4963,10 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 492 516 re B*
+n -6 -6 492 432 re B*
Q
q
-BT 1 0 0 1 0 497.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# ishelve.py) Tj T* (import os, shelve, plac) Tj T* T* (DEFAULT_SHELVE = os.path.expanduser\('~/conf.shelve'\)) Tj T* T* (@plac.annotations\() Tj T* ( help=\('show help', 'flag'\),) Tj T* ( showall=\('show all parameters in the shelve', 'flag'\),) Tj T* ( clear=\('clear the shelve', 'flag'\),) Tj T* ( delete=\('delete an element', 'option'\),) Tj T* ( filename=\('filename of the shelve', 'option'\),) Tj T* ( params='names of the parameters in the shelve',) Tj T* ( setters='setters param=value'\)) Tj T* (def main\(help, showall, clear, delete, filename=DEFAULT_SHELVE,) Tj T* ( *params, **setters\):) Tj T* ( "A simple interface to a shelve") Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if not any\([help, showall, clear, delete, params, setters]\):) Tj T* ( yield 'no arguments passed, use .help to see the available commands') Tj T* ( elif help: # custom help) Tj T* ( yield 'Commands: .help, .showall, .clear, .delete') Tj T* ( yield ') Tj (<) Tj (param) Tj (>) Tj ( ...') Tj T* ( yield ') Tj (<) Tj (param=value) Tj (>) Tj ( ...') Tj T* ( elif showall:) Tj T* ( for param, name in sh.items\(\):) Tj T* ( yield '%s=%s' % \(param, name\)) Tj T* ( elif clear:) Tj T* ( sh.clear\(\)) Tj T* ( yield 'cleared the shelve') Tj T* ( elif delete:) Tj T* ( try:) Tj T* ( del sh[delete]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % delete) Tj T* ( else:) Tj T* ( yield 'deleted %s' % delete) Tj T* ( for param in params:) Tj T* ( try:) Tj T* ( yield sh[param]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % param ) Tj T* ET
+BT 1 0 0 1 0 413.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# ishelve.py) Tj T* (import os, shelve, plac) Tj T* T* (DEFAULT_SHELVE = os.path.expanduser\('~/conf.shelve'\)) Tj T* T* (@plac.annotations\() Tj T* ( help=\('show help', 'flag'\),) Tj T* ( showall=\('show all parameters in the shelve', 'flag'\),) Tj T* ( clear=\('clear the shelve', 'flag'\),) Tj T* ( delete=\('delete an element', 'option'\),) Tj T* ( filename=\('filename of the shelve', 'option'\),) Tj T* ( params='names of the parameters in the shelve',) Tj T* ( setters='setters param=value'\)) Tj T* (def main\(help, showall, clear, delete, filename=DEFAULT_SHELVE,) Tj T* ( *params, **setters\):) Tj T* ( "A simple interface to a shelve") Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if not any\([help, showall, clear, delete, params, setters]\):) Tj T* ( yield 'no arguments passed, use .help to see the available commands') Tj T* ( elif help: # custom help) Tj T* ( yield 'Commands: .help, .showall, .clear, .delete') Tj T* ( yield ') Tj (<) Tj (param) Tj (>) Tj ( ...') Tj T* ( yield ') Tj (<) Tj (param=value) Tj (>) Tj ( ...') Tj T* ( elif showall:) Tj T* ( for param, name in sh.items\(\):) Tj T* ( yield '%s=%s' % \(param, name\)) Tj T* ( elif clear:) Tj T* ( sh.clear\(\)) Tj T* ( yield 'cleared the shelve') Tj T* ( elif delete:) Tj T* ( try:) Tj T* ( del sh[delete]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % delete) Tj T* ET
Q
Q
Q
@@ -4958,11 +4986,11 @@ endobj
% 'R194': class PDFStream
194 0 obj
% page stream
-<< /Length 6972 >>
+<< /Length 6795 >>
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 523.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4972,30 +5000,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 240 re B*
Q
q
-BT 1 0 0 1 0 137.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( for param, value in setters.items\(\):) Tj T* ( sh[param] = value) Tj T* ( yield 'setting %s=%s' % \(param, value\)) Tj T* ( finally:) Tj T* ( sh.close\(\)) Tj T* T* (main.add_help = False # there is a custom help, remove the default one) Tj T* (main.prefix_chars = '.' # use dot-prefixed commands) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
+BT 1 0 0 1 0 221.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ( else:) Tj T* ( yield 'deleted %s' % delete) Tj T* ( for param in params:) Tj T* ( try:) Tj T* ( yield sh[param]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % param ) Tj T* ( for param, value in setters.items\(\):) Tj T* ( sh[param] = value) Tj T* ( yield 'setting %s=%s' % \(param, value\)) Tj T* ( finally:) Tj T* ( sh.close\(\)) Tj T* T* (main.add_help = False # there is a custom help, remove the default one) Tj T* (main.prefix_chars = '.' # use dot-prefixed commands) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 587.8236 cm
+1 0 0 1 62.69291 503.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (A few notes are in order:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 581.8236 cm
+1 0 0 1 62.69291 497.8236 cm
Q
q
-1 0 0 1 62.69291 581.8236 cm
+1 0 0 1 62.69291 497.8236 cm
Q
q
-1 0 0 1 62.69291 551.8236 cm
+1 0 0 1 62.69291 467.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5015,13 +5043,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 551.8236 cm
+1 0 0 1 62.69291 467.8236 cm
Q
q
-1 0 0 1 62.69291 551.8236 cm
+1 0 0 1 62.69291 467.8236 cm
Q
q
-1 0 0 1 62.69291 533.8236 cm
+1 0 0 1 62.69291 449.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5042,13 +5070,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 533.8236 cm
+1 0 0 1 62.69291 449.8236 cm
Q
q
-1 0 0 1 62.69291 533.8236 cm
+1 0 0 1 62.69291 449.8236 cm
Q
q
-1 0 0 1 62.69291 503.8236 cm
+1 0 0 1 62.69291 419.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5068,13 +5096,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 503.8236 cm
+1 0 0 1 62.69291 419.8236 cm
Q
q
-1 0 0 1 62.69291 503.8236 cm
+1 0 0 1 62.69291 419.8236 cm
Q
q
-1 0 0 1 62.69291 473.8236 cm
+1 0 0 1 62.69291 389.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5094,13 +5122,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 473.8236 cm
+1 0 0 1 62.69291 389.8236 cm
Q
q
-1 0 0 1 62.69291 473.8236 cm
+1 0 0 1 62.69291 389.8236 cm
Q
q
-1 0 0 1 62.69291 455.8236 cm
+1 0 0 1 62.69291 371.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5120,13 +5148,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 455.8236 cm
+1 0 0 1 62.69291 371.8236 cm
Q
q
-1 0 0 1 62.69291 455.8236 cm
+1 0 0 1 62.69291 371.8236 cm
Q
q
-1 0 0 1 62.69291 437.8236 cm
+1 0 0 1 62.69291 353.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5146,13 +5174,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 437.8236 cm
+1 0 0 1 62.69291 353.8236 cm
Q
q
-1 0 0 1 62.69291 437.8236 cm
+1 0 0 1 62.69291 353.8236 cm
Q
q
-1 0 0 1 62.69291 419.8236 cm
+1 0 0 1 62.69291 335.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5172,13 +5200,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 419.8236 cm
+1 0 0 1 62.69291 335.8236 cm
Q
q
-1 0 0 1 62.69291 419.8236 cm
+1 0 0 1 62.69291 335.8236 cm
Q
q
-1 0 0 1 62.69291 377.8236 cm
+1 0 0 1 62.69291 293.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5198,19 +5226,19 @@ q
Q
Q
q
-1 0 0 1 62.69291 377.8236 cm
+1 0 0 1 62.69291 293.8236 cm
Q
q
-1 0 0 1 62.69291 377.8236 cm
+1 0 0 1 62.69291 293.8236 cm
Q
q
-1 0 0 1 62.69291 359.8236 cm
+1 0 0 1 62.69291 275.8236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F4 10 Tf (ishelve.py ) Tj /F1 10 Tf (without arguments you get the following message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 314.6236 cm
+1 0 0 1 62.69291 230.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -5231,13 +5259,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 294.6236 cm
+1 0 0 1 62.69291 210.6236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F4 10 Tf (ishelve.py ) Tj /F1 10 Tf (with the option ) Tj /F4 10 Tf (.h ) Tj /F1 10 Tf (\(or any abbreviation of ) Tj /F4 10 Tf (.help) Tj /F1 10 Tf (\) you get:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 225.4236 cm
+1 0 0 1 62.69291 141.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -5257,34 +5285,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 205.4236 cm
+1 0 0 1 62.69291 121.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can check by hand that the tool work:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 100.2236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 96 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL ($ python ishelve.py .clear # start from an empty shelve) Tj T* (cleared the shelve) Tj T* ($ python ishelve.py a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* ($ python ishelve.py .showall) Tj T* (b=2) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -5298,11 +5305,11 @@ endobj
% 'R195': class PDFStream
195 0 obj
% page stream
-<< /Length 8351 >>
+<< /Length 7204 >>
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 523.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -5312,36 +5319,36 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
+n -6 -6 468.6898 240 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (a=1) Tj T* ($ python ishelve.py .del b # abbreviation for .delete) Tj T* (deleted b) Tj T* ($ python ishelve.py a) Tj T* (1) Tj T* ($ python ishelve.py b) Tj T* (b: not found) Tj T* ($ python ishelve.py .cler # mispelled command) Tj T* (usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) Tj T* (ishelve.py: error: unrecognized arguments: .cler) Tj T* ET
+BT 1 0 0 1 0 221.71 Tm /F4 10 Tf 12 TL ($ python ishelve.py .clear # start from an empty shelve) Tj T* (cleared the shelve) Tj T* ($ python ishelve.py a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* ($ python ishelve.py .showall) Tj T* (b=2) Tj T* (a=1) Tj T* ($ python ishelve.py .del b # abbreviation for .delete) Tj T* (deleted b) Tj T* ($ python ishelve.py a) Tj T* (1) Tj T* ($ python ishelve.py b) Tj T* (b: not found) Tj T* ($ python ishelve.py .cler # mispelled command) Tj T* (usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) Tj T* (ishelve.py: error: unrecognized arguments: .cler) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 574.8236 cm
+1 0 0 1 62.69291 490.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac vs argparse) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 532.8236 cm
+1 0 0 1 62.69291 448.8236 cm
q
BT 1 0 0 1 0 28.82 Tm 1.065988 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is opinionated and by design it does not try to make available all of the features of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (in an) Tj T* 0 Tw .177126 Tw (easy way. In particular you should be aware of the following limitations/differences \(the following assumes) Tj T* 0 Tw (knowledge of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (\):) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 526.8236 cm
+1 0 0 1 62.69291 442.8236 cm
Q
q
-1 0 0 1 62.69291 526.8236 cm
+1 0 0 1 62.69291 442.8236 cm
Q
q
-1 0 0 1 62.69291 448.8236 cm
+1 0 0 1 62.69291 364.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5361,13 +5368,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 448.8236 cm
+1 0 0 1 62.69291 364.8236 cm
Q
q
-1 0 0 1 62.69291 448.8236 cm
+1 0 0 1 62.69291 364.8236 cm
Q
q
-1 0 0 1 62.69291 394.8236 cm
+1 0 0 1 62.69291 310.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5387,13 +5394,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 394.8236 cm
+1 0 0 1 62.69291 310.8236 cm
Q
q
-1 0 0 1 62.69291 394.8236 cm
+1 0 0 1 62.69291 310.8236 cm
Q
q
-1 0 0 1 62.69291 328.8236 cm
+1 0 0 1 62.69291 244.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5413,13 +5420,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 328.8236 cm
+1 0 0 1 62.69291 244.8236 cm
Q
q
-1 0 0 1 62.69291 328.8236 cm
+1 0 0 1 62.69291 244.8236 cm
Q
q
-1 0 0 1 62.69291 286.8236 cm
+1 0 0 1 62.69291 202.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5439,13 +5446,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 286.8236 cm
+1 0 0 1 62.69291 202.8236 cm
Q
q
-1 0 0 1 62.69291 286.8236 cm
+1 0 0 1 62.69291 202.8236 cm
Q
q
-1 0 0 1 62.69291 256.8236 cm
+1 0 0 1 62.69291 172.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5465,13 +5472,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 256.8236 cm
+1 0 0 1 62.69291 172.8236 cm
Q
q
-1 0 0 1 62.69291 256.8236 cm
+1 0 0 1 62.69291 172.8236 cm
Q
q
-1 0 0 1 62.69291 214.8236 cm
+1 0 0 1 62.69291 130.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5491,30 +5498,18 @@ q
Q
Q
q
-1 0 0 1 62.69291 214.8236 cm
+1 0 0 1 62.69291 130.8236 cm
Q
q
-1 0 0 1 62.69291 214.8236 cm
+1 0 0 1 62.69291 130.8236 cm
Q
q
-1 0 0 1 62.69291 196.8236 cm
+1 0 0 1 62.69291 112.8236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can leverage directly on many ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 154.8236 cm
-q
-BT 1 0 0 1 0 28.82 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 1.435976 Tw /F4 10 Tf ('==SUPPRESS==' ) Tj /F1 10 Tf (as help string \(or ) Tj /F4 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use ) Tj 0 0 .501961 rg (argparse.FileType) Tj T* 0 Tw 0 0 0 rg (directly.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 100.8236 cm
-q
-BT 1 0 0 1 0 40.82 Tm 1.639213 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is also possible to pass options to the underlying ) Tj /F4 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F4 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F4 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -5528,11 +5523,23 @@ endobj
% 'R196': class PDFStream
196 0 obj
% page stream
-<< /Length 7105 >>
+<< /Length 6860 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 703.8236 cm
+1 0 0 1 62.69291 729.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 1.435976 Tw /F4 10 Tf ('==SUPPRESS==' ) Tj /F1 10 Tf (as help string \(or ) Tj /F4 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use ) Tj 0 0 .501961 rg (argparse.FileType) Tj T* 0 Tw 0 0 0 rg (directly.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 675.0236 cm
+q
+BT 1 0 0 1 0 40.82 Tm 1.639213 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is also possible to pass options to the underlying ) Tj /F4 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F4 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F4 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 605.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -5553,26 +5560,26 @@ Q
Q
Q
q
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 561.8236 cm
q
BT 1 0 0 1 0 28.82 Tm .239318 Tw 12 TL /F1 10 Tf 0 0 0 rg (disables the recognition of the help flag ) Tj /F4 10 Tf (-h, --help) Tj /F1 10 Tf (. This mechanism does not look particularly elegant,) Tj T* 0 Tw .566988 Tw (but it works well enough. I assume that the typical user of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (will be happy with the defaults and would) Tj T* 0 Tw (not want to change them; still it is possible if she wants to.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 629.8236 cm
+1 0 0 1 62.69291 531.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.391235 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, by setting the ) Tj /F4 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add a comment to the usage) Tj T* 0 Tw (message \(by default the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function is used as description\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 599.8236 cm
+1 0 0 1 62.69291 501.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .392619 Tw (It is also possible to change the option prefix; for instance if your script must run under Windows and you) Tj T* 0 Tw (want to use "/" as option prefix you can add the line:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 566.6236 cm
+1 0 0 1 62.69291 468.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -5593,19 +5600,19 @@ Q
Q
Q
q
-1 0 0 1 62.69291 522.6236 cm
+1 0 0 1 62.69291 424.6236 cm
q
BT 1 0 0 1 0 28.82 Tm .924198 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first prefix char \() Tj /F4 10 Tf (/) Tj /F1 10 Tf (\) is used as the default for the recognition of options and flags; the second prefix) Tj T* 0 Tw .26832 Tw (char \() Tj /F4 10 Tf (-) Tj /F1 10 Tf (\) is kept to keep the ) Tj /F4 10 Tf (-h/--help ) Tj /F1 10 Tf (option working: however you can disable it and reimplement it, if) Tj T* 0 Tw (you like, as seen in the ) Tj /F4 10 Tf (ishelve ) Tj /F1 10 Tf (example.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 492.6236 cm
+1 0 0 1 62.69291 394.6236 cm
q
BT 1 0 0 1 0 16.82 Tm 7.709147 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is possible to access directly the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, by invoking the) Tj T* 0 Tw /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 399.4236 cm
+1 0 0 1 62.69291 301.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -5625,43 +5632,60 @@ Q
Q
Q
q
-1 0 0 1 62.69291 355.4236 cm
+1 0 0 1 62.69291 257.4236 cm
q
BT 1 0 0 1 0 28.82 Tm 2.646905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F4 10 Tf (plac.call ) Tj /F1 10 Tf (uses ) Tj /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and adds the parser to the main function as an) Tj T* 0 Tw .982126 Tw (attribute. When ) Tj /F4 10 Tf (plac.call\(func\) ) Tj /F1 10 Tf (is invoked multiple time, the parser is re-used and not rebuilt from) Tj T* 0 Tw (scratch again.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 325.4236 cm
+1 0 0 1 62.69291 227.4236 cm
q
BT 1 0 0 1 0 16.82 Tm .982765 Tw 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (in the unit tests of the module, but regular users should not need to use it,) Tj T* 0 Tw (unless they want to access ) Tj /F5 10 Tf (all ) Tj /F1 10 Tf (of the features of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (directly without calling the main function.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 271.4236 cm
+1 0 0 1 62.69291 173.4236 cm
q
BT 1 0 0 1 0 40.82 Tm 1.442126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Interested readers should read the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to understand the meaning of the other) Tj T* 0 Tw .771567 Tw (options. If there is a set of options that you use very often, you may consider writing a decorator adding) Tj T* 0 Tw 1.257045 Tw (such options to the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function for you. For simplicity, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not perform any magic except the) Tj T* 0 Tw (addition of the ) Tj /F4 10 Tf (.p ) Tj /F1 10 Tf (attribute.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 238.4236 cm
+1 0 0 1 62.69291 140.4236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (plac vs the rest of the world) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 196.4236 cm
+1 0 0 1 62.69291 98.42362 cm
q
BT 1 0 0 1 0 28.82 Tm 1.866905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Originally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (boasted about being "the easiest command-line arguments parser in the world". Since) Tj T* 0 Tw .306457 Tw (then, people started pointing out to me various projects which are based on the same idea \(extracting the) Tj T* 0 Tw (parser from the main function signature\) and are arguably even easier than ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 190.4236 cm
+1 0 0 1 62.69291 92.42362 cm
Q
q
-1 0 0 1 62.69291 190.4236 cm
+1 0 0 1 62.69291 92.42362 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
+% 'R197': class PDFStream
+197 0 obj
+% page stream
+<< /Length 7645 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 172.4236 cm
+1 0 0 1 62.69291 747.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5681,13 +5705,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 172.4236 cm
+1 0 0 1 62.69291 747.0236 cm
Q
q
-1 0 0 1 62.69291 172.4236 cm
+1 0 0 1 62.69291 747.0236 cm
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 729.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5707,90 +5731,67 @@ q
Q
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 729.0236 cm
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 729.0236 cm
Q
q
-1 0 0 1 62.69291 124.4236 cm
+1 0 0 1 62.69291 699.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 2.136457 Tw 12 TL /F1 10 Tf 0 0 0 rg (Luckily for me none of such projects had the idea of using function annotations and ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (; as a) Tj T* 0 Tw (consequence, they are no match for the capabilities of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 94.42362 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.551163 Tw 12 TL /F1 10 Tf 0 0 0 rg (Of course, there are tons of other libraries to parse the command line. For instance ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (by Matthew ) Tj T* 0 Tw 1.211567 Tw (Frazier which appeared on PyPI just the day before ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (; ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (is fine but it is certainly not easier than) Tj T* 0 Tw ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (16) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R197': class PDFStream
-197 0 obj
-% page stream
-<< /Length 6121 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 753.0236 cm
+1 0 0 1 62.69291 657.0236 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 1.551163 Tw 12 TL /F1 10 Tf 0 0 0 rg (Of course, there are tons of other libraries to parse the command line. For instance ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (by Matthew) Tj T* 0 Tw 1.211567 Tw (Frazier which appeared on PyPI just the day before ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (; ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (is fine but it is certainly not easier than) Tj T* 0 Tw 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 720.0236 cm
+1 0 0 1 62.69291 624.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The future) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 654.0236 cm
+1 0 0 1 62.69291 558.0236 cm
q
BT 1 0 0 1 0 52.82 Tm .135542 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently the core of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is around 200 lines of code, not counting blanks, comments and docstrings. I do) Tj T* 0 Tw .968626 Tw (not plan to extend the core much in the future. The idea is to keep the module short: it is and it should) Tj T* 0 Tw .11811 Tw (remain a little wrapper over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually I have thought about contributing the core back to ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw 2.307485 Tw 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (becomes successfull and gains a reasonable number of users. For the moment it should be) Tj T* 0 Tw (considered in alpha status.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 612.0236 cm
+1 0 0 1 62.69291 516.0236 cm
q
BT 1 0 0 1 0 28.82 Tm .927488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that even if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (has been designed to be simple to use for simple stuff, its power should not be) Tj T* 0 Tw 1.02186 Tw (underestimated; it is actually a quite advanced tool with a domain of applicability which far exceeds the) Tj T* 0 Tw (realm of command-line arguments parsers.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 546.0236 cm
+1 0 0 1 62.69291 450.0236 cm
q
BT 1 0 0 1 0 52.82 Tm .285988 Tw 12 TL /F1 10 Tf 0 0 0 rg (Version 0.5 of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (doubled the code base and the documentation: it is based on the idea of using ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to) Tj T* 0 Tw .408555 Tw (implement command-line interpreters, i.e. something akin to the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module in the standard library, only) Tj T* 0 Tw .49936 Tw (better. The new features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (are described in the ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (. They are implemented) Tj T* 0 Tw .313828 Tw (in a separated module \() Tj /F4 10 Tf (plac_ext.py) Tj /F1 10 Tf (\), since they require Python 2.5 to work, whereas ) Tj /F4 10 Tf (plac_core.py) Tj T* 0 Tw /F1 10 Tf (only requires Python 2.3.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 513.0236 cm
+1 0 0 1 62.69291 417.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Trivia: the story behind the name) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 447.0236 cm
+1 0 0 1 62.69291 351.0236 cm
q
BT 1 0 0 1 0 52.82 Tm .942651 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (project started very humble: I just wanted to make easy_installable my old ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw .565988 Tw (and to publish it on PyPI. The original name of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was optionparser and the idea behind it was to build) Tj T* 0 Tw .603735 Tw (an ) Tj 0 0 .501961 rg (OptionParser ) Tj 0 0 0 rg (object from the docstring of the module. However, before doing that, I decided to check) Tj T* 0 Tw .244198 Tw (out the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module, since I knew it was going into Python 2.7 and Python 2.7 was coming out. Soon) Tj T* 0 Tw (enough I realized two things:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 441.0236 cm
+1 0 0 1 62.69291 345.0236 cm
Q
q
-1 0 0 1 62.69291 441.0236 cm
+1 0 0 1 62.69291 345.0236 cm
Q
q
-1 0 0 1 62.69291 411.0236 cm
+1 0 0 1 62.69291 315.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5810,13 +5811,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 411.0236 cm
+1 0 0 1 62.69291 315.0236 cm
Q
q
-1 0 0 1 62.69291 411.0236 cm
+1 0 0 1 62.69291 315.0236 cm
Q
q
-1 0 0 1 62.69291 381.0236 cm
+1 0 0 1 62.69291 285.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -5837,32 +5838,32 @@ q
Q
Q
q
-1 0 0 1 62.69291 381.0236 cm
+1 0 0 1 62.69291 285.0236 cm
Q
q
-1 0 0 1 62.69291 381.0236 cm
+1 0 0 1 62.69291 285.0236 cm
Q
q
-1 0 0 1 62.69291 327.0236 cm
+1 0 0 1 62.69291 231.0236 cm
q
BT 1 0 0 1 0 40.82 Tm .600574 Tw 12 TL /F1 10 Tf 0 0 0 rg (Putting together these two observations with the original idea of inferring the parser I decided to build an) Tj T* 0 Tw .516905 Tw 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object from function annotations. The ) Tj /F4 10 Tf (optionparser ) Tj /F1 10 Tf (name was ruled out, since I was) Tj T* 0 Tw 2.085984 Tw (now using ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (; a name like ) Tj /F4 10 Tf (argparse_plus ) Tj /F1 10 Tf (was also ruled out, since the typical usage was) Tj T* 0 Tw (completely different from the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (usage.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 297.0236 cm
+1 0 0 1 62.69291 201.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.093876 Tw 12 TL /F1 10 Tf 0 0 0 rg (I made a research on PyPI and the name ) Tj /F5 10 Tf (clap ) Tj /F1 10 Tf (\(Command Line Arguments Parser\) was not taken, so I) Tj T* 0 Tw (renamed everything to clap. After two days a ) Tj 0 0 .501961 rg (Clap ) Tj 0 0 0 rg (module appeared on PyPI <) Tj (expletives deleted) Tj (>) Tj (!) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 267.0236 cm
+1 0 0 1 62.69291 171.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .877209 Tw (Having little imagination, I decided to rename everything again to plac, an anagram of clap: since it is a) Tj T* 0 Tw (non-existing English name, I hope nobody will steal it from me!) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 249.0236 cm
+1 0 0 1 62.69291 153.0236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (That's all, I hope you will enjoy working with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (!) Tj T* ET
Q
@@ -6144,45 +6145,45 @@ xref
0000031138 00000 n
0000031417 00000 n
0000031671 00000 n
-0000031923 00000 n
-0000032226 00000 n
-0000032757 00000 n
-0000033011 00000 n
-0000033300 00000 n
-0000033552 00000 n
-0000033804 00000 n
-0000034058 00000 n
-0000034312 00000 n
-0000034566 00000 n
-0000034825 00000 n
-0000035082 00000 n
-0000035334 00000 n
-0000035588 00000 n
-0000035846 00000 n
-0000036098 00000 n
-0000036341 00000 n
-0000036792 00000 n
-0000037046 00000 n
-0000037300 00000 n
-0000037552 00000 n
-0000037804 00000 n
-0000038057 00000 n
-0000038311 00000 n
-0000038565 00000 n
-0000038819 00000 n
-0000039073 00000 n
-0000039352 00000 n
-0000039606 00000 n
-0000039893 00000 n
-0000040147 00000 n
-0000040458 00000 n
-0000040710 00000 n
-0000040962 00000 n
-0000041251 00000 n
-0000041503 00000 n
-0000041755 00000 n
-0000042013 00000 n
-0000042252 00000 n
+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
+0000037230 00000 n
+0000037482 00000 n
+0000037734 00000 n
+0000037987 00000 n
+0000038241 00000 n
+0000038495 00000 n
+0000038749 00000 n
+0000039003 00000 n
+0000039282 00000 n
+0000039536 00000 n
+0000039823 00000 n
+0000040077 00000 n
+0000040388 00000 n
+0000040640 00000 n
+0000040892 00000 n
+0000041181 00000 n
+0000041433 00000 n
+0000041685 00000 n
+0000041943 00000 n
+0000042182 00000 n
0000042759 00000 n
0000042923 00000 n
0000043197 00000 n
@@ -6211,39 +6212,39 @@ xref
0000082887 00000 n
0000088055 00000 n
0000093492 00000 n
-0000098162 00000 n
-0000102104 00000 n
-0000106055 00000 n
-0000109787 00000 n
-0000116862 00000 n
-0000125316 00000 n
-0000132524 00000 n
-0000138752 00000 n
-0000139079 00000 n
-0000139158 00000 n
-0000139237 00000 n
-0000139316 00000 n
-0000139395 00000 n
-0000139474 00000 n
-0000139553 00000 n
-0000139632 00000 n
-0000139711 00000 n
-0000139790 00000 n
-0000139870 00000 n
-0000139950 00000 n
-0000140030 00000 n
-0000140110 00000 n
-0000140190 00000 n
-0000140270 00000 n
-0000140350 00000 n
+0000098106 00000 n
+0000102445 00000 n
+0000105971 00000 n
+0000110121 00000 n
+0000117019 00000 n
+0000124326 00000 n
+0000131289 00000 n
+0000139041 00000 n
+0000139368 00000 n
+0000139447 00000 n
+0000139526 00000 n
+0000139605 00000 n
+0000139684 00000 n
+0000139763 00000 n
+0000139842 00000 n
+0000139921 00000 n
+0000140000 00000 n
+0000140079 00000 n
+0000140159 00000 n
+0000140239 00000 n
+0000140319 00000 n
+0000140399 00000 n
+0000140479 00000 n
+0000140559 00000 n
+0000140639 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\270G\023\(\355mFCp\200\3773\015M\207;) (\270G\023\(\355mFCp\200\3773\015M\207;)]
+ [(\035U\351\206J$\3719\335\010i\224\201=?\204) (\035U\351\206J$\3719\335\010i\224\201=?\204)]
/Info 164 0 R
/Root 163 0 R
/Size 216 >>
startxref
-140399
+140688
%%EOF
diff --git a/plac/doc/plac.txt b/plac/doc/plac.txt
index ccbe8f7..f43d57c 100644
--- a/plac/doc/plac.txt
+++ b/plac/doc/plac.txt
@@ -419,13 +419,19 @@ errors visible early, and avoids mistakes in code like the following::
except:
# do something
-Without the ``listify`` functionality, a main function returning a
+Without the "listify" functionality, a main function returning a
generator object would not raise any exception until the generator
-is iterated over.
+is iterated over.
-Moreover you can rely on type checks like ``isinstance(result, list)``
-for the output of ``plac.call``, to check if the output is an iterable
-or not.
+If you are a fan of lazyness, you can still have it by setting the ``eager``
+flag to ``False``, as in the following example::
+
+ for line in plac.call(main, args, eager=False):
+ print(line)
+
+If ``main`` 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.
A realistic example
---------------------------------------
diff --git a/plac/doc/plac_adv.html b/plac/doc/plac_adv.html
index ecbaf2f..5629997 100644
--- a/plac/doc/plac_adv.html
+++ b/plac/doc/plac_adv.html
@@ -4,9 +4,9 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
-<title>Testing and scripting your applications with plac</title>
+<title>Advanced usages of plac</title>
<meta name="author" content="Michele Simionato" />
-<meta name="date" content="June 2010" />
+<meta name="date" content="July 2010" />
<style type="text/css">
.first {
@@ -413,8 +413,8 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
</style>
</head>
<body>
-<div class="document" id="testing-and-scripting-your-applications-with-plac">
-<h1 class="title">Testing and scripting your applications with plac</h1>
+<div class="document" id="advanced-usages-of-plac">
+<h1 class="title">Advanced usages of plac</h1>
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
@@ -424,7 +424,7 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
</tr>
<tr><th class="docinfo-name">Date:</th>
-<td>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>
@@ -438,7 +438,9 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
</tbody>
</table>
<p><em>The present document discusses a few of the advanced use
-cases for plac. It assumes you have already read an understood the
+cases for plac. It shows how to write interactive and non-interactive
+interpreters with plac, and how to use plac for testing and scripting a generic
+application. It assumes you have already read an understood the
basic documentation.</em></p>
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
@@ -448,13 +450,16 @@ basic documentation.</em></p>
<li><a class="reference internal" href="#testing-a-plac-application" id="id3">Testing a plac application</a></li>
<li><a class="reference internal" href="#plac-easy-tests" id="id4">Plac easy tests</a></li>
<li><a class="reference internal" href="#plac-batch-scripts" id="id5">Plac batch scripts</a></li>
-<li><a class="reference internal" href="#containers-of-commands" id="id6">Containers of commands</a></li>
-<li><a class="reference internal" href="#for-cmd-lovers" id="id7">For <tt class="docutils literal">cmd</tt> lovers</a></li>
+<li><a class="reference internal" href="#implementing-subcommands" id="id6">Implementing subcommands</a></li>
+<li><a class="reference internal" href="#readline-support" id="id7">Readline support</a></li>
<li><a class="reference internal" href="#the-plac-runner" id="id8">The plac runner</a></li>
<li><a class="reference internal" href="#a-non-class-based-example" id="id9">A non class-based example</a></li>
<li><a class="reference internal" href="#writing-your-own-plac-runner" id="id10">Writing your own plac runner</a></li>
-<li><a class="reference internal" href="#summary" id="id11">Summary</a></li>
-<li><a class="reference internal" href="#appendix-custom-annotation-objects" id="id12">Appendix: custom annotation objects</a></li>
+<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>
</ul>
</div>
<div class="section" id="introduction">
@@ -524,8 +529,9 @@ $ python shelve_interpreter.py -h
usage: shelve_interpreter.py [-h] [-interactive]
[subcommands [subcommands ...]]
-This script works both interactively and non-interactively. Use .help to see
-the internal commands.
+ This script works both interactively and non-interactively.
+ Use .help to see the internal commands.
+
positional arguments:
subcommands the commands of the underlying ishelve interpreter
@@ -541,13 +547,14 @@ and non-interactively:</p>
$ python shelve_interpreter.py .clear # non-interactive use
cleared the shelve
</pre>
-<p>Here is an usage session, using <a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a> to enable readline features
-(<a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a> is available in Unix-like systems):</p>
+<p>Here is an usage session:</p>
<pre class="literal-block">
-$ rlwrap python shelve_interpreter.py -i # interactive use
-usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
+$ python shelve_interpreter.py -i # interactive use
+A simple interface to a shelve. Use .help to see the available commands.
+i&gt; .help
+Commands: .help, .showall, .clear, .delete
+&lt;param&gt; ...
+&lt;param=value&gt; ...
i&gt; a=1
setting a=1
i&gt; a
@@ -570,17 +577,19 @@ reads commands from the console and send them to the
underlying interpreter, until the user send a CTRL-D
command (CTRL-Z in Windows). There is a default
argument <tt class="docutils literal"><span class="pre">prompt='i&gt;</span> '</tt> which
-can be used to change the prompt. The message displayed
-by default is the argparse-provided usage message, but can be
-customized by setting an <tt class="docutils literal">.intro</tt> attribute on the main function.</p>
-<p>Notice that <tt class="docutils literal">plac.Interpreter</tt> is available only if you are using a recent
-version of Python (&gt;= 2.5), because it is a context manager object
-which uses extended generators internally.</p>
-<p>You can conveniently test your application in interactive mode.
-However manual testing is a poor substitute for automatic testing.</p>
+can be used to change the prompt. The text displayed at the beginning
+of the interactive session is the docstring of the main function.
+<tt class="docutils literal">plac</tt> also understands command abbreviations: in this example
+<tt class="docutils literal">del</tt> is an abbreviation for <tt class="docutils literal">delete</tt>. In case of ambiguous
+abbreviations <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> raises a <tt class="docutils literal">NameError</tt>.</p>
+<p>Finally I must notice that the <tt class="docutils literal">plac.Interpreter</tt> is available only if you
+are using a recent version of Python (&gt;= 2.5), because it is a context
+manager object which uses extended generators internally.</p>
</div>
<div class="section" id="testing-a-plac-application">
<h1><a class="toc-backref" href="#id3">Testing a plac application</a></h1>
+<p>You can conveniently test your application in interactive mode.
+However manual testing is a poor substitute for automatic testing.</p>
<p>In principle, one could write automatic tests for the
<tt class="docutils literal">ishelve</tt> application by using <tt class="docutils literal">plac.call</tt> directly:</p>
<pre class="literal-block">
@@ -623,8 +632,8 @@ def test():
</pre>
<p>The method <tt class="docutils literal">.check(given_input, expected_output)</tt> works on strings
and raises an <tt class="docutils literal">AssertionError</tt> if the output produced by the
-interpreter is different from the expected output for the given input.</p>
-<p><tt class="docutils literal">AssertionError</tt> is catched by tools like <tt class="docutils literal">py.test</tt> and
+interpreter is different from the expected output for the given input.
+Notice that <tt class="docutils literal">AssertionError</tt> is catched by tools like <tt class="docutils literal">py.test</tt> and
<tt class="docutils literal">nosetests</tt> and actually <tt class="docutils literal">plac</tt> tests are intended to be run with
such tools.</p>
<p>Interpreters offer a minor syntactic advantage with respect to calling
@@ -649,7 +658,7 @@ improvement over writing them in terms of <tt class="docutils literal">plac.call
are still too low-level for my taste. The <tt class="docutils literal">Interpreter</tt> class provides
support for doctest-style tests, a.k.a. <em>plac easy tests</em>.</p>
<p>By using plac easy tests you can cut and paste your interactive session and
-turn it into a runnable automatics test!
+turn it into a runnable automatics test.
Consider for instance the following file <tt class="docutils literal">ishelve.placet</tt> (the <tt class="docutils literal">.placet</tt>
extension is a mnemonic for plac easy tests):</p>
<pre class="literal-block">
@@ -665,7 +674,7 @@ deleted a
i&gt; a
a: not found
i&gt; .cler # spelling error
-SystemExit: unrecognized arguments: .cler
+.cler: not found
</pre>
<p>Notice the precence of the shebang line containing the name of the
@@ -682,25 +691,26 @@ plac.Interpreter(ishelve.main).doctest(open('ishelve.placet'), verbose=True)&quo
</pre>
<p>Internally <tt class="docutils literal">Interpreter.doctests</tt> invokes something like <tt class="docutils literal">Interpreter.check</tt>
multiple times inside the same context and compare the output with the
-expected output: if even a check fails, the whole test fail. The
-easy tests supported by <tt class="docutils literal">plac</tt> are <em>not</em> unittests: they should be
-used to model user interaction when the order of the operations
-matters. Since the single subtests in a <tt class="docutils literal">.placet</tt> file are not
-independent, it makes sense to exit immediately at the first failure.</p>
-<p>The support for doctests in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> comes nearly for free, thanks to the <a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a>
-module in the standard library, which is able to parse simple
+expected output: if even a check fails, the whole test fail.</p>
+<p>You should realize tha the easy tests supported by <tt class="docutils literal">plac</tt> are <em>not</em>
+unittests: they are functional tests. They model then user interaction and the
+order of the operations generally matters. The single subtests in a
+<tt class="docutils literal">.placet</tt> file are not independent and it makes sense to exit
+immediately at the first failure.</p>
+<p>The support for doctests in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> comes nearly for free, thanks to the
+<a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a> module in the standard library, which is able to parse simple
languages as the ones you can implement with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. In particular,
thanks to <a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a>, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize comments (the default
-comment character is <tt class="docutils literal">#</tt>), continuation lines, escape sequences and
-more. Look at the <a class="reference external" href="http://docs.python.org/library/shlex.html">shlex</a> documentation if you need to customize how
-the language is interpreted.</p>
+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>
<p>In addition, I have implemented from scratch some support for line number
recognition, so that if a test fail you get the line number of the
failing command. This is especially useful if your tests are
-stored in external files (plac easy tests does not need to be in
+stored in external files, even if plac easy tests does not need to be in
a file: you can just pass to the <tt class="docutils literal">.doctest</tt> method a list of
-strings corresponding to the lines of the file).</p>
-<p>At the present plac easy tests do not use any code from the doctest
+strings corresponding to the lines of the file.</p>
+<p>At the present <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not use any code from the doctest
module, but the situation may change in the future (it would be nice
if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> could reuse doctests directives like ELLIPSIS).</p>
<p>It is straighforward to integrate your <tt class="docutils literal">.placet</tt> tests with standard
@@ -724,7 +734,7 @@ def test_doct():
</pre>
<p>Here you should notice that usage of <tt class="docutils literal">plac.import_main</tt>, an utility
which is able to import the main function of the script specified in
-the shabng line. You can use both the full path name of the
+the shebang line. You can use both the full path name of the
tool, or a relative path name. In this case the runner look at the
environment variable <tt class="docutils literal">PLACPATH</tt> and it searches
the plac tool in the directories specified there (<tt class="docutils literal">PLACPATH</tt> is just
@@ -745,8 +755,9 @@ background in case of unexpected errors. The implementation of
<tt class="docutils literal">plac.call</tt> internally is re-raised. In other words, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
interpreters <em>wrap the errors, but does not eat them</em>: the errors are
always accessible and can be re-raised on demand.</p>
-<p>In particular consider the following batch file, which contains a syntax
-error (<tt class="docutils literal">.dl</tt> instead of <tt class="docutils literal">.del</tt>):</p>
+<p>The exception is the case of invalid commands, which are skipped.
+Consider for instance the following batch file, which contains a
+mispelled command (<tt class="docutils literal">.dl</tt> instead of <tt class="docutils literal">.del</tt>):</p>
<pre class="literal-block">
#!ishelve.py
.clear
@@ -757,9 +768,8 @@ a=1 b=2
.show
</pre>
-<p>If you execute the batch file, the interpreter will raise a <tt class="docutils literal">SystemExit</tt>
-with an appropriated error message at the <tt class="docutils literal">.dl</tt> line and the last command
-will <em>not</em> be executed:</p>
+<p>If you execute the batch file, the interpreter will print a <tt class="docutils literal">.dl: not found</tt>
+at the <tt class="docutils literal">.dl</tt> line and will continue:</p>
<pre class="literal-block">
$ python -c &quot;import plac, ishelve
plac.Interpreter(ishelve.main).execute(open('ishelve.plac'), verbose=True)&quot;
@@ -774,19 +784,22 @@ a=1
i&gt; .del a
deleted a
i&gt; .dl b
-unrecognized arguments: .dl
+2
+.dl: not found
+i&gt; .show
+b=2
</pre>
<p>The <tt class="docutils literal">verbose</tt> flag is there to show the lines which are being interpreted
(prefixed by <tt class="docutils literal">i&gt;</tt>). This is done on purpose, so that you can cut and paste
the output of the batch script and turn it into a <tt class="docutils literal">.placet</tt> test
(cool, isn't it?).</p>
</div>
-<div class="section" id="containers-of-commands">
-<h1><a class="toc-backref" href="#id6">Containers of commands</a></h1>
+<div class="section" id="implementing-subcommands">
+<h1><a class="toc-backref" href="#id6">Implementing subcommands</a></h1>
<p>When I discussed the <tt class="docutils literal">ishelve</tt> implementation in the <a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">basic
documentation</a>, I said that it looked like a poor man implementation
of an object system as a chain of elifs; I also said that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was
-able to do better. Here I will substantiate my claim.</p>
+able to do much better than that. Here I will substantiate my claim.</p>
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is actually able to infer a set of subparsers from a
generic container of commands. This is useful if you want to
implement <em>subcommands</em> (a familiar example of a command-line
@@ -794,25 +807,28 @@ application featuring subcommands is subversion).</p>
<p>Technically a container of commands is any object with a <tt class="docutils literal">.commands</tt> attribute
listing a set of functions or methods which are valid commands. A command
container may have initialization/finalization hooks (<tt class="docutils literal">__enter__/__exit__</tt>)
-and dispatch hooks (<tt class="docutils literal">__missing__</tt>, invoked for invalid command names).</p>
-<p>Using this feature the shelve interface can be rewritten in a more
-object-oriented way as follows:</p>
+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>
<pre class="literal-block">
# ishelve2.py
import shelve, os, sys, plac
class ShelveInterface(object):
- &quot;A minimal interface over a shelve object&quot;
+ &quot;A minimal interface over a shelve object.&quot;
commands = 'set', 'show', 'showall', 'delete'
&#64;plac.annotations(
configfile=('path name of the shelve', 'option'))
def __init__(self, configfile='~/conf.shelve'):
self.fname = os.path.expanduser(configfile)
- self.intro = 'Operating on %s. Available commands:\n%s' % (
- self.fname, '\n'.join(c for c in self.commands))
+ self.__doc__ += '\nOperating on %s.\n.help to see '\
+ 'the available commands.\n' % self.fname
def __enter__(self):
self.sh = shelve.open(self.fname)
return self
+ def __exit__(self, etype, exc, tb):
+ self.sh.close()
def set(self, name, value):
&quot;set name value&quot;
yield 'setting %s=%s' % (name, value)
@@ -833,14 +849,12 @@ class ShelveInterface(object):
else:
yield 'deleting %s' % name
del self.sh[name] # no error checking
- def __exit__(self, etype, exc, tb):
- self.sh.close()
main = ShelveInterface # the main 'function' can also be a class!
if __name__ == '__main__':
- i = plac.Interpreter(main())
- i.interact()
+ shelve_interface = plac.call(main)
+ plac.Interpreter(shelve_interface).interact()
</pre>
<p><tt class="docutils literal">plac.Interpreter</tt> objects wrap context manager objects
@@ -854,14 +868,30 @@ correctly even in the case of exceptions. Notice that I have not
implemented any error checking in the <tt class="docutils literal">show</tt> and <tt class="docutils literal">delete</tt> methods
on purpose, to verify that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> works correctly in the presence of
exceptions.</p>
+<p>When working with command containers, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> automatically adds two
+special commands to the set of provided commands: <tt class="docutils literal">.help</tt>
+and <tt class="docutils literal">.last_tb</tt>. The <tt class="docutils literal">.help</tt> command is the easier to understand:
+when invoked without arguments it displays the list of available commands
+with the same formatting of the <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module; when invoked with the name of
+a command it displays the usage message for that command.
+The <tt class="docutils literal">.last_tb</tt> command is useful when debugging: in case of errors,
+it allows you to display the traceback of the last executed command.</p>
<p>Here is a session of usage on an Unix-like operating system:</p>
<pre class="literal-block">
-$ rlwrap python ishelve2.py
-Operating on /home/micheles/conf.shelve. Available commands:
-set
-show
-showall
-delete
+$ python ishelve2.py
+A minimal interface over a shelve object.
+Operating on /home/micheles/conf.shelve.
+.help to see the available commands.
+i&gt; .help
+
+special commands
+================
+.help .last_tb
+
+custom commands
+===============
+delete set show showall
+
i&gt; delete
deleting everything
i&gt; set a pippo
@@ -879,77 +909,128 @@ deleting a
i&gt; showall
b = lippo
i&gt; delete a
-DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
+deleting a
+KeyError: 'a'
+i&gt; .last_tb
+ File &quot;/usr/local/lib/python2.6/dist-packages/plac-0.6.0-py2.6.egg/plac_ext.py&quot;, line 190, in _wrap
+ for value in genobj:
+ File &quot;./ishelve2.py&quot;, line 37, in delete
+ del self.sh[name] # no error checking
+ File &quot;/usr/lib/python2.6/shelve.py&quot;, line 136, in __delitem__
+ del self.dict[key]
i&gt;
</pre>
<p>Notice that in interactive mode the traceback is hidden, unless
you pass the <tt class="docutils literal">verbose</tt> flag to the <tt class="docutils literal">Interpreter.interact</tt> method.</p>
-<p>The interactive mode of <tt class="docutils literal">plac</tt> can be used as a replacement of the
-<tt class="docutils literal">cmd</tt> module in the standard library. There are a few differences,
-however. For instance you miss tab completion, even if use <a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a>
-(you get persistent command history for free, however). This is not
-a big issue, since <tt class="docutils literal">plac</tt> understands command abbreviations.</p>
-<p>If an abbreviation is ambiguous, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> warns you:</p>
-<pre class="literal-block">
-$ rlwrap python ishelve2.py
-usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...
-i&gt; sh
-NameError: Ambiguous command 'sh': matching ['showall', 'show']
-</pre>
</div>
-<div class="section" id="for-cmd-lovers">
-<h1><a class="toc-backref" href="#id7">For <tt class="docutils literal">cmd</tt> lovers</a></h1>
-<p>I have been using the <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module of the standard library for years.
-I have also written a much enhanced <tt class="docutils literal">cmd2</tt> module which we are using
-internally at work and from which I have taken some ideas used in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.
-In many ways <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> makes the <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module obsolete,
-but I realize why many nostalgic souls would still use <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a>, especially
-until <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not grow real auto-completion features, instead of
-relying on <a class="reference external" href="http://freshmeat.net/projects/rlwrap/">rlwrap</a>. But there must not be competition between <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-and <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a>: actually the two can happily work togethere. For this
-reason I have put in the <tt class="docutils literal">plac_ext</tt> module a few lines of code
-for gluing together <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> and <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, the <tt class="docutils literal">cmd_interface</tt>.
-Using the <tt class="docutils literal">cmd_interface</tt> is quite trivial: give to it a plac
-command container and you will get in exchange a <tt class="docutils literal">cmd.Cmd</tt> object:</p>
+<div class="section" id="readline-support">
+<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>
<pre class="literal-block">
-# cmd_ext.py
-from plac_ext import cmd_interface
-import ishelve2
+import os, plac
+from sqlalchemy.ext.sqlsoup import SqlSoup
+
+SQLKEYWORDS = set(['select', 'from', 'inner', 'join', 'outer', 'left', 'right']
+ ) # and many others
+DBTABLES = set(['table1', 'table2']) # you can read them from the db schema
+
+COMPLETIONS = SQLKEYWORDS | DBTABLES
+
+class SqlInterface(object):
+ commands = ['SELECT']
+ def __init__(self, dsn):
+ self.soup = SqlSoup(dsn)
+ def SELECT(self, *args):
+ sql = 'SELECT ' + ' '.join(args)
+ for row in self.soup.bind.execute(sql):
+ yield str(row) # the formatting can be much improved
+
+rl_input = plac.ReadlineInput(
+ COMPLETIONS, prompt='sql&gt; ',
+ histfile=os.path.expanduser('~/.sql_interface.history'),
+ case_sensitive=False)
if __name__ == '__main__':
- cmd_interface(ishelve2.main()).cmdloop()
+ plac.Interpreter(plac.call(SqlInterface)).interact(rl_input)
</pre>
-<p>Here is an example of interactive session:</p>
+<p>Here is an example of usage:</p>
<pre class="literal-block">
-$ python cmd_ex.py
-(Cmd) help
+$ python sql_interface.py &lt;some dsn&gt;
+sql&gt; SELECT a.* FROM TABLE1 AS a INNER JOIN TABLE2 AS b ON a.id = b.id
+...
+</pre>
+<p>You can check that entering just <tt class="docutils literal">sel</tt> and pressing TAB the readline library
+completes the <tt class="docutils literal">SELECT</tt> keyword for you and makes it upper case; idem for
+<tt class="docutils literal">FROM</tt>, <tt class="docutils literal">INNER</tt>, <tt class="docutils literal">JOIN</tt> and even for the names of the tables. An
+obvious improvement is to read the names of the tables by introspecting
+the database: actually you can even read the names of the views and of
+the columns, and have full autocompletion. All the entered commands
+and recorded and saved in the file <tt class="docutils literal"><span class="pre">~/.sql_interface.history</span></tt> when
+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).
+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
+not need to be the GPL (actually it is a BSD
+do-whatever-you-want-with-it licence).</p>
+<p>The interactive mode of <tt class="docutils literal">plac</tt> can be used as a replacement of the
+<a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module in the standard library. It is actually better than <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a>:
+for instance, the <tt class="docutils literal">.help</tt> command is more powerful, since it
+provides information about the arguments accepted by the given command:</p>
+<pre class="literal-block">
+i&gt; .help set
+usage: set name value
-Documented commands (type help &lt;topic&gt;):
-========================================
-delete set show showall
+set name value
-Undocumented commands:
-======================
-EOF help
+positional arguments:
+ name
+ value
-(Cmd) set a 1
-setting a=1
-(Cmd) show a
-a = 1
-(Cmd) showall
-a = 1
-(Cmd) delete b
-KeyError: 'b'
-(Cmd) EOF [or CTRL-D]
+i&gt; .help delete
+usage: delete [name]
+
+delete given parameter (or everything)
+
+positional arguments:
+ name
+
+i&gt; .help show
+usage: show [names [names ...]]
+
+show given parameters
+
+positional arguments:
+ names
+</pre>
+<p>As you can imagine, the help message is provided by the underlying <a class="reference external" href="http://argparse.googlecode.com">argparse</a>
+subparser (there is a subparser for each command). <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> commands accept
+options, flags, varargs, keyword arguments, arguments with defaults,
+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
+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>
+<pre class="literal-block">
+i&gt; sh
+NameError: Ambiguous command 'sh': matching ['showall', 'show']
</pre>
-<p>Internally the <tt class="docutils literal">cmd_interface</tt> builds a <tt class="docutils literal">cmd.Cmd</tt> class and adds
-to it the <tt class="docutils literal">do_</tt> methods corresponding to the commands in the container,
-then it returns a <tt class="docutils literal">cmd.Cmd</tt> instance.</p>
-<p>The <tt class="docutils literal">cmd_interface</tt> is just a proof of concept: it is there so that you
-can study the source code and see an example of integration of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-with a different framework. It may change and even go away in future
-releases of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
</div>
<div class="section" id="the-plac-runner">
<h1><a class="toc-backref" href="#id8">The plac runner</a></h1>
@@ -987,17 +1068,16 @@ plac runner does not eat the traceback.</p>
<p>The runner can also be used to run Python modules in interactive
mode and non-interactive mode. If you put this alias in your bashrc</p>
<blockquote>
-<tt class="docutils literal">alias <span class="pre">plac=&quot;rlwrap</span> plac_runner.py&quot;</tt></blockquote>
+<tt class="docutils literal">alias <span class="pre">plac=&quot;plac_runner.py&quot;</span></tt></blockquote>
<p>(or you define a suitable <tt class="docutils literal">plac.bat</tt> script in Windows) you can
run the <tt class="docutils literal">ishelve2.py</tt> script in interactive mode as
follows:</p>
<pre class="literal-block">
$ plac -i ishelve2.py
-Operating on /home/micheles/conf.shelve. Available commands:
-set
-show
-showall
-delete
+A minimal interface over a shelve object.
+Operating on /home/micheles/conf.shelve.
+.help to see the available commands.
+
i&gt; del
deleting everything
i&gt; set a 1
@@ -1017,8 +1097,8 @@ i&gt; set a 1
setting a=1
i&gt; set b 2
setting b=2
-i&gt; show b
-b = 2
+i&gt; show a
+a = 1
</pre>
<p>Notice that the first line specifies a test database
@@ -1245,8 +1325,196 @@ with interpreter:
loop in a separate process and send commands to it via the Queue
class provided by the <a class="reference external" href="http://docs.python.org/library/multiprocessing.html">multiprocessing</a> module.</p>
</div>
+<div class="section" id="long-running-commands">
+<h1><a class="toc-backref" href="#id11">Long running commands</a></h1>
+<p>As we saw, by default a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> interpreter blocks until
+the command terminates. This is an issue, in the sense that it makes
+the interactive experience quite painful for long running commands. An
+example is better than a thousand words, so consider the following
+fake importer:</p>
+<pre class="literal-block">
+import time
+import plac
+
+class FakeImporter(object):
+ &quot;A fake importer with an import_file command&quot;
+ commands = ['import_file']
+ def __init__(self, dsn):
+ self.dsn = dsn
+ def import_file(self, fname):
+ &quot;Import a file into the database&quot;
+ try:
+ for n in range(10000):
+ time.sleep(.01)
+ if n % 100 == 99:
+ yield 'Imported %d lines' % (n+1)
+ finally:
+ print('closing the file')
+
+if __name__ == '__main__':
+ plac.Interpreter(plac.call(FakeImporter)).interact()
+
+</pre>
+<p>If you run the <tt class="docutils literal">import_file</tt> command, you will have to wait for 200 seconds
+before entering a new command:</p>
+<pre class="literal-block">
+$ python importer1.py dsn
+A fake importer with an import_file command
+i&gt; import_file file1
+Imported 100 lines
+Imported 200 lines
+Imported 300 lines
+... &lt;wait 3+ minutes&gt;
+Imported 10000 lines
+closing the file
+</pre>
+<p>Being unable to enter any other command is quite annoying: in such situation one
+would like to run the long running commands in the background, to keep
+the interface responsive. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides two ways to reach this goal: threads
+and processes.</p>
+</div>
+<div class="section" id="threaded-commands">
+<h1><a class="toc-backref" href="#id12">Threaded commands</a></h1>
+<p>The most familiar way to execute a task in the background (even if not
+necessarily the best way) is to run it into a separated thread. In our
+example it is sufficient to replace the line</p>
+<blockquote>
+<tt class="docutils literal">commands = ['import_file']</tt></blockquote>
+<p>with</p>
+<blockquote>
+<tt class="docutils literal">thcommands = ['import_file']</tt></blockquote>
+<p>to tell to the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> interpreter that the command <tt class="docutils literal">import_file</tt> should be
+run into a separated thread. Here is an example session:</p>
+<pre class="literal-block">
+i&gt; import_file file1
+&lt;ThreadedTask 1 [import_file file1] RUNNING&gt;
+</pre>
+<p>The import task started in a separated thread. You can see the
+progress of the task by using the special command <tt class="docutils literal">.output</tt>:</p>
+<pre class="literal-block">
+i&gt; .output 1
+&lt;ThreadedTask 1 [import_file file1] RUNNING&gt;
+Imported 100 lines
+Imported 200 lines
+</pre>
+<p>If you look after a while, you will get more lines of output:</p>
+<pre class="literal-block">
+i&gt; .output 1
+&lt;ThreadedTask 1 [import_file file1] RUNNING&gt;
+Imported 100 lines
+Imported 200 lines
+Imported 300 lines
+Imported 400 lines
+</pre>
+<p>If you look after a time long enough, the task will be finished:</p>
+<pre class="literal-block">
+i&gt; .output 1
+&lt;ThreadedTask 1 [import_file file1] FINISHED&gt;
+</pre>
+<p>You can launch many tasks one after the other:</p>
+<pre class="literal-block">
+i&gt; import_file file2
+&lt;ThreadedTask 5 [import_file file2] RUNNING&gt;
+i&gt; import_file file3
+&lt;ThreadedTask 6 [import_file file3] RUNNING&gt;
+</pre>
+<p>The <tt class="docutils literal">.list</tt> command displays all the running tasks:</p>
+<pre class="literal-block">
+i&gt; .list
+&lt;ThreadedTask 5 [import_file file2] RUNNING&gt;
+&lt;ThreadedTask 6 [import_file file3] RUNNING&gt;
+</pre>
+<p>It is even possible to kill a task:</p>
+<pre class="literal-block">
+i&gt; .kill 5
+&lt;ThreadedTask 5 [import_file file2] TOBEKILLED&gt;
+# wait a bit ...
+closing the file
+i&gt; .output 5
+&lt;ThreadedTask 5 [import_file file2] KILLED&gt;
+</pre>
+<p>You should notice that since at the Python level it is impossible to kill
+a thread, the <tt class="docutils literal">.kill</tt> commands works by setting the status of the task to
+<tt class="docutils literal">TOBEKILLED</tt>. Internally the generator corresponding to the command
+is executed in the thread and the status is checked at each iteration:
+when the status become <tt class="docutils literal">TOBEKILLED</tt> a <tt class="docutils literal">GeneratorExit</tt> exception is
+raised and the thread terminates (softly, so that the <tt class="docutils literal">finally</tt> clause
+is honored). In our example the generator is yielding
+back control once every 100 iterations, i.e. every two seconds (not much).
+In order to get a responsive interface it is a good idea to yield more
+often, for instance every 10 iterations (i.e. 5 times per second),
+as in the following code:</p>
+<pre class="literal-block">
+import time
+import plac
+
+class FakeImporter(object):
+ &quot;A fake importer with an import_file command&quot;
+ thcommands = ['import_file']
+ def __init__(self, dsn):
+ self.dsn = dsn
+ def import_file(self, fname):
+ &quot;Import a file into the database&quot;
+ try:
+ for n in range(10000):
+ time.sleep(.02)
+ if n % 100 == 99: # every two seconds
+ yield 'Imported %d lines' % (n+1)
+ if n % 10 == 9: # every 0.2 seconds
+ yield # go back and check the TOBEKILLED status
+ finally:
+ print('closing the file')
+
+if __name__ == '__main__':
+ plac.Interpreter(plac.call(FakeImporter)).interact()
+
+</pre>
+</div>
+<div class="section" id="running-commands-as-external-processes">
+<h1><a class="toc-backref" href="#id13">Running commands as external processes</a></h1>
+<p>Threads are not loved much in the Python world and actually most people
+prefer to use processes instead. For this reason <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides the
+option to execute long running commands as external processes. Unfortunately
+the current implementation only works in Unix-like operating systems
+(including Mac OS X) because it relies on fork via the <a class="reference external" href="http://docs.python.org/library/multiprocessing.html">multiprocessing</a>
+module.</p>
+<p>In our example, to enable the feature it is sufficient to replace the line</p>
+<blockquote>
+<tt class="docutils literal">thcommands = ['import_file']</tt></blockquote>
+<p>with</p>
+<blockquote>
+<tt class="docutils literal">mpcommands = ['import_file']</tt>.</blockquote>
+<p>The user experience is exactly the same as with threads and you will not see any
+difference at the user interface level:</p>
+<pre class="literal-block">
+i&gt; import_file file3
+&lt;MPTask 1 [import_file file3] SUBMITTED&gt;
+i&gt; .kill 1
+&lt;MPTask 1 [import_file file3] RUNNING&gt;
+closing the file
+i&gt; .o 1
+&lt;MPTask 1 [import_file file3] KILLED&gt;
+Imported 100 lines
+Imported 200 lines
+i&gt;
+</pre>
+<p>Still, using processes is quite different than using threads: in
+particular, when using processes you can only yield pickleable values
+and you cannot re-raise an exception first raised in a different
+process, because traceback objects are not pickleable. Moreover,
+you cannot rely on automatic sharing of your objects.</p>
+<p>On the plus side, when using processes you do not need to worry about
+killing a command: they are killed immediately using a SIGTERM signal,
+and there is not a <tt class="docutils literal">TOBEKILLED</tt> mechanism. Moreover, the killing is
+guaranteed to be soft: internally a command receiving a SIGTERM raises
+a <tt class="docutils literal">TerminatedProcess</tt> exception which is trapped in the generator
+loop, so that the command is closed properly.</p>
+<p>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.</p>
+</div>
<div class="section" id="summary">
-<h1><a class="toc-backref" href="#id11">Summary</a></h1>
+<h1><a class="toc-backref" href="#id14">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
@@ -1261,12 +1529,15 @@ rules are quite simple:</p>
<li>for testing call the <tt class="docutils literal">Interpreter.check</tt> method in the appropriate context
or use the <tt class="docutils literal">Interpreter.doctest</tt> feature;</li>
<li>if you need to go at a lower level, you may need to call the
-<tt class="docutils literal">Interpreter.send</tt> method.</li>
+<tt class="docutils literal">Interpreter.send</tt> method which returns a (finished) <tt class="docutils literal">Task</tt> object.</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>
</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="#id12">Appendix: custom annotation objects</a></h1>
+<h1><a class="toc-backref" href="#id15">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>
@@ -1320,10 +1591,6 @@ Using custom annotation objects you could do advanced things like extracting the
annotations from a configuration file or from a database, but I expect such
use cases to be quite rare: the default mechanism should work
pretty well for most users.</p>
-<!-- If the script is invoked with no arguments, the default help is
-invoked: this is done by invoking the argparse_ low-level method
-``.format_help``. In other words, the recognition of the couple
-``-h/- -help`` is disabled, but the original help feature is still there. -->
</div>
</div>
</body>
diff --git a/plac/doc/plac_adv.pdf b/plac/doc/plac_adv.pdf
index 0061f67..7abe4f7 100644
--- a/plac/doc/plac_adv.pdf
+++ b/plac/doc/plac_adv.pdf
@@ -7,8 +7,7 @@
/F2 3 0 R
/F3 4 0 R
/F4 8 0 R
- /F5 9 0 R
- /F6 22 0 R >>
+ /F5 9 0 R >>
endobj
% 'F1': class PDFType1Font
2 0 obj
@@ -106,15 +105,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 42 0 R
+ /Dest [ 45 0 R
/XYZ
62.69291
- 302.0236
+ 236.0236
0 ]
/Rect [ 62.69291
- 518.5936
+ 506.5936
121.0229
- 530.5936 ]
+ 518.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -124,15 +123,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 42 0 R
+ /Dest [ 45 0 R
/XYZ
62.69291
- 302.0236
+ 236.0236
0 ]
/Rect [ 527.0227
- 518.5936
+ 506.5936
532.5827
- 530.5936 ]
+ 518.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -142,15 +141,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 44 0 R
+ /Dest [ 49 0 R
/XYZ
62.69291
- 765.0236
+ 717.0236
0 ]
/Rect [ 62.69291
- 500.5936
+ 488.5936
249.4129
- 512.5936 ]
+ 500.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -160,15 +159,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 44 0 R
+ /Dest [ 49 0 R
/XYZ
62.69291
- 765.0236
+ 717.0236
0 ]
/Rect [ 527.0227
- 500.5936
+ 488.5936
532.5827
- 512.5936 ]
+ 500.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -178,15 +177,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 47 0 R
+ /Dest [ 51 0 R
/XYZ
62.69291
- 306.6236
+ 259.4236
0 ]
/Rect [ 62.69291
- 482.5936
+ 470.5936
184.9529
- 494.5936 ]
+ 482.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -196,15 +195,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 47 0 R
+ /Dest [ 51 0 R
/XYZ
62.69291
- 306.6236
+ 259.4236
0 ]
/Rect [ 527.0227
- 482.5936
+ 470.5936
532.5827
- 494.5936 ]
+ 482.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -214,15 +213,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 48 0 R
+ /Dest [ 52 0 R
/XYZ
62.69291
- 297.8236
+ 222.6236
0 ]
/Rect [ 62.69291
- 464.5936
+ 452.5936
134.4029
- 476.5936 ]
+ 464.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -232,15 +231,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 48 0 R
+ /Dest [ 52 0 R
/XYZ
62.69291
- 297.8236
+ 222.6236
0 ]
/Rect [ 527.0227
- 464.5936
+ 452.5936
532.5827
- 476.5936 ]
+ 464.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -250,15 +249,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 61 0 R
+ /Dest [ 66 0 R
/XYZ
62.69291
- 681.0236
+ 588.7757
0 ]
/Rect [ 62.69291
- 446.5936
+ 434.5936
148.2829
- 458.5936 ]
+ 446.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -268,15 +267,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 61 0 R
+ /Dest [ 66 0 R
/XYZ
62.69291
- 681.0236
+ 588.7757
0 ]
/Rect [ 527.0227
- 446.5936
+ 434.5936
532.5827
- 458.5936 ]
+ 446.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -286,15 +285,15 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 65 0 R
+ /Dest [ 71 0 R
/XYZ
62.69291
- 765.0236
+ 671.8236
0 ]
/Rect [ 62.69291
- 428.5936
- 182.7129
- 440.5936 ]
+ 416.5936
+ 201.0529
+ 428.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -304,350 +303,419 @@ endobj
0
0 ]
/Contents ()
- /Dest [ 65 0 R
+ /Dest [ 71 0 R
/XYZ
62.69291
- 765.0236
+ 671.8236
0 ]
/Rect [ 527.0227
- 428.5936
+ 416.5936
532.5827
- 440.5936 ]
+ 428.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'F6': class PDFType1Font
-22 0 obj
-% Font Courier-Bold
-<< /BaseFont /Courier-Bold
- /Encoding /WinAnsiEncoding
- /Name /F6
- /Subtype /Type1
- /Type /Font >>
-endobj
% 'Annot.NUMBER16': class LinkAnnotation
-23 0 obj
+22 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 85 0 R
+ /Dest [ 77 0 R
/XYZ
62.69291
- 765.0236
+ 562.2269
0 ]
/Rect [ 62.69291
- 410.5936
- 135.0429
- 422.5936 ]
+ 398.5936
+ 144.3729
+ 410.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER17': class LinkAnnotation
-24 0 obj
+23 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 85 0 R
+ /Dest [ 77 0 R
/XYZ
62.69291
- 765.0236
+ 562.2269
0 ]
/Rect [ 527.0227
- 410.5936
+ 398.5936
532.5827
- 422.5936 ]
+ 410.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER18': class LinkAnnotation
-25 0 obj
+24 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 85 0 R
+ /Dest [ 93 0 R
/XYZ
62.69291
- 181.6236
+ 715.8236
0 ]
/Rect [ 62.69291
- 392.5936
+ 380.5936
137.7129
- 404.5936 ]
+ 392.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER19': class LinkAnnotation
-26 0 obj
+25 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 85 0 R
+ /Dest [ 93 0 R
/XYZ
62.69291
- 181.6236
+ 715.8236
0 ]
- /Rect [ 527.0227
- 392.5936
+ /Rect [ 521.4627
+ 380.5936
532.5827
- 404.5936 ]
+ 392.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER20': class LinkAnnotation
-27 0 obj
+26 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 89 0 R
+ /Dest [ 96 0 R
/XYZ
62.69291
- 371.4236
+ 242.2236
0 ]
/Rect [ 62.69291
- 374.5936
+ 362.5936
193.8529
- 386.5936 ]
+ 374.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER21': class LinkAnnotation
-28 0 obj
+27 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 89 0 R
+ /Dest [ 96 0 R
/XYZ
62.69291
- 371.4236
+ 242.2236
0 ]
/Rect [ 521.4627
- 374.5936
+ 362.5936
532.5827
- 386.5936 ]
+ 374.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER22': class LinkAnnotation
-29 0 obj
+28 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 92 0 R
+ /Dest [ 99 0 R
/XYZ
62.69291
- 384.6236
+ 253.4236
0 ]
/Rect [ 62.69291
- 356.5936
+ 344.5936
201.6029
- 368.5936 ]
+ 356.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER23': class LinkAnnotation
-30 0 obj
+29 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 92 0 R
+ /Dest [ 99 0 R
/XYZ
62.69291
- 384.6236
+ 253.4236
0 ]
/Rect [ 521.4627
- 356.5936
+ 344.5936
532.5827
- 368.5936 ]
+ 356.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER24': class LinkAnnotation
-31 0 obj
+30 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 95 0 R
+ /Dest [ 104 0 R
/XYZ
62.69291
- 241.4236
+ 765.0236
0 ]
/Rect [ 62.69291
- 338.5936
- 108.2629
- 350.5936 ]
+ 326.5936
+ 182.7029
+ 338.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER25': class LinkAnnotation
-32 0 obj
+31 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 95 0 R
+ /Dest [ 104 0 R
/XYZ
62.69291
- 241.4236
+ 765.0236
0 ]
/Rect [ 521.4627
- 338.5936
+ 326.5936
532.5827
- 350.5936 ]
+ 338.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER26': class LinkAnnotation
-33 0 obj
+32 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 97 0 R
+ /Dest [ 104 0 R
/XYZ
62.69291
- 687.0236
+ 223.6236
0 ]
/Rect [ 62.69291
- 320.5936
- 241.6029
- 332.5936 ]
+ 308.5936
+ 163.2729
+ 320.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Annot.NUMBER27': class LinkAnnotation
-34 0 obj
+33 0 obj
<< /Border [ 0
0
0 ]
/Contents ()
- /Dest [ 97 0 R
+ /Dest [ 104 0 R
/XYZ
62.69291
- 687.0236
+ 223.6236
0 ]
/Rect [ 521.4627
- 320.5936
+ 308.5936
532.5827
- 332.5936 ]
+ 320.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER28': class PDFDictionary
+% 'Annot.NUMBER28': class LinkAnnotation
+34 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 109 0 R
+ /XYZ
+ 62.69291
+ 419.8236
+ 0 ]
+ /Rect [ 62.69291
+ 290.5936
+ 266.0929
+ 302.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER29': class LinkAnnotation
35 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+<< /Border [ 0
0
0 ]
- /Rect [ 185.4471
- 266.5936
- 207.1062
- 278.5936 ]
+ /Contents ()
+ /Dest [ 109 0 R
+ /XYZ
+ 62.69291
+ 419.8236
+ 0 ]
+ /Rect [ 521.4627
+ 290.5936
+ 532.5827
+ 302.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER29': class PDFDictionary
+% 'Annot.NUMBER30': class LinkAnnotation
36 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
+<< /Border [ 0
0
0 ]
- /Rect [ 177.6784
+ /Contents ()
+ /Dest [ 112 0 R
+ /XYZ
+ 62.69291
+ 621.0236
+ 0 ]
+ /Rect [ 62.69291
+ 272.5936
+ 108.2629
+ 284.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER31': class LinkAnnotation
+37 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 112 0 R
+ /XYZ
+ 62.69291
+ 621.0236
+ 0 ]
+ /Rect [ 521.4627
+ 272.5936
+ 532.5827
+ 284.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER32': class LinkAnnotation
+38 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 112 0 R
+ /XYZ
+ 62.69291
+ 354.0236
+ 0 ]
+ /Rect [ 62.69291
254.5936
- 199.6123
+ 241.6029
266.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER30': class PDFDictionary
-37 0 obj
+% 'Annot.NUMBER33': class LinkAnnotation
+39 0 obj
+<< /Border [ 0
+ 0
+ 0 ]
+ /Contents ()
+ /Dest [ 112 0 R
+ /XYZ
+ 62.69291
+ 354.0236
+ 0 ]
+ /Rect [ 521.4627
+ 254.5936
+ 532.5827
+ 266.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER34': class PDFDictionary
+40 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
+ /Rect [ 185.4471
200.5936
- 83.81291
+ 207.1062
212.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER31': class PDFDictionary
-38 0 obj
+% 'Annot.NUMBER35': class PDFDictionary
+41 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 278.4678
- 182.5936
- 300.0328
- 194.5936 ]
+ /Rect [ 177.6784
+ 188.5936
+ 199.6123
+ 200.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER32': class PDFDictionary
-39 0 obj
+% 'Annot.NUMBER36': class PDFDictionary
+42 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 117.3573
- 170.5936
- 138.5845
- 182.5936 ]
+ /Rect [ 62.69291
+ 134.5936
+ 83.81291
+ 146.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER33': class PDFDictionary
-40 0 obj
+% 'Annot.NUMBER37': class PDFDictionary
+43 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://twill.idyll.org/) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 83.20457
- 128.5936
- 105.3762
- 140.5936 ]
+ /Rect [ 278.4678
+ 116.5936
+ 300.0328
+ 128.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER34': class PDFDictionary
-41 0 obj
+% 'Annot.NUMBER38': class PDFDictionary
+44 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 128.6679
- 128.5936
- 147.0079
- 140.5936 ]
+ /Rect [ 117.3573
+ 104.5936
+ 138.5845
+ 116.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page1': class PDFPage
-42 0 obj
+45 0 obj
% Page dictionary
<< /Annots [ 5 0 R
6 0 R
@@ -664,6 +732,7 @@ endobj
19 0 R
20 0 R
21 0 R
+ 22 0 R
23 0 R
24 0 R
25 0 R
@@ -682,13 +751,16 @@ endobj
38 0 R
39 0 R
40 0 R
- 41 0 R ]
- /Contents 115 0 R
+ 41 0 R
+ 42 0 R
+ 43 0 R
+ 44 0 R ]
+ /Contents 133 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -699,8 +771,38 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER35': class PDFDictionary
-43 0 obj
+% 'Annot.NUMBER39': class PDFDictionary
+46 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER40': class PDFDictionary
+47 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER41': class PDFDictionary
+48 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
@@ -708,22 +810,24 @@ endobj
0
0 ]
/Rect [ 62.69291
- 633.5936
+ 585.5936
157.1829
- 645.5936 ]
+ 597.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page2': class PDFPage
-44 0 obj
+49 0 obj
% Page dictionary
-<< /Annots [ 43 0 R ]
- /Contents 116 0 R
+<< /Annots [ 46 0 R
+ 47 0 R
+ 48 0 R ]
+ /Contents 134 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -734,47 +838,31 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER36': class PDFDictionary
-45 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://freshmeat.net/projects/rlwrap/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 223.0988
- 711.3936
- 255.7481
- 723.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER37': class PDFDictionary
-46 0 obj
+% 'Annot.NUMBER42': class PDFDictionary
+50 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://freshmeat.net/projects/rlwrap/) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 390.8554
- 711.3936
- 423.5047
- 723.3936 ]
+ /Rect [ 383.9329
+ 304.9936
+ 405.0529
+ 316.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page3': class PDFPage
-47 0 obj
+51 0 obj
% Page dictionary
-<< /Annots [ 45 0 R
- 46 0 R ]
- /Contents 117 0 R
+<< /Annots [ 50 0 R ]
+ /Contents 135 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -786,14 +874,14 @@ endobj
/Type /Page >>
endobj
% 'Page4': class PDFPage
-48 0 obj
+52 0 obj
% Page dictionary
-<< /Contents 118 0 R
+<< /Contents 136 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -804,8 +892,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER38': class PDFDictionary
-49 0 obj
+% 'Annot.NUMBER43': class PDFDictionary
+53 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -813,14 +901,14 @@ endobj
0
0 ]
/Rect [ 370.6785
- 651.3936
+ 579.3936
392.4956
- 663.3936 ]
+ 591.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER39': class PDFDictionary
-50 0 obj
+% 'Annot.NUMBER44': class PDFDictionary
+54 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -828,14 +916,14 @@ endobj
0
0 ]
/Rect [ 455.8742
- 651.3936
+ 579.3936
477.6913
- 663.3936 ]
+ 591.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER40': class PDFDictionary
-51 0 obj
+% 'Annot.NUMBER45': class PDFDictionary
+55 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -843,14 +931,14 @@ endobj
0
0 ]
/Rect [ 185.9351
- 454.6093
+ 364.6093
207.4695
- 466.6093 ]
+ 376.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER41': class PDFDictionary
-52 0 obj
+% 'Annot.NUMBER46': class PDFDictionary
+56 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -858,14 +946,14 @@ endobj
0
0 ]
/Rect [ 369.8905
- 454.6093
+ 364.6093
396.425
- 466.6093 ]
+ 376.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER42': class PDFDictionary
-53 0 obj
+% 'Annot.NUMBER47': class PDFDictionary
+57 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -873,14 +961,14 @@ endobj
0
0 ]
/Rect [ 408.8916
- 442.6093
+ 352.6093
427.2316
- 454.6093 ]
+ 364.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER43': class PDFDictionary
-54 0 obj
+% 'Annot.NUMBER48': class PDFDictionary
+58 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
@@ -888,75 +976,91 @@ endobj
0
0 ]
/Rect [ 62.69291
- 430.6093
+ 340.6093
86.03291
- 442.6093 ]
+ 352.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER44': class PDFDictionary
-55 0 obj
+% 'Annot.NUMBER49': class PDFDictionary
+59 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 91.65423
- 430.6093
- 112.8355
- 442.6093 ]
+ /Rect [ 92.4689
+ 340.6093
+ 114.4649
+ 352.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER45': class PDFDictionary
-56 0 obj
+% 'Annot.NUMBER50': class PDFDictionary
+60 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/shlex.html) >>
/Border [ 0
0
0 ]
- /Rect [ 223.1366
- 418.6093
- 250.7972
- 430.6093 ]
+ /Rect [ 143.2929
+ 328.6093
+ 169.4129
+ 340.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER46': class PDFDictionary
-57 0 obj
+% 'Annot.NUMBER51': class PDFDictionary
+61 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 201.0929
- 322.6093
- 222.2129
- 334.6093 ]
+ /Rect [ 129.6923
+ 256.6093
+ 151.4655
+ 268.6093 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER52': class PDFDictionary
+62 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 173.8529
+ 244.6093
+ 194.9729
+ 256.6093 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page5': class PDFPage
-58 0 obj
+63 0 obj
% Page dictionary
-<< /Annots [ 49 0 R
- 50 0 R
- 51 0 R
- 52 0 R
- 53 0 R
+<< /Annots [ 53 0 R
54 0 R
55 0 R
56 0 R
- 57 0 R ]
- /Contents 119 0 R
+ 57 0 R
+ 58 0 R
+ 59 0 R
+ 60 0 R
+ 61 0 R
+ 62 0 R ]
+ /Contents 137 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -967,8 +1071,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER47': class PDFDictionary
-59 0 obj
+% 'Annot.NUMBER53': class PDFDictionary
+64 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -976,14 +1080,14 @@ endobj
0
0 ]
/Rect [ 460.388
- 633.5936
+ 541.3457
482.0127
- 645.5936 ]
+ 553.3457 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER48': class PDFDictionary
-60 0 obj
+% 'Annot.NUMBER54': class PDFDictionary
+65 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -991,23 +1095,23 @@ endobj
0
0 ]
/Rect [ 95.32996
- 567.5936
+ 475.3457
116.857
- 579.5936 ]
+ 487.3457 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page6': class PDFPage
-61 0 obj
+66 0 obj
% Page dictionary
-<< /Annots [ 59 0 R
- 60 0 R ]
- /Contents 120 0 R
+<< /Annots [ 64 0 R
+ 65 0 R ]
+ /Contents 138 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1018,8 +1122,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER49': class PDFDictionary
-62 0 obj
+% 'Annot.NUMBER55': class PDFDictionary
+67 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://micheles.googlecode.com/hg/plac/doc/plac.html) >>
@@ -1027,29 +1131,29 @@ endobj
0
0 ]
/Rect [ 316.3528
- 729.5936
+ 636.3936
409.2453
- 741.5936 ]
+ 648.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER50': class PDFDictionary
-63 0 obj
+% 'Annot.NUMBER56': class PDFDictionary
+68 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 441.0467
- 717.5936
- 463.6507
- 729.5936 ]
+ /Rect [ 419.1694
+ 624.3936
+ 440.4061
+ 636.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER51': class PDFDictionary
-64 0 obj
+% 'Annot.NUMBER57': class PDFDictionary
+69 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1057,24 +1161,40 @@ endobj
0
0 ]
/Rect [ 62.69291
- 687.5936
+ 594.3936
84.70395
- 699.5936 ]
+ 606.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER58': class PDFDictionary
+70 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 293.2359
+ 516.3936
+ 316.4697
+ 528.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page7': class PDFPage
-65 0 obj
+71 0 obj
% Page dictionary
-<< /Annots [ 62 0 R
- 63 0 R
- 64 0 R ]
- /Contents 121 0 R
+<< /Annots [ 67 0 R
+ 68 0 R
+ 69 0 R
+ 70 0 R ]
+ /Contents 139 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1085,8 +1205,8 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER52': class PDFDictionary
-66 0 obj
+% 'Annot.NUMBER59': class PDFDictionary
+72 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1094,54 +1214,54 @@ endobj
0
0 ]
/Rect [ 431.7904
- 639.3936
+ 531.3936
453.7245
- 651.3936 ]
+ 543.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER53': class PDFDictionary
-67 0 obj
+% 'Annot.NUMBER60': class PDFDictionary
+73 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://freshmeat.net/projects/rlwrap/) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 465.1151
- 226.1936
- 495.7839
- 238.1936 ]
+ /Rect [ 255.5885
+ 501.3936
+ 278.2757
+ 513.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER54': class PDFDictionary
-68 0 obj
+% 'Annot.NUMBER61': class PDFDictionary
+74 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://docs.python.org/library/cmd.html) >>
/Border [ 0
0
0 ]
- /Rect [ 206.6529
- 184.1936
- 227.7729
- 196.1936 ]
+ /Rect [ 513.6927
+ 477.3936
+ 532.1846
+ 489.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page8': class PDFPage
-69 0 obj
+75 0 obj
% Page dictionary
-<< /Annots [ 66 0 R
- 67 0 R
- 68 0 R ]
- /Contents 122 0 R
+<< /Annots [ 72 0 R
+ 73 0 R
+ 74 0 R ]
+ /Contents 140 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1152,203 +1272,254 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER55': class PDFDictionary
-70 0 obj
+% 'Annot.NUMBER62': class PDFDictionary
+76 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 163.0507
- 729.5936
- 185.1123
- 741.5936 ]
+ /Rect [ 179.0529
+ 526.7969
+ 201.1953
+ 538.7969 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER56': class PDFDictionary
-71 0 obj
+% 'Page9': class PDFPage
+77 0 obj
+% Page dictionary
+<< /Annots [ 76 0 R ]
+ /Contents 141 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.NUMBER63': class PDFDictionary
+78 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://freshmeat.net/projects/rlwrap/) >>
/Border [ 0
0
0 ]
- /Rect [ 514.6827
- 717.5936
- 533.0227
- 729.5936 ]
+ /Rect [ 377.8504
+ 621.3936
+ 409.861
+ 633.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER57': class PDFDictionary
-72 0 obj
+% 'Annot.NUMBER64': class PDFDictionary
+79 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 127.0179
- 705.5936
- 148.2762
- 717.5936 ]
+ /Rect [ 242.4466
+ 609.3936
+ 263.7922
+ 621.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER58': class PDFDictionary
-73 0 obj
+% 'Annot.NUMBER65': class PDFDictionary
+80 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 197.4628
- 705.5936
- 219.2712
- 717.5936 ]
+ /Rect [ 62.69291
+ 585.3936
+ 85.67624
+ 597.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER59': class PDFDictionary
-74 0 obj
+% 'Annot.NUMBER66': class PDFDictionary
+81 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 693.5936
- 81.58291
- 705.5936 ]
+ /Rect [ 211.9062
+ 573.3936
+ 236.7428
+ 585.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER60': class PDFDictionary
-75 0 obj
+% 'Annot.NUMBER67': class PDFDictionary
+82 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://docs.python.org/library/cmd.html) >>
/Border [ 0
0
0 ]
- /Rect [ 158.1049
- 693.5936
- 180.2789
- 705.5936 ]
+ /Rect [ 366.9454
+ 543.3936
+ 388.8033
+ 555.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER61': class PDFDictionary
-76 0 obj
+% 'Annot.NUMBER68': class PDFDictionary
+83 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://freshmeat.net/projects/rlwrap/) >>
+ /URI (http://docs.python.org/library/cmd.html) >>
/Border [ 0
0
0 ]
- /Rect [ 483.7387
- 693.5936
- 510.9587
- 705.5936 ]
+ /Rect [ 170.8855
+ 531.3936
+ 189.7755
+ 543.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER62': class PDFDictionary
-77 0 obj
+% 'Annot.NUMBER69': class PDFDictionary
+84 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 244.5366
- 681.5936
- 266.7822
- 693.5936 ]
+ /Rect [ 390.8027
+ 190.1936
+ 435.0027
+ 202.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER63': class PDFDictionary
-78 0 obj
+% 'Annot.NUMBER70': class PDFDictionary
+85 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 287.3678
- 681.5936
- 306.2578
- 693.5936 ]
+ /Rect [ 213.451
+ 178.1936
+ 237.5255
+ 190.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER64': class PDFDictionary
-79 0 obj
+% 'Annot.NUMBER71': class PDFDictionary
+86 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
+ /URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 452.6357
- 669.5936
- 474.6447
- 681.5936 ]
+ /Rect [ 114.9429
+ 154.1936
+ 157.1829
+ 166.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER65': class PDFDictionary
-80 0 obj
+% 'Annot.NUMBER72': class PDFDictionary
+87 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 494.4437
- 669.5936
- 512.7837
- 681.5936 ]
+ /Rect [ 385.0429
+ 154.1936
+ 403.3829
+ 166.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER66': class PDFDictionary
-81 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 [ 193.5645
- 209.1936
- 215.6228
- 221.1936 ]
+ /Rect [ 514.2427
+ 124.1936
+ 532.3264
+ 136.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER67': class PDFDictionary
-82 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 [ 114.3829
- 197.1936
- 132.7229
- 209.1936 ]
+ /Rect [ 256.6729
+ 94.19362
+ 277.7929
+ 106.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER68': class PDFDictionary
-83 0 obj
+% '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
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1356,14 +1527,14 @@ endobj
0
0 ]
/Rect [ 149.5469
- 146.1936
+ 680.3936
172.1982
- 158.1936 ]
+ 692.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER69': class PDFDictionary
-84 0 obj
+% 'Annot.NUMBER76': class PDFDictionary
+92 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/distutils/) >>
@@ -1371,36 +1542,23 @@ endobj
0
0 ]
/Rect [ 224.3178
- 134.1936
+ 668.3936
260.8853
- 146.1936 ]
+ 680.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page9': class PDFPage
-85 0 obj
+% 'Page11': class PDFPage
+93 0 obj
% Page dictionary
-<< /Annots [ 70 0 R
- 71 0 R
- 72 0 R
- 73 0 R
- 74 0 R
- 75 0 R
- 76 0 R
- 77 0 R
- 78 0 R
- 79 0 R
- 80 0 R
- 81 0 R
- 82 0 R
- 83 0 R
- 84 0 R ]
- /Contents 123 0 R
+<< /Annots [ 91 0 R
+ 92 0 R ]
+ /Contents 143 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1411,15 +1569,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page10': class PDFPage
-86 0 obj
+% 'Annot.NUMBER77': class PDFDictionary
+94 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://argparse.googlecode.com) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 381.1529
+ 615.3936
+ 423.3929
+ 627.3936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER78': class PDFDictionary
+95 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 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page12': class PDFPage
+96 0 obj
% Page dictionary
-<< /Contents 124 0 R
+<< /Annots [ 94 0 R
+ 95 0 R ]
+ /Contents 144 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1430,47 +1620,85 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER70': class PDFDictionary
-87 0 obj
+% 'Page13': class PDFPage
+97 0 obj
+% Page dictionary
+<< /Contents 145 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.NUMBER79': class PDFDictionary
+98 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://argparse.googlecode.com) >>
+ /URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 381.1529
- 744.5936
- 423.3929
- 756.5936 ]
+ /Rect [ 182.479
+ 217.9936
+ 203.7662
+ 229.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER71': class PDFDictionary
-88 0 obj
+% 'Page14': class PDFPage
+99 0 obj
+% Page dictionary
+<< /Annots [ 98 0 R ]
+ /Contents 146 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.NUMBER80': class PDFDictionary
+100 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://docs.python.org/library/multiprocessing.html) >>
/Border [ 0
0
0 ]
- /Rect [ 62.69291
- 335.9936
- 84.72012
- 347.9936 ]
+ /Rect [ 295.0229
+ 134.9936
+ 367.2629
+ 146.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page11': class PDFPage
-89 0 obj
+% 'Page15': class PDFPage
+101 0 obj
% Page dictionary
-<< /Annots [ 87 0 R
- 88 0 R ]
- /Contents 125 0 R
+<< /Annots [ 100 0 R ]
+ /Contents 147 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1481,15 +1709,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page12': class PDFPage
-90 0 obj
+% 'Annot.NUMBER81': class PDFDictionary
+102 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 179.1295
+ 729.5936
+ 201.6839
+ 741.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER82': class PDFDictionary
+103 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 414.8874
+ 251.1936
+ 436.9487
+ 263.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page16': class PDFPage
+104 0 obj
% Page dictionary
-<< /Contents 126 0 R
+<< /Annots [ 102 0 R
+ 103 0 R ]
+ /Contents 148 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1500,31 +1760,31 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER72': class PDFDictionary
-91 0 obj
+% 'Annot.NUMBER83': class PDFDictionary
+105 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
/Border [ 0
0
0 ]
- /Rect [ 182.479
- 349.1936
- 203.7662
- 361.1936 ]
+ /Rect [ 122.7054
+ 756.5936
+ 145.2085
+ 768.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page13': class PDFPage
-92 0 obj
+% 'Page17': class PDFPage
+106 0 obj
% Page dictionary
-<< /Annots [ 91 0 R ]
- /Contents 127 0 R
+<< /Annots [ 105 0 R ]
+ /Contents 149 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1535,47 +1795,47 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER73': class PDFDictionary
-93 0 obj
+% 'Annot.NUMBER84': class PDFDictionary
+107 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
- 256.9936
- 367.2629
- 268.9936 ]
+ /Rect [ 183.3657
+ 372.3936
+ 207.8364
+ 384.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER74': class PDFDictionary
-94 0 obj
+% 'Annot.NUMBER85': class PDFDictionary
+108 0 obj
<< /A << /S /URI
/Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
+ /URI (http://docs.python.org/library/multiprocessing.html) >>
/Border [ 0
0
0 ]
- /Rect [ 91.57623
- 205.9936
- 114.8995
- 217.9936 ]
+ /Rect [ 254.9929
+ 348.3936
+ 327.2329
+ 360.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page14': class PDFPage
-95 0 obj
+% 'Page18': class PDFPage
+109 0 obj
% Page dictionary
-<< /Annots [ 93 0 R
- 94 0 R ]
- /Contents 128 0 R
+<< /Annots [ 107 0 R
+ 108 0 R ]
+ /Contents 150 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1586,8 +1846,23 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER75': class PDFDictionary
-96 0 obj
+% 'Annot.NUMBER86': class PDFDictionary
+110 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 91.57623
+ 585.5936
+ 114.8995
+ 597.5936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER87': class PDFDictionary
+111 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1595,22 +1870,23 @@ endobj
0
0 ]
/Rect [ 106.6216
- 651.5936
+ 318.5936
128.3202
- 663.5936 ]
+ 330.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page15': class PDFPage
-97 0 obj
+% 'Page19': class PDFPage
+112 0 obj
% Page dictionary
-<< /Annots [ 96 0 R ]
- /Contents 129 0 R
+<< /Annots [ 110 0 R
+ 111 0 R ]
+ /Contents 151 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1621,15 +1897,15 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'Page16': class PDFPage
-98 0 obj
+% 'Page20': class PDFPage
+113 0 obj
% Page dictionary
-<< /Contents 130 0 R
+<< /Contents 152 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 114 0 R
+ /Parent 132 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1640,205 +1916,245 @@ endobj
/Trans << >>
/Type /Page >>
endobj
-% 'R99': class PDFCatalog
-99 0 obj
+% 'R114': class PDFCatalog
+114 0 obj
% Document Root
-<< /Outlines 101 0 R
- /PageLabels 131 0 R
+<< /Outlines 116 0 R
+ /PageLabels 153 0 R
/PageMode /UseNone
- /Pages 114 0 R
+ /Pages 132 0 R
/Type /Catalog >>
endobj
-% 'R100': class PDFInfo
-100 0 obj
+% 'R115': class PDFInfo
+115 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100621062523-01'00')
+ /CreationDate (D:20100710083035-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
- /Title (Testing and scripting your applications with plac) >>
+ /Title (Advanced usages of plac) >>
endobj
-% 'R101': class PDFOutlines
-101 0 obj
-<< /Count 12
- /First 102 0 R
- /Last 113 0 R
+% 'R116': class PDFOutlines
+116 0 obj
+<< /Count 15
+ /First 117 0 R
+ /Last 131 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-102 0 obj
-<< /Dest [ 42 0 R
+117 0 obj
+<< /Dest [ 45 0 R
/XYZ
62.69291
- 302.0236
+ 236.0236
0 ]
- /Next 103 0 R
- /Parent 101 0 R
+ /Next 118 0 R
+ /Parent 116 0 R
/Title (Introduction) >>
endobj
% 'Outline.1': class OutlineEntryObject
-103 0 obj
-<< /Dest [ 44 0 R
+118 0 obj
+<< /Dest [ 49 0 R
/XYZ
62.69291
- 765.0236
+ 717.0236
0 ]
- /Next 104 0 R
- /Parent 101 0 R
- /Prev 102 0 R
+ /Next 119 0 R
+ /Parent 116 0 R
+ /Prev 117 0 R
/Title (From scripts to interactive applications) >>
endobj
% 'Outline.2': class OutlineEntryObject
-104 0 obj
-<< /Dest [ 47 0 R
+119 0 obj
+<< /Dest [ 51 0 R
/XYZ
62.69291
- 306.6236
+ 259.4236
0 ]
- /Next 105 0 R
- /Parent 101 0 R
- /Prev 103 0 R
+ /Next 120 0 R
+ /Parent 116 0 R
+ /Prev 118 0 R
/Title (Testing a plac application) >>
endobj
% 'Outline.3': class OutlineEntryObject
-105 0 obj
-<< /Dest [ 48 0 R
+120 0 obj
+<< /Dest [ 52 0 R
/XYZ
62.69291
- 297.8236
+ 222.6236
0 ]
- /Next 106 0 R
- /Parent 101 0 R
- /Prev 104 0 R
+ /Next 121 0 R
+ /Parent 116 0 R
+ /Prev 119 0 R
/Title (Plac easy tests) >>
endobj
% 'Outline.4': class OutlineEntryObject
-106 0 obj
-<< /Dest [ 61 0 R
+121 0 obj
+<< /Dest [ 66 0 R
/XYZ
62.69291
- 681.0236
+ 588.7757
0 ]
- /Next 107 0 R
- /Parent 101 0 R
- /Prev 105 0 R
+ /Next 122 0 R
+ /Parent 116 0 R
+ /Prev 120 0 R
/Title (Plac batch scripts) >>
endobj
% 'Outline.5': class OutlineEntryObject
-107 0 obj
-<< /Dest [ 65 0 R
+122 0 obj
+<< /Dest [ 71 0 R
/XYZ
62.69291
- 765.0236
+ 671.8236
0 ]
- /Next 108 0 R
- /Parent 101 0 R
- /Prev 106 0 R
- /Title (Containers of commands) >>
+ /Next 123 0 R
+ /Parent 116 0 R
+ /Prev 121 0 R
+ /Title (Implementing subcommands) >>
endobj
% 'Outline.6': class OutlineEntryObject
-108 0 obj
-<< /Dest [ 85 0 R
+123 0 obj
+<< /Dest [ 77 0 R
/XYZ
62.69291
- 765.0236
+ 562.2269
0 ]
- /Next 109 0 R
- /Parent 101 0 R
- /Prev 107 0 R
- /Title (For cmd lovers) >>
+ /Next 124 0 R
+ /Parent 116 0 R
+ /Prev 122 0 R
+ /Title (Readline support) >>
endobj
% 'Outline.7': class OutlineEntryObject
-109 0 obj
-<< /Dest [ 85 0 R
+124 0 obj
+<< /Dest [ 93 0 R
/XYZ
62.69291
- 181.6236
+ 715.8236
0 ]
- /Next 110 0 R
- /Parent 101 0 R
- /Prev 108 0 R
+ /Next 125 0 R
+ /Parent 116 0 R
+ /Prev 123 0 R
/Title (The plac runner) >>
endobj
% 'Outline.8': class OutlineEntryObject
-110 0 obj
-<< /Dest [ 89 0 R
+125 0 obj
+<< /Dest [ 96 0 R
/XYZ
62.69291
- 371.4236
+ 242.2236
0 ]
- /Next 111 0 R
- /Parent 101 0 R
- /Prev 109 0 R
+ /Next 126 0 R
+ /Parent 116 0 R
+ /Prev 124 0 R
/Title (A non class-based example) >>
endobj
% 'Outline.9': class OutlineEntryObject
-111 0 obj
-<< /Dest [ 92 0 R
+126 0 obj
+<< /Dest [ 99 0 R
/XYZ
62.69291
- 384.6236
+ 253.4236
0 ]
- /Next 112 0 R
- /Parent 101 0 R
- /Prev 110 0 R
+ /Next 127 0 R
+ /Parent 116 0 R
+ /Prev 125 0 R
/Title (Writing your own plac runner) >>
endobj
% 'Outline.10': class OutlineEntryObject
-112 0 obj
-<< /Dest [ 95 0 R
+127 0 obj
+<< /Dest [ 104 0 R
/XYZ
62.69291
- 241.4236
+ 765.0236
0 ]
- /Next 113 0 R
- /Parent 101 0 R
- /Prev 111 0 R
- /Title (Summary) >>
+ /Next 128 0 R
+ /Parent 116 0 R
+ /Prev 126 0 R
+ /Title (Long running commands) >>
endobj
% 'Outline.11': class OutlineEntryObject
-113 0 obj
-<< /Dest [ 97 0 R
+128 0 obj
+<< /Dest [ 104 0 R
/XYZ
62.69291
- 687.0236
+ 223.6236
0 ]
- /Parent 101 0 R
- /Prev 112 0 R
+ /Next 129 0 R
+ /Parent 116 0 R
+ /Prev 127 0 R
+ /Title (Threaded commands) >>
+endobj
+% 'Outline.12': class OutlineEntryObject
+129 0 obj
+<< /Dest [ 109 0 R
+ /XYZ
+ 62.69291
+ 419.8236
+ 0 ]
+ /Next 130 0 R
+ /Parent 116 0 R
+ /Prev 128 0 R
+ /Title (Running commands as external processes) >>
+endobj
+% 'Outline.13': class OutlineEntryObject
+130 0 obj
+<< /Dest [ 112 0 R
+ /XYZ
+ 62.69291
+ 621.0236
+ 0 ]
+ /Next 131 0 R
+ /Parent 116 0 R
+ /Prev 129 0 R
+ /Title (Summary) >>
+endobj
+% 'Outline.14': class OutlineEntryObject
+131 0 obj
+<< /Dest [ 112 0 R
+ /XYZ
+ 62.69291
+ 354.0236
+ 0 ]
+ /Parent 116 0 R
+ /Prev 130 0 R
/Title (Appendix: custom annotation objects) >>
endobj
-% 'R114': class PDFPages
-114 0 obj
+% 'R132': class PDFPages
+132 0 obj
% page tree
-<< /Count 16
- /Kids [ 42 0 R
- 44 0 R
- 47 0 R
- 48 0 R
- 58 0 R
- 61 0 R
- 65 0 R
- 69 0 R
- 85 0 R
- 86 0 R
- 89 0 R
+<< /Count 20
+ /Kids [ 45 0 R
+ 49 0 R
+ 51 0 R
+ 52 0 R
+ 63 0 R
+ 66 0 R
+ 71 0 R
+ 75 0 R
+ 77 0 R
90 0 R
- 92 0 R
- 95 0 R
+ 93 0 R
+ 96 0 R
97 0 R
- 98 0 R ]
+ 99 0 R
+ 101 0 R
+ 104 0 R
+ 106 0 R
+ 109 0 R
+ 112 0 R
+ 113 0 R ]
/Type /Pages >>
endobj
-% 'R115': class PDFStream
-115 0 obj
+% 'R133': class PDFStream
+133 0 obj
% page stream
-<< /Length 8624 >>
+<< /Length 9046 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
1 0 0 1 62.69291 741.0236 cm
q
-BT 1 0 0 1 0 9.64 Tm 2.664882 0 Td 24 TL /F2 20 Tf 0 0 0 rg (Testing and scripting your applications with plac) Tj T* -2.664882 0 Td ET
+BT 1 0 0 1 0 9.64 Tm 114.9049 0 Td 24 TL /F2 20 Tf 0 0 0 rg (Advanced usages of plac) Tj T* -114.9049 0 Td ET
Q
Q
q
@@ -1899,7 +2215,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
@@ -2013,30 +2329,30 @@ q
Q
Q
q
-1 0 0 1 62.69291 569.0236 cm
+1 0 0 1 62.69291 557.0236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F5 10 Tf 12 TL .372339 Tw (The present document discusses a few of the advanced use cases for plac. It assumes you have already) Tj T* 0 Tw (read an understood the basic documentation.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm /F5 10 Tf 12 TL 2.399986 Tw (The present document discusses a few of the advanced use cases for plac. It shows how to write) Tj T* 0 Tw 2.164651 Tw (interactive and non-interactive interpreters with plac, and how to use plac for testing and scripting a) Tj T* 0 Tw (generic application. It assumes you have already read an understood the basic documentation.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 536.0236 cm
+1 0 0 1 62.69291 524.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Contents) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 314.0236 cm
+1 0 0 1 62.69291 248.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
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 (Introduction) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 201 cm
+1 0 0 1 397.8898 255 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2044,13 +2360,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 183 cm
+1 0 0 1 0 237 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (From scripts to interactive applications) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 183 cm
+1 0 0 1 397.8898 237 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2058,13 +2374,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 165 cm
+1 0 0 1 0 219 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Testing a plac application) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 165 cm
+1 0 0 1 397.8898 219 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2072,13 +2388,13 @@ BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (3) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 0 147 cm
+1 0 0 1 0 201 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac easy tests) Tj T* ET
Q
Q
q
-1 0 0 1 397.8898 147 cm
+1 0 0 1 397.8898 201 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2086,13 +2402,13 @@ BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (4) Tj T* -66.44 0 Td ET
Q
Q
q
-1 0 0 1 0 129 cm
+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
Q
Q
q
-1 0 0 1 397.8898 129 cm
+1 0 0 1 397.8898 183 cm
q
0 0 .501961 rg
0 0 .501961 RG
@@ -2100,9 +2416,51 @@ 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 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
+Q
+Q
+q
+1 0 0 1 397.8898 165 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 147 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Readline support) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 147 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
+Q
+Q
+q
+1 0 0 1 0 129 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (The plac runner) Tj T* ET
+Q
+Q
+q
+1 0 0 1 397.8898 129 cm
+q
+0 0 .501961 rg
+0 0 .501961 RG
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) 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 (Containers of commands) 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
@@ -2110,13 +2468,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 (12) 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 (For ) Tj /F6 10 Tf (cmd ) Tj /F2 10 Tf (lovers) 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
@@ -2124,13 +2482,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 (14) 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 (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
@@ -2138,13 +2496,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 (16) 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 (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
@@ -2152,13 +2510,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 (16) Tj T* -60.88 0 Td ET
Q
Q
q
1 0 0 1 0 39 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Writing your own plac runner) Tj T* ET
+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
@@ -2166,7 +2524,7 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (13) Tj T* -60.88 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (18) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2180,7 +2538,7 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (14) Tj T* -60.88 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (19) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2194,43 +2552,37 @@ q
q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (15) Tj T* -60.88 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (19) Tj T* -60.88 0 Td ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 281.0236 cm
+1 0 0 1 62.69291 215.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 239.0236 cm
+1 0 0 1 62.69291 173.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 197.0236 cm
+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 155.0236 cm
+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
Q
Q
q
-1 0 0 1 62.69291 113.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
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -2241,38 +2593,44 @@ Q
endstream
endobj
-% 'R116': class PDFStream
-116 0 obj
+% 'R134': class PDFStream
+134 0 obj
% page stream
-<< /Length 4233 >>
+<< /Length 4496 >>
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 729.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
+Q
+Q
+q
+1 0 0 1 62.69291 696.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 690.0236 cm
+1 0 0 1 62.69291 642.0236 cm
q
BT 1 0 0 1 0 40.82 Tm 3.215814 Tw 12 TL /F1 10 Tf 0 0 0 rg (Command-line scripts have many advantages, but are no substitute for interactive applications. In) Tj T* 0 Tw .088171 Tw (particular, if you have a script with a large startup time which must be run multiple times, it is best to turn it) Tj T* 0 Tw 4.582126 Tw (into an interactive application, so that the startup is performed only once. ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (provides an) Tj T* 0 Tw /F4 10 Tf (Interpreter ) Tj /F1 10 Tf (class just for this purpose.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 660.0236 cm
+1 0 0 1 62.69291 612.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 630.0236 cm
+1 0 0 1 62.69291 582.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 380.8236 cm
+1 0 0 1 62.69291 332.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2293,13 +2651,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 336.8236 cm
+1 0 0 1 62.69291 288.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 303.6236 cm
+1 0 0 1 62.69291 255.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2320,7 +2678,7 @@ Q
Q
Q
q
-1 0 0 1 62.69291 138.4236 cm
+1 0 0 1 62.69291 90.42362 cm
q
q
1 0 0 1 0 0 cm
@@ -2334,20 +2692,13 @@ n -6 -6 468.6898 156 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (usage: shelve_interpreter.py [-h] [-interactive]) Tj T* ( [subcommands [subcommands ...]]) Tj T* T* (This script works both interactively and non-interactively. Use .help to see) Tj T* (the internal commands.) Tj T* T* (positional arguments:) Tj T* ( subcommands the commands of the underlying ishelve interpreter) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -interactive start interactive interface) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 118.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Thanks to this ingenuous trick, the script can be run both interactively and non-interactively:) Tj T* ET
-Q
-Q
-q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
@@ -2358,14 +2709,42 @@ Q
endstream
endobj
-% 'R117': class PDFStream
-117 0 obj
+% 'R135': class PDFStream
+135 0 obj
% page stream
-<< /Length 4141 >>
+<< /Length 4451 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 727.8236 cm
+1 0 0 1 62.69291 739.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 24 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL ( -interactive start interactive interface) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 719.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (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
q
q
1 0 0 1 0 0 cm
@@ -2386,13 +2765,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 695.8236 cm
+1 0 0 1 62.69291 654.6236 cm
q
-BT 1 0 0 1 0 16.82 Tm 2.649318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is an usage session, using ) Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (to enable readline features \() Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (is available in Unix-like) Tj T* 0 Tw (systems\):) Tj T* ET
+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 434.6236 cm
+1 0 0 1 62.69291 369.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -2402,48 +2782,48 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 252 re B*
+n -6 -6 468.6898 276 re B*
Q
q
-BT 1 0 0 1 0 233.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ rlwrap python shelve_interpreter.py -i # interactive use) Tj T* (usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]) Tj T* ( [.filename /home/micheles/conf.shelve]) Tj T* ( [params [params ...]] [setters [setters ...]]) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* (1) Tj T* (i) Tj (>) Tj ( b=2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( a b) Tj T* (1) Tj T* (2) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* ET
+BT 1 0 0 1 0 257.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python shelve_interpreter.py -i # interactive use) Tj T* (A simple interface to a shelve. Use .help to see the available commands.) Tj T* (i) Tj (>) Tj ( .help) Tj T* (Commands: .help, .showall, .clear, .delete) Tj T* (<) Tj (param) Tj (>) Tj ( ...) Tj T* (<) Tj (param=value) Tj (>) Tj ( ...) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* (1) Tj T* (i) Tj (>) Tj ( b=2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( a b) Tj T* (1) Tj T* (2) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 378.6236 cm
+1 0 0 1 62.69291 301.4236 cm
q
-BT 1 0 0 1 0 40.82 Tm .256412 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (.interact ) Tj /F1 10 Tf (method reads commands from the console and send them to the underlying interpreter,) Tj T* 0 Tw .065984 Tw (until the user send a CTRL-D command \(CTRL-Z in Windows\). There is a default argument ) Tj /F4 10 Tf (prompt='i) Tj (>) Tj T* 0 Tw .81936 Tw (' ) Tj /F1 10 Tf (which can be used to change the prompt. The message displayed by default is the argparse-provided) Tj T* 0 Tw (usage message, but can be customized by setting an ) Tj /F4 10 Tf (.intro ) Tj /F1 10 Tf (attribute on the main function.) Tj T* ET
+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 348.6236 cm
+1 0 0 1 62.69291 271.4236 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.03811 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that ) Tj /F4 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (is available only if you are using a recent version of Python \() Tj (>) Tj (= 2.5\),) Tj T* 0 Tw (because it is a context manager object which uses extended generators internally.) Tj T* ET
+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 318.6236 cm
+1 0 0 1 62.69291 238.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
+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 285.6236 cm
+1 0 0 1 62.69291 208.4236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Testing a plac application) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 3.034269 Tw (You can conveniently test your application in interactive mode. However manual testing is a poor) Tj T* 0 Tw (substitute for automatic testing.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 267.6236 cm
+1 0 0 1 62.69291 190.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 102.4236 cm
+1 0 0 1 62.69291 97.22362 cm
q
q
1 0 0 1 0 0 cm
@@ -2453,11 +2833,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 84 re B*
Q
q
0 0 0 rg
-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
+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
@@ -2474,26 +2854,47 @@ Q
endstream
endobj
-% 'R118': class PDFStream
-118 0 obj
+% 'R136': class PDFStream
+136 0 obj
% page stream
-<< /Length 6129 >>
+<< /Length 6154 >>
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 679.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 re B*
+Q
+q
+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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 635.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 687.0236 cm
+1 0 0 1 62.69291 593.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 533.8236 cm
+1 0 0 1 62.69291 440.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2514,34 +2915,28 @@ Q
Q
Q
q
-1 0 0 1 62.69291 489.8236 cm
+1 0 0 1 62.69291 384.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm 6.299974 Tw 12 TL /F1 10 Tf 0 0 0 rg (The method ) Tj /F4 10 Tf (.check\(given_input, expected_output\) ) Tj /F1 10 Tf (works on strings and raises an) Tj T* 0 Tw .971318 Tw /F4 10 Tf (AssertionError ) Tj /F1 10 Tf (if the output produced by the interpreter is different from the expected output for the) Tj T* 0 Tw (given input.) Tj T* ET
+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 459.8236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 2.179982 Tw 12 TL /F4 10 Tf 0 0 0 rg (AssertionError ) Tj /F1 10 Tf (is catched by tools like ) Tj /F4 10 Tf (py.test ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (nosetests ) Tj /F1 10 Tf (and actually ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (tests are) Tj T* 0 Tw (intended to be run with such tools.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 405.8236 cm
+1 0 0 1 62.69291 330.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 387.8236 cm
+1 0 0 1 62.69291 312.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 381.8236 cm
+1 0 0 1 62.69291 306.6236 cm
Q
q
-1 0 0 1 62.69291 369.8236 cm
+1 0 0 1 62.69291 294.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2556,57 +2951,37 @@ q
Q
Q
q
-1 0 0 1 62.69291 369.8236 cm
+1 0 0 1 62.69291 294.6236 cm
Q
q
-1 0 0 1 62.69291 351.8236 cm
+1 0 0 1 62.69291 276.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 309.8236 cm
+1 0 0 1 62.69291 234.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 276.8236 cm
+1 0 0 1 62.69291 201.6236 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
Q
Q
q
-1 0 0 1 62.69291 234.8236 cm
+1 0 0 1 62.69291 159.6236 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
Q
Q
q
-1 0 0 1 62.69291 192.8236 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
-Q
-Q
-q
-1 0 0 1 62.69291 99.62362 cm
-q
+1 0 0 1 62.69291 117.6236 cm
q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
-Q
-q
-BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (#!ishelve.py) Tj T* (i) Tj (>) Tj ( .clear # start from a clean state) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* ET
-Q
-Q
-Q
+BT 1 0 0 1 0 28.82 Tm 2.142209 Tw 12 TL /F1 10 Tf 0 0 0 rg (By using plac easy tests you can cut and paste your interactive session and turn it into a runnable) Tj T* 0 Tw .519213 Tw (automatics test. Consider for instance the following file ) Tj /F4 10 Tf (ishelve.placet ) Tj /F1 10 Tf (\(the ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (extension is a) Tj T* 0 Tw (mnemonic for plac easy tests\):) Tj T* ET
Q
Q
q
@@ -2620,14 +2995,14 @@ Q
endstream
endobj
-% 'R119': class PDFStream
-119 0 obj
+% 'R137': class PDFStream
+137 0 obj
% page stream
-<< /Length 5874 >>
+<< /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 667.8236 cm
+1 0 0 1 62.69291 595.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2637,29 +3012,29 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 96 re B*
+n -6 -6 468.6898 168 re B*
Q
q
-BT 1 0 0 1 0 77.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .cler # spelling error) Tj T* (SystemExit: unrecognized arguments: .cler) Tj T* ET
+BT 1 0 0 1 0 149.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (#!ishelve.py) Tj T* (i) Tj (>) Tj ( .clear # start from a clean state) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( a) Tj T* (1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( a) Tj T* (a: not found) Tj T* (i) Tj (>) Tj ( .cler # spelling error) Tj T* (.cler: not found) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 611.8236 cm
+1 0 0 1 62.69291 539.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 581.8236 cm
+1 0 0 1 62.69291 509.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 537.0393 cm
+1 0 0 1 62.69291 465.0393 cm
q
q
.988825 0 0 .988825 0 0 cm
@@ -2680,51 +3055,57 @@ Q
Q
Q
q
-1 0 0 1 62.69291 469.0393 cm
+1 0 0 1 62.69291 421.0393 cm
q
-BT 1 0 0 1 0 52.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 2.628221 Tw (test fail. 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 should be used to model user) Tj T* 0 Tw .491163 Tw (interaction when the order of the operations matters. Since the single subtests in a ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (file are not) Tj T* 0 Tw (independent, it makes sense to exit immediately at the first failure.) Tj T* ET
+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 403.0393 cm
+1 0 0 1 62.69291 379.0393 cm
q
-BT 1 0 0 1 0 52.82 Tm .414431 Tw 12 TL /F1 10 Tf 0 0 0 rg (The support for doctests in ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (comes nearly for free, thanks to the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (module in the standard library,) Tj T* 0 Tw .352765 Tw (which is able to parse simple languages as the ones you can implement with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. In particular, thanks to) Tj T* 0 Tw .061318 Tw 0 0 .501961 rg (shlex) Tj 0 0 0 rg (, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize comments \(the default comment character is ) Tj /F4 10 Tf (#) Tj /F1 10 Tf (\), continuation lines, escape) Tj T* 0 Tw 1.54061 Tw (sequences and more. Look at the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (documentation if you need to customize how the language is) Tj T* 0 Tw (interpreted.) Tj T* ET
+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 349.0393 cm
+1 0 0 1 62.69291 325.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 .86408 Tw (files \(plac easy tests does not need to be in a file: you can just pass to the ) Tj /F4 10 Tf (.doctest ) Tj /F1 10 Tf (method a list of) Tj T* 0 Tw (strings corresponding to the lines of the file\).) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 319.0393 cm
+1 0 0 1 62.69291 271.0393 cm
q
-BT 1 0 0 1 0 16.82 Tm .12683 Tw 12 TL /F1 10 Tf 0 0 0 rg (At the present plac easy tests do not use any code from the doctest module, but the situation may change) Tj T* 0 Tw (in the future \(it would be nice if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (could reuse doctests directives like ELLIPSIS\).) Tj T* ET
+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 289.0393 cm
+1 0 0 1 62.69291 241.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 101.8642 cm
+1 0 0 1 62.69291 93.83932 cm
q
q
-.988825 0 0 .988825 0 0 cm
+1 0 0 1 0 0 cm
q
-1 0 0 1 6.6 6.674587 cm
+1 0 0 1 6.6 6.6 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 474 180 re B*
+n -6 -6 468.6898 108 re B*
Q
q
0 0 0 rg
-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
+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
@@ -2741,44 +3122,65 @@ Q
endstream
endobj
-% 'R120': class PDFStream
-120 0 obj
+% 'R138': class PDFStream
+138 0 obj
% page stream
-<< /Length 4648 >>
+<< /Length 4789 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 693.0236 cm
+1 0 0 1 62.69291 680.7757 cm
+q
+q
+.988825 0 0 .988825 0 0 cm
+q
+1 0 0 1 6.6 6.674587 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 474 84 re B*
+Q
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 1.040465 Tw (function of the script specified in the shabng line. You can use both the full path name of the tool, or a) Tj T* 0 Tw .87686 Tw (relative path name. In this case the runner look at the environment variable ) Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (and it searches) Tj T* 0 Tw 1.900651 Tw (the plac tool in the directories specified there \() Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (is just a string containing directory names) Tj T* 0 Tw .56332 Tw (separated by colons\). If the variable ) Tj /F4 10 Tf (PLACPATH ) Tj /F1 10 Tf (is not defined, it just looks in the current directory. If the) Tj T* 0 Tw (plac tool is not found, an ) Tj /F4 10 Tf (ImportError ) Tj /F1 10 Tf (is raised.) Tj T* ET
+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
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 660.0236 cm
+1 0 0 1 62.69291 600.7757 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
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 618.0236 cm
+1 0 0 1 62.69291 525.7757 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 552.0236 cm
+1 0 0 1 62.69291 459.7757 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 534.0236 cm
+1 0 0 1 62.69291 429.7757 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (In particular consider the following batch file, which contains a syntax error \() Tj /F4 10 Tf (.dl ) Tj /F1 10 Tf (instead of ) Tj /F4 10 Tf (.del) Tj /F1 10 Tf (\):) Tj T* ET
+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 428.8236 cm
+1 0 0 1 62.69291 324.5757 cm
q
q
1 0 0 1 0 0 cm
@@ -2799,13 +3201,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 396.8236 cm
+1 0 0 1 62.69291 292.5757 cm
q
-BT 1 0 0 1 0 16.82 Tm .26686 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you execute the batch file, the interpreter will raise a ) Tj /F4 10 Tf (SystemExit ) Tj /F1 10 Tf (with an appropriated error message) Tj T* 0 Tw (at the ) Tj /F4 10 Tf (.dl ) Tj /F1 10 Tf (line and the last command will ) Tj /F5 10 Tf (not ) Tj /F1 10 Tf (be executed:) Tj T* ET
+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 207.6236 cm
+1 0 0 1 62.69291 91.37571 cm
q
q
1 0 0 1 0 0 cm
@@ -2815,22 +3217,16 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 180 re B*
+n -6 -6 468.6898 192 re B*
Q
q
-BT 1 0 0 1 0 161.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python -c "import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).execute\(open\('ishelve.plac'\), verbose=True\)") Tj T* (i) Tj (>) Tj ( .clear) Tj T* (cleared the shelve) Tj T* (i) Tj (>) Tj ( a=1 b=2) Tj T* (setting a=1) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* (a=1) Tj T* (i) Tj (>) Tj ( .del a) Tj T* (deleted a) Tj T* (i) Tj (>) Tj ( .dl b) Tj T* (unrecognized arguments: .dl) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 163.6236 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 56.69291 56.69291 cm
q
0 0 0 rg
@@ -2841,45 +3237,71 @@ Q
endstream
endobj
-% 'R121': class PDFStream
-121 0 obj
+% 'R139': class PDFStream
+139 0 obj
% page stream
-<< /Length 3980 >>
+<< /Length 4588 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 744.0236 cm
+1 0 0 1 62.69291 727.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Containers of commands) Tj T* ET
+.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 ( .show) Tj T* (b=2) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 702.0236 cm
+1 0 0 1 62.69291 683.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 1.483988 Tw (poor man implementation of an object system as a chain of elifs; I also said that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was able to do) Tj T* 0 Tw (better. Here I will substantiate my claim.) Tj T* ET
+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 660.0236 cm
+1 0 0 1 62.69291 650.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
+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
q
BT 1 0 0 1 0 28.82 Tm .89104 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is actually able to infer a set of subparsers from a generic container of commands. This is useful if) Tj T* 0 Tw 3.125814 Tw (you want to implement ) Tj /F5 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line application featuring) Tj T* 0 Tw (subcommands is subversion\).) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 618.0236 cm
+1 0 0 1 62.69291 500.8236 cm
q
-BT 1 0 0 1 0 28.82 Tm .015868 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a container of commands is any object with a ) Tj /F4 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or) Tj T* 0 Tw 2.550888 Tw (methods which are valid commands. A command container may have initialization/finalization hooks) Tj T* 0 Tw (\() Tj /F4 10 Tf (__enter__/__exit__) Tj /F1 10 Tf (\) and dispatch hooks \() Tj /F4 10 Tf (__missing__) Tj /F1 10 Tf (, invoked for invalid command names\).) Tj T* ET
+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 600.0236 cm
+1 0 0 1 62.69291 482.8236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Using this feature the shelve interface can be rewritten in a more object-oriented way as follows:) Tj T* ET
+BT 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
Q
Q
q
-1 0 0 1 62.69291 98.82362 cm
+1 0 0 1 62.69291 89.62362 cm
q
q
1 0 0 1 0 0 cm
@@ -2889,11 +3311,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 492 re B*
+n -6 -6 468.6898 384 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 473.71 Tm /F4 10 Tf 12 TL (# ishelve2.py) Tj T* (import shelve, os, sys, plac) Tj T* T* (class ShelveInterface\(object\):) Tj T* ( "A minimal interface over a shelve object") Tj T* ( commands = 'set', 'show', 'showall', 'delete') Tj T* ( @plac.annotations\() Tj T* ( configfile=\('path name of the shelve', 'option'\)\)) Tj T* ( def __init__\(self, configfile='~/conf.shelve'\):) Tj T* ( self.fname = os.path.expanduser\(configfile\)) Tj T* ( self.intro = 'Operating on %s. Available commands:\\n%s' % \() Tj T* ( self.fname, '\\n'.join\(c for c in self.commands\)\)) Tj T* ( def __enter__\(self\):) Tj T* ( self.sh = shelve.open\(self.fname\)) Tj T* ( return self) Tj T* ( def set\(self, name, value\):) Tj T* ( "set name value") Tj T* ( yield 'setting %s=%s' % \(name, value\)) Tj T* ( self.sh[name] = value) Tj T* ( def show\(self, *names\):) Tj T* ( "show given parameters") Tj T* ( for name in names:) Tj T* ( yield '%s = %s' % \(name, self.sh[name]\) # no error checking) Tj T* ( def showall\(self\):) Tj T* ( "show all parameters") Tj T* ( for name in self.sh:) Tj T* ( yield '%s = %s' % \(name, self.sh[name]\)) Tj T* ( def delete\(self, name=None\):) Tj T* ( "delete given parameter \(or everything\)") Tj T* ( if name is None:) Tj T* ( yield 'deleting everything') Tj T* ( self.sh.clear\(\)) Tj T* ( else:) Tj T* ( yield 'deleting %s' % name) Tj T* ( del self.sh[name] # no error checking) Tj T* ( def __exit__\(self, etype, exc, tb\):) Tj T* ( self.sh.close\(\)) Tj T* T* (main = ShelveInterface # the main 'function' can also be a class!) Tj T* T* ET
+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
Q
Q
Q
@@ -2910,14 +3332,14 @@ Q
endstream
endobj
-% 'R122': class PDFStream
-122 0 obj
+% 'R140': class PDFStream
+140 0 obj
% page stream
-<< /Length 4384 >>
+<< /Length 4077 >>
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 607.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2927,31 +3349,37 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 156 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( i = plac.Interpreter\(main\(\)\)) Tj T* ( i.interact\(\)) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 623.8236 cm
+1 0 0 1 62.69291 515.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 605.8236 cm
+1 0 0 1 62.69291 437.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
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is a session of usage on an Unix-like operating system:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 284.6236 cm
+1 0 0 1 62.69291 98.62362 cm
q
q
1 0 0 1 0 0 cm
@@ -2964,81 +3392,113 @@ q
n -6 -6 468.6898 312 re B*
Q
q
-BT 1 0 0 1 0 293.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ rlwrap python ishelve2.py) Tj T* (Operating on /home/micheles/conf.shelve. Available commands:) Tj T* (set) Tj T* (show) Tj T* (showall) Tj T* (delete) Tj T* (i) Tj (>) Tj ( delete) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a pippo) Tj T* (setting a=pippo) Tj T* (i) Tj (>) Tj ( set b lippo) Tj T* (setting b=lippo) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (a = pippo) Tj T* (i) Tj (>) Tj ( show a b) Tj T* (a = pippo) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( del a) Tj T* (deleting a) Tj T* (i) Tj (>) Tj ( showall) Tj T* (b = lippo) Tj T* (i) Tj (>) Tj ( delete a) Tj T* (DBNotFoundError: \(-30988, 'DB_NOTFOUND: No matching key/data pair found'\)) Tj T* (i) Tj (>) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 252.6236 cm
+1 0 0 1 56.69291 56.69291 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R141': class PDFStream
+141 0 obj
+% page stream
+<< /Length 3815 >>
+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
+q
+q
+.773863 0 0 .773863 0 0 cm
+q
+1 0 0 1 6.6 8.528639 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 606 204 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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 574.2269 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 198.6236 cm
+1 0 0 1 62.69291 541.2269 cm
q
-BT 1 0 0 1 0 40.82 Tm .532209 Tw 12 TL /F1 10 Tf 0 0 0 rg (The interactive mode of ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (can be used as a replacement of the ) Tj /F4 10 Tf (cmd ) Tj /F1 10 Tf (module in the standard library.) Tj T* 0 Tw .66881 Tw (There are a few differences, however. For instance you miss tab completion, even if use ) Tj 0 0 .501961 rg (rlwrap ) Tj 0 0 0 rg (\(you get) Tj T* 0 Tw .092651 Tw (persistent command history for free, however\). This is not a big issue, since ) Tj /F4 10 Tf (plac ) Tj /F1 10 Tf (understands command) Tj T* 0 Tw (abbreviations.) Tj T* ET
+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 180.6236 cm
+1 0 0 1 62.69291 463.2269 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (If an abbreviation is ambiguous, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (warns you:) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 111.4236 cm
+1 0 0 1 62.69291 153.1549 cm
q
q
-1 0 0 1 0 0 cm
+.96447 0 0 .96447 0 0 cm
q
-1 0 0 1 6.6 6.6 cm
+1 0 0 1 6.6 6.843137 cm
q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 486 312 re B*
Q
q
-BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ rlwrap python ishelve2.py) Tj T* (usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...) Tj T* (i) Tj (>) Tj ( sh) Tj T* (NameError: Ambiguous command 'sh': matching ['showall', 'show']) Tj T* ET
+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
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
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (9) Tj T* -238.1649 0 Td ET
Q
Q
endstream
endobj
-% 'R123': class PDFStream
-123 0 obj
+% 'R142': class PDFStream
+142 0 obj
% page stream
-<< /Length 5097 >>
+<< /Length 5061 >>
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 (For ) Tj /F6 17.5 Tf (cmd ) Tj /F2 17.5 Tf (lovers) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 642.0236 cm
-q
-BT 1 0 0 1 0 88.82 Tm .391567 Tw 12 TL /F1 10 Tf 0 0 0 rg (I have been using the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module of the standard library for years. I have also written a much enhanced) Tj T* 0 Tw .212619 Tw /F4 10 Tf (cmd2 ) Tj /F1 10 Tf (module which we are using internally at work and from which I have taken some ideas used in ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* 0 Tw .13832 Tw (In many ways ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (makes the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module obsolete, but I realize why many nostalgic souls would still use) Tj T* 0 Tw 1.053984 Tw 0 0 .501961 rg (cmd) Tj 0 0 0 rg (, especially until ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not grow real auto-completion features, instead of relying on ) Tj 0 0 .501961 rg (rlwrap) Tj 0 0 0 rg (. But) Tj T* 0 Tw 1.12561 Tw (there must not be competition between ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and ) Tj 0 0 .501961 rg (cmd) Tj 0 0 0 rg (: actually the two can happily work togethere. For) Tj T* 0 Tw .338988 Tw (this reason I have put in the ) Tj /F4 10 Tf (plac_ext ) Tj /F1 10 Tf (module a few lines of code for gluing together ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (and ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (, the) Tj T* 0 Tw 1.229269 Tw /F4 10 Tf (cmd_interface) Tj /F1 10 Tf (. Using the ) Tj /F4 10 Tf (cmd_interface ) Tj /F1 10 Tf (is quite trivial: give to it a plac command container and) Tj T* 0 Tw (you will get in exchange a ) Tj /F4 10 Tf (cmd.Cmd ) Tj /F1 10 Tf (object:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 548.8236 cm
+1 0 0 1 62.69291 715.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3048,25 +3508,35 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 48 re B*
Q
q
-0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# cmd_ext.py) Tj T* (from plac_ext import cmd_interface) Tj T* (import ishelve2) Tj T* T* (if __name__ == '__main__':) Tj T* ( cmd_interface\(ishelve2.main\(\)\).cmdloop\(\)) Tj T* ET
+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 528.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 (Here is an example of interactive session:) 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 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
Q
Q
q
-1 0 0 1 62.69291 267.6236 cm
+1 0 0 1 62.69291 206.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3076,64 +3546,90 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 252 re B*
+n -6 -6 468.6898 300 re B*
Q
q
-BT 1 0 0 1 0 233.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python cmd_ex.py) Tj T* (\(Cmd\) help) Tj T* T* (Documented commands \(type help ) Tj (<) Tj (topic) Tj (>) Tj (\):) Tj T* (========================================) Tj T* (delete set show showall) Tj T* T* (Undocumented commands:) Tj T* (======================) Tj T* (EOF help) Tj T* T* (\(Cmd\) set a 1) Tj T* (setting a=1) Tj T* (\(Cmd\) show a) Tj T* (a = 1) Tj T* (\(Cmd\) showall) Tj T* (a = 1) Tj T* (\(Cmd\) delete b) Tj T* (KeyError: 'b') Tj T* (\(Cmd\) EOF [or CTRL-D]) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 235.6236 cm
+1 0 0 1 62.69291 150.6236 cm
q
-BT 1 0 0 1 0 16.82 Tm .504983 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally the ) Tj /F4 10 Tf (cmd_interface ) Tj /F1 10 Tf (builds a ) Tj /F4 10 Tf (cmd.Cmd ) Tj /F1 10 Tf (class and adds to it the ) Tj /F4 10 Tf (do_ ) Tj /F1 10 Tf (methods corresponding) Tj T* 0 Tw (to the commands in the container, then it returns a ) Tj /F4 10 Tf (cmd.Cmd ) Tj /F1 10 Tf (instance.) Tj T* ET
+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 193.6236 cm
+1 0 0 1 62.69291 108.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm .089488 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F4 10 Tf (cmd_interface ) Tj /F1 10 Tf (is just a proof of concept: it is there so that you can study the source code and see) Tj T* 0 Tw .93832 Tw (an example of integration of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (with a different framework. It may change and even go away in future) Tj T* 0 Tw (releases of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 160.6236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The plac runner) Tj T* ET
+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
Q
Q
q
-1 0 0 1 62.69291 94.62362 cm
+1 0 0 1 62.69291 90.62362 cm
q
-BT 1 0 0 1 0 52.82 Tm 1.531318 Tw 12 TL /F1 10 Tf 0 0 0 rg (The distribution of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (includes a runner script named ) Tj /F4 10 Tf (plac_runner.py) Tj /F1 10 Tf (, which will be installed in a) Tj T* 0 Tw .44748 Tw (suitable directory in your system by ) Tj 0 0 .501961 rg (distutils ) Tj 0 0 0 rg (\(say in ) Tj /F4 10 Tf (\\usr\\local\\bin\\plac_runner.py ) Tj /F1 10 Tf (in a Unix-like) Tj T* 0 Tw .680651 Tw (operative system\). The runner provides many facilities to run ) Tj /F4 10 Tf (.plac ) Tj /F1 10 Tf (scripts and ) Tj /F4 10 Tf (.placet ) Tj /F1 10 Tf (files, as well) Tj T* 0 Tw 1.47311 Tw (as Python modules containg a ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (object, which can be a function, a command container object or) Tj T* 0 Tw (even a command container class.) Tj T* ET
+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 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 238.1649 0 Td (9) Tj T* -238.1649 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (10) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R124': class PDFStream
-124 0 obj
+% 'R143': class PDFStream
+143 0 obj
% page stream
-<< /Length 4203 >>
+<< /Length 5053 >>
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 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 ( sh) Tj T* (NameError: Ambiguous command 'sh': matching ['showall', 'show']) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 694.8236 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
+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
q
BT 1 0 0 1 0 16.82 Tm 1.994269 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, suppose you want to execute a script containing commands defined in the ) Tj /F4 10 Tf (ishelve2) Tj T* 0 Tw /F1 10 Tf (module like the following one:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 671.8236 cm
+1 0 0 1 62.69291 529.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3154,13 +3650,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 627.8236 cm
+1 0 0 1 62.69291 485.6236 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
Q
Q
q
-1 0 0 1 62.69291 538.6505 cm
+1 0 0 1 62.69291 396.4505 cm
q
q
.952737 0 0 .952737 0 0 cm
@@ -3181,24 +3677,24 @@ Q
Q
Q
q
-1 0 0 1 62.69291 506.6505 cm
+1 0 0 1 62.69291 364.4505 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 476.6505 cm
+1 0 0 1 62.69291 334.4505 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 470.6505 cm
+1 0 0 1 62.69291 328.4505 cm
Q
q
-1 0 0 1 62.69291 458.6505 cm
+1 0 0 1 62.69291 316.4505 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3206,23 +3702,23 @@ q
1 0 0 1 20 0 cm
q
0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (alias plac="rlwrap plac_runner.py") Tj T* ET
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (alias plac="plac_runner.py") Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 458.6505 cm
+1 0 0 1 62.69291 316.4505 cm
Q
q
-1 0 0 1 62.69291 428.6505 cm
+1 0 0 1 62.69291 286.4505 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 239.4505 cm
+1 0 0 1 62.69291 109.2505 cm
q
q
1 0 0 1 0 0 cm
@@ -3232,23 +3728,40 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 180 re B*
+n -6 -6 468.6898 168 re B*
Q
q
-BT 1 0 0 1 0 161.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ plac -i ishelve2.py) Tj T* (Operating on /home/micheles/conf.shelve. Available commands:) Tj T* (set) Tj T* (show) Tj T* (showall) Tj T* (delete) Tj T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show b) Tj T* (b = 2) Tj T* ET
+BT 1 0 0 1 0 149.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ plac -i 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* T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show b) Tj T* (b = 2) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 219.4505 cm
+1 0 0 1 62.69291 89.25045 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 62.69291 90.25045 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
+% '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
q
q
1 0 0 1 0 0 cm
@@ -3261,44 +3774,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 b) Tj T* (b = 2) Tj T* ET
+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
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 (10) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R125': class PDFStream
-125 0 obj
-% page stream
-<< /Length 5347 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 741.0236 cm
+1 0 0 1 62.69291 611.8236 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 723.0236 cm
+1 0 0 1 62.69291 593.8236 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 677.8236 cm
+1 0 0 1 62.69291 548.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3319,19 +3815,19 @@ Q
Q
Q
q
-1 0 0 1 62.69291 633.8236 cm
+1 0 0 1 62.69291 504.6236 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 603.8236 cm
+1 0 0 1 62.69291 474.6236 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 570.6236 cm
+1 0 0 1 62.69291 441.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3352,23 +3848,23 @@ Q
Q
Q
q
-1 0 0 1 62.69291 538.6236 cm
+1 0 0 1 62.69291 409.4236 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 520.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 (It also works in non-interactive mode, if you call it as) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 514.6236 cm
+1 0 0 1 62.69291 385.4236 cm
Q
q
-1 0 0 1 62.69291 502.6236 cm
+1 0 0 1 62.69291 373.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3383,17 +3879,17 @@ q
Q
Q
q
-1 0 0 1 62.69291 502.6236 cm
+1 0 0 1 62.69291 373.4236 cm
Q
q
-1 0 0 1 62.69291 484.6236 cm
+1 0 0 1 62.69291 355.4236 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 415.4236 cm
+1 0 0 1 62.69291 286.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3414,32 +3910,32 @@ Q
Q
Q
q
-1 0 0 1 62.69291 383.4236 cm
+1 0 0 1 62.69291 254.2236 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
Q
Q
q
-1 0 0 1 62.69291 350.4236 cm
+1 0 0 1 62.69291 221.2236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A non class-based example) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 308.4236 cm
+1 0 0 1 62.69291 179.2236 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 278.4236 cm
+1 0 0 1 62.69291 149.2236 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 89.22362 cm
+1 0 0 1 62.69291 92.02362 cm
q
q
1 0 0 1 0 0 cm
@@ -3449,11 +3945,11 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 180 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\(url='url of the source code'\)) Tj T* (def checkout\(url\):) Tj T* ( "A fake checkout command") Tj T* ( return \('checkout ', url\)) Tj T* T* (@plac.annotations\(message=\('commit message', 'option'\)\)) Tj T* (def commit\(message\):) Tj T* ( "A fake commit command") Tj T* ET
+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
Q
Q
Q
@@ -3463,21 +3959,21 @@ q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (11) 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 (12) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R126': class PDFStream
-126 0 obj
+% 'R145': class PDFStream
+145 0 obj
% page stream
-<< /Length 3894 >>
+<< /Length 3414 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 559.8236 cm
+1 0 0 1 62.69291 427.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3487,30 +3983,30 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 204 re B*
+n -6 -6 468.6898 336 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 185.71 Tm /F4 10 Tf 12 TL ( return \('commit ', message\)) Tj T* T* (@plac.annotations\(quiet=\('summary information', 'flag', 'q'\)\)) Tj T* (def status\(quiet\):) Tj T* ( "A fake status command") Tj T* ( return \('status ', quiet\)) Tj T* T* (def __missing__\(name\):) Tj T* ( return 'Command %r does not exist' % name) Tj T* T* (def __exit__\(etype, exc, tb\):) Tj T* ( "Will be called automatically at the end of the call/cmdloop") Tj T* ( if etype in \(None, GeneratorExit\): # success) Tj T* ( print\('ok'\)) Tj T* T* (main = __import__\(__name__\) # the module imports itself!) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 515.8236 cm
+1 0 0 1 62.69291 383.8236 cm
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
Q
Q
q
-1 0 0 1 62.69291 485.8236 cm
+1 0 0 1 62.69291 353.8236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.259986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /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 344.6236 cm
+1 0 0 1 62.69291 212.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3531,13 +4027,13 @@ Q
Q
Q
q
-1 0 0 1 62.69291 324.6236 cm
+1 0 0 1 62.69291 192.6236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can get help for the subcommands by postponing ) Tj /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 207.4236 cm
+1 0 0 1 62.69291 99.42362 cm
q
q
1 0 0 1 0 0 cm
@@ -3547,31 +4043,35 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
+n -6 -6 468.6898 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL ($ 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
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 175.4236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 2.064985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice how the docstring of the command is automatically shown in usage message, as well as the) Tj T* 0 Tw (documentation for the sub flag ) Tj /F4 10 Tf (-q) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 157.4236 cm
+1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is an example of a non-interactive session:) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
Q
Q
+
+endstream
+
+endobj
+% 'R146': class PDFStream
+146 0 obj
+% page stream
+<< /Length 5361 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 100.2236 cm
+1 0 0 1 62.69291 727.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3581,35 +4081,31 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
+n -6 -6 468.6898 36 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py check url) Tj T* (checkout) Tj T* (url) Tj T* ET
+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
Q
Q
Q
Q
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 695.8236 cm
+q
+BT 1 0 0 1 0 16.82 Tm 2.064985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice how the docstring of the command is automatically shown in usage message, as well as the) Tj T* 0 Tw (documentation for the sub flag ) Tj /F4 10 Tf (-q) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 677.8236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is an example of a non-interactive session:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R127': class PDFStream
-127 0 obj
-% page stream
-<< /Length 5650 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 679.8236 cm
+1 0 0 1 62.69291 548.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3619,25 +4115,25 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
+n -6 -6 468.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py st -q) Tj T* (status) Tj T* (True) Tj T* ($ plac vcs.py co) Tj T* (commit) Tj T* (None) Tj T* ET
+BT 1 0 0 1 0 101.71 Tm /F4 10 Tf 12 TL ($ plac vcs.py check url) Tj T* (checkout) Tj T* (url) Tj T* ($ plac vcs.py st -q) Tj T* (status) Tj T* (True) Tj T* ($ plac vcs.py co) Tj T* (commit) Tj T* (None) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 528.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (and here is an interactive session:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 458.6236 cm
+1 0 0 1 62.69291 327.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3657,38 +4153,38 @@ Q
Q
Q
q
-1 0 0 1 62.69291 426.6236 cm
+1 0 0 1 62.69291 295.4236 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 396.6236 cm
+1 0 0 1 62.69291 265.4236 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 363.6236 cm
+1 0 0 1 62.69291 232.4236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Writing your own plac runner) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 309.6236 cm
+1 0 0 1 62.69291 178.4236 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 303.6236 cm
+1 0 0 1 62.69291 172.4236 cm
Q
q
-1 0 0 1 62.69291 303.6236 cm
+1 0 0 1 62.69291 172.4236 cm
Q
q
-1 0 0 1 62.69291 285.6236 cm
+1 0 0 1 62.69291 154.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3708,13 +4204,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 285.6236 cm
+1 0 0 1 62.69291 154.4236 cm
Q
q
-1 0 0 1 62.69291 285.6236 cm
+1 0 0 1 62.69291 154.4236 cm
Q
q
-1 0 0 1 62.69291 267.6236 cm
+1 0 0 1 62.69291 136.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3734,13 +4230,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 267.6236 cm
+1 0 0 1 62.69291 136.4236 cm
Q
q
-1 0 0 1 62.69291 267.6236 cm
+1 0 0 1 62.69291 136.4236 cm
Q
q
-1 0 0 1 62.69291 249.6236 cm
+1 0 0 1 62.69291 118.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3760,13 +4256,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 249.6236 cm
+1 0 0 1 62.69291 118.4236 cm
Q
q
-1 0 0 1 62.69291 249.6236 cm
+1 0 0 1 62.69291 118.4236 cm
Q
q
-1 0 0 1 62.69291 231.6236 cm
+1 0 0 1 62.69291 100.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3786,25 +4282,75 @@ q
Q
Q
q
-1 0 0 1 62.69291 231.6236 cm
+1 0 0 1 62.69291 100.4236 cm
Q
q
-1 0 0 1 62.69291 231.6236 cm
+1 0 0 1 62.69291 100.4236 cm
Q
q
-1 0 0 1 62.69291 189.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 (14) Tj T* -235.3849 0 Td ET
+Q
+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
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 171.6236 cm
+1 0 0 1 62.69291 711.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 90.42362 cm
+1 0 0 1 62.69291 617.8236 cm
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 re B*
+Q
+q
+BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( from ishelve import ishelve) Tj T* (>) Tj (>) Tj (>) Tj ( with plac.Interpreter\(ishelve\) as i:) Tj T* (... print\(i.send\('.cler'\)\)) Tj T* (...) Tj T* (SystemExit: unrecognized arguments: .cler) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 585.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
+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
q
q
1 0 0 1 0 0 cm
@@ -3814,34 +4360,87 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
+n -6 -6 468.6898 228 re B*
Q
q
-BT 1 0 0 1 0 53.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( from ishelve import ishelve) Tj T* (>) Tj (>) Tj (>) Tj ( with plac.Interpreter\(ishelve\) as i:) Tj T* (... print\(i.send\('.cler'\)\)) Tj T* (...) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 209.71 Tm /F4 10 Tf 12 TL (input_widget = WidgetReadingInput\(\)) Tj T* (output_widget = WidgetDisplayingOutput\(\)) Tj T* T* (def send\(interpreter, line\):) Tj T* ( out = interpreter.send\(line\)) Tj T* ( if out.tb: # there was an error) Tj T* ( output_widget.display\(out.tb, color='red'\)) Tj T* ( else:) Tj T* ( output_widget.display\(out.str\)) Tj T* T* (main = plac.import_main\(tool_path\) # get the main object) Tj T* T* (with plac.Interpreter\(main\) as i:) Tj T* ( def callback\(event\):) Tj T* ( if event.user_pressed_ENTER\(\):) Tj T* ( send\(i, input_widget.last_line\)) Tj T* ( input_widget.addcallback\(callback\)) Tj T* ( gui_mainloop.start\(\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 274.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
+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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 48 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (with interpreter:) Tj T* ( for input_value in iter\(input_queue.get, EXIT\):) Tj T* ( output_queue.put\(interpreter.send\(input_value\)\)) Tj T* ET
Q
Q
Q
Q
Q
q
+1 0 0 1 62.69291 131.4236 cm
+q
+BT 1 0 0 1 0 16.82 Tm .106098 Tw 12 TL /F1 10 Tf 0 0 0 rg (The same trick also work for processes; you could run the interpreter loop in a separate process and send) Tj T* 0 Tw (commands to it via the Queue class provided by the ) Tj 0 0 .501961 rg (multiprocessing ) Tj 0 0 0 rg (module.) Tj T* ET
+Q
+Q
+q
1 0 0 1 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
-% 'R128': class PDFStream
-128 0 obj
+% 'R148': class PDFStream
+148 0 obj
% page stream
-<< /Length 6119 >>
+<< /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 739.8236 cm
+1 0 0 1 62.69291 744.0236 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Long running commands) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 702.0236 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.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
q
q
1 0 0 1 0 0 cm
@@ -3851,30 +4450,135 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
+n -6 -6 468.6898 252 re B*
Q
q
-BT 1 0 0 1 0 5.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (SystemExit: unrecognized arguments: .cler) Tj T* ET
+0 0 0 rg
+BT 1 0 0 1 0 233.71 Tm /F4 10 Tf 12 TL (import time) Tj T* (import plac) Tj T* T* (class FakeImporter\(object\):) Tj T* ( "A fake importer with an import_file command") Tj T* ( commands = ['import_file']) Tj T* ( def __init__\(self, dsn\):) Tj T* ( self.dsn = dsn) Tj T* ( def import_file\(self, fname\):) Tj T* ( "Import a file into the database") Tj T* ( try:) Tj T* ( for n in range\(10000\):) Tj T* ( time.sleep\(.01\)) Tj T* ( if n % 100 == 99:) Tj T* ( yield 'Imported %d lines' % \(n+1\)) Tj T* ( finally:) Tj T* ( print\('closing the file'\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.Interpreter\(plac.call\(FakeImporter\)\).interact\(\)) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 707.8236 cm
+1 0 0 1 62.69291 408.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
+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 665.8236 cm
+1 0 0 1 62.69291 279.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
+BT 1 0 0 1 0 101.71 Tm 12 TL /F4 10 Tf 0 0 0 rg ($ python importer1.py dsn) Tj T* (A fake importer with an import_file command) Tj T* (i) Tj (>) Tj ( import_file file1) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) Tj T* (Imported 300 lines) Tj T* (... ) Tj (<) Tj (wait 3+ minutes) Tj (>) Tj T* (Imported 10000 lines) Tj T* (closing the file) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 235.6236 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
+Q
+Q
+q
+1 0 0 1 62.69291 202.6236 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
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
+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 428.6236 cm
+1 0 0 1 62.69291 166.6236 cm
+Q
+q
+1 0 0 1 62.69291 154.6236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (commands = ['import_file']) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 154.6236 cm
+Q
+q
+1 0 0 1 62.69291 136.6236 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
+Q
+q
+1 0 0 1 62.69291 118.6236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (thcommands = ['import_file']) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 118.6236 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
+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
q
q
1 0 0 1 0 0 cm
@@ -3884,31 +4588,130 @@ q
.662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 228 re B*
+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 ( import_file file1) Tj T* (<) Tj (ThreadedTask 1 [import_file file1] RUNNING) Tj (>) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 663.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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 60 re B*
+Q
+q
+BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .output 1) Tj T* (<) Tj (ThreadedTask 1 [import_file file1] RUNNING) Tj (>) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 574.6236 cm
+q
0 0 0 rg
-BT 1 0 0 1 0 209.71 Tm /F4 10 Tf 12 TL (input_widget = WidgetReadingInput\(\)) Tj T* (output_widget = WidgetDisplayingOutput\(\)) Tj T* T* (def send\(interpreter, line\):) Tj T* ( out = interpreter.send\(line\)) Tj T* ( if out.tb: # there was an error) Tj T* ( output_widget.display\(out.tb, color='red'\)) Tj T* ( else:) Tj T* ( output_widget.display\(out.str\)) Tj T* T* (main = plac.import_main\(tool_path\) # get the main object) Tj T* T* (with plac.Interpreter\(main\) as i:) Tj T* ( def callback\(event\):) Tj T* ( if event.user_pressed_ENTER\(\):) Tj T* ( send\(i, input_widget.last_line\)) Tj T* ( input_widget.addcallback\(callback\)) Tj T* ( gui_mainloop.start\(\)) Tj T* ET
+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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 84 re B*
+Q
+q
+BT 1 0 0 1 0 65.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .output 1) Tj T* (<) Tj (ThreadedTask 1 [import_file file1] RUNNING) Tj (>) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) Tj T* (Imported 300 lines) Tj T* (Imported 400 lines) Tj T* ET
Q
Q
Q
Q
Q
q
-1 0 0 1 62.69291 396.6236 cm
+1 0 0 1 62.69291 461.4236 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
+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 342.6236 cm
+1 0 0 1 62.69291 416.2236 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
+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 ( .output 1) Tj T* (<) Tj (ThreadedTask 1 [import_file file1] FINISHED) Tj (>) Tj T* ET
+Q
+Q
+Q
Q
Q
q
-1 0 0 1 62.69291 285.4236 cm
+1 0 0 1 62.69291 396.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
+q
+q
+1 0 0 1 0 0 cm
+q
+1 0 0 1 6.6 6.6 cm
+q
+.662745 .662745 .662745 RG
+.5 w
+.960784 .960784 .862745 rg
+n -6 -6 468.6898 60 re B*
+Q
+q
+BT 1 0 0 1 0 41.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (i) Tj (>) Tj ( import_file file2) Tj T* (<) Tj (ThreadedTask 5 [import_file file2] RUNNING) Tj (>) Tj T* (i) Tj (>) Tj ( import_file file3) Tj T* (<) Tj (ThreadedTask 6 [import_file file3] RUNNING) Tj (>) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 307.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
q
q
1 0 0 1 0 0 cm
@@ -3921,39 +4724,240 @@ q
n -6 -6 468.6898 48 re B*
Q
q
+BT 1 0 0 1 0 29.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (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
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 229.8236 cm
+q
0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (with interpreter:) Tj T* ( for input_value in iter\(input_queue.get, EXIT\):) Tj T* ( output_queue.put\(interpreter.send\(input_value\)\)) Tj T* ET
+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 62.69291 136.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 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
q
-1 0 0 1 62.69291 253.4236 cm
+1 0 0 1 62.69291 92.62362 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
+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
+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 (17) Tj T* -235.3849 0 Td ET
+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
+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
+Q
+Q
+q
+1 0 0 1 62.69291 431.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 276 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 257.71 Tm /F4 10 Tf 12 TL (import time) Tj T* (import plac) Tj T* T* (class FakeImporter\(object\):) Tj T* ( "A fake importer with an import_file command") Tj T* ( thcommands = ['import_file']) Tj T* ( def __init__\(self, dsn\):) Tj T* ( self.dsn = dsn) Tj T* ( def import_file\(self, fname\):) Tj T* ( "Import a file into the database") Tj T* ( try:) Tj T* ( for n in range\(10000\):) Tj T* ( time.sleep\(.02\)) Tj T* ( if n % 100 == 99: # every two seconds) Tj T* ( yield 'Imported %d lines' % \(n+1\)) Tj T* ( if n % 10 == 9: # every 0.2 seconds) Tj T* ( yield # go back and check the TOBEKILLED status) Tj T* ( finally:) Tj T* ( print\('closing the file'\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.Interpreter\(plac.call\(FakeImporter\)\).interact\(\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+1 0 0 1 62.69291 398.8236 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 220.4236 cm
+1 0 0 1 62.69291 344.8236 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
+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
+Q
+q
+1 0 0 1 62.69291 308.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 5.71 Tm /F4 10 Tf 12 TL (thcommands = ['import_file']) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 308.8236 cm
+Q
+q
+1 0 0 1 62.69291 290.8236 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
+Q
+q
+1 0 0 1 62.69291 272.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+BT 1 0 0 1 0 4.82 Tm 12 TL /F4 10 Tf 0 0 0 rg (mpcommands = ['import_file']) Tj /F1 10 Tf (.) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 272.8236 cm
+Q
+q
+1 0 0 1 62.69291 242.8236 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
+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 (i) Tj (>) Tj ( import_file file3) Tj T* (<) Tj (MPTask 1 [import_file file3] SUBMITTED) Tj (>) Tj T* (i) Tj (>) Tj ( .kill 1) Tj T* (<) Tj (MPTask 1 [import_file file3] RUNNING) Tj (>) Tj T* (closing the file) Tj T* (i) Tj (>) Tj ( .o 1) Tj T* (<) Tj (MPTask 1 [import_file file3] KILLED) Tj (>) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) Tj T* (i) Tj (>) 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
+Q
+Q
+
+endstream
+
+endobj
+% 'R151': class PDFStream
+151 0 obj
+% page stream
+<< /Length 7386 >>
+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
+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 663.0236 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 633.0236 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 600.0236 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 178.4236 cm
+1 0 0 1 62.69291 558.0236 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 172.4236 cm
+1 0 0 1 62.69291 552.0236 cm
Q
q
-1 0 0 1 62.69291 172.4236 cm
+1 0 0 1 62.69291 552.0236 cm
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 534.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -3973,13 +4977,13 @@ q
Q
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 534.0236 cm
Q
q
-1 0 0 1 62.69291 154.4236 cm
+1 0 0 1 62.69291 534.0236 cm
Q
q
-1 0 0 1 62.69291 94.42362 cm
+1 0 0 1 62.69291 474.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
@@ -4057,107 +5061,116 @@ q
Q
Q
q
-1 0 0 1 62.69291 94.42362 cm
+1 0 0 1 62.69291 474.0236 cm
Q
q
-1 0 0 1 62.69291 94.42362 cm
+1 0 0 1 62.69291 474.0236 cm
Q
q
-1 0 0 1 56.69291 56.69291 cm
+1 0 0 1 62.69291 444.0236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+q
+1 0 0 1 6 15 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (3.) Tj T* -5.66 0 Td ET
Q
Q
-
-endstream
-
-endobj
-% 'R129': class PDFStream
-129 0 obj
-% page stream
-<< /Length 4712 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 735.0236 cm
+1 0 0 1 23 3 cm
+q
+BT 1 0 0 1 0 16.82 Tm 5.126647 Tw 12 TL /F1 10 Tf 0 0 0 rg (for testing call the ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (method in the appropriate context or use the) Tj T* 0 Tw /F4 10 Tf (Interpreter.doctest ) Tj /F1 10 Tf (feature;) Tj T* ET
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 444.0236 cm
+Q
+q
+1 0 0 1 62.69291 444.0236 cm
+Q
+q
+1 0 0 1 62.69291 414.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
1 0 0 1 6 15 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (3.) Tj T* -5.66 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (4.) Tj T* -5.66 0 Td ET
Q
Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 16.82 Tm 5.126647 Tw 12 TL /F1 10 Tf 0 0 0 rg (for testing call the ) Tj /F4 10 Tf (Interpreter.check ) Tj /F1 10 Tf (method in the appropriate context or use the) Tj T* 0 Tw /F4 10 Tf (Interpreter.doctest ) Tj /F1 10 Tf (feature;) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.356457 Tw 12 TL /F1 10 Tf 0 0 0 rg (if you need to go at a lower level, you may need to call the ) Tj /F4 10 Tf (Interpreter.send ) Tj /F1 10 Tf (method which) Tj T* 0 Tw (returns a \(finished\) ) Tj /F4 10 Tf (Task ) Tj /F1 10 Tf (object.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 735.0236 cm
+1 0 0 1 62.69291 414.0236 cm
Q
q
-1 0 0 1 62.69291 735.0236 cm
+1 0 0 1 62.69291 414.0236 cm
Q
q
-1 0 0 1 62.69291 717.0236 cm
+1 0 0 1 62.69291 384.0236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
q
-1 0 0 1 6 3 cm
+1 0 0 1 6 15 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (4.) Tj T* -5.66 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 5.66 0 Td (5.) Tj T* -5.66 0 Td ET
Q
Q
q
1 0 0 1 23 3 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you need to go at a lower level, you may need to call the ) Tj /F4 10 Tf (Interpreter.send ) Tj /F1 10 Tf (method.) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.469269 Tw 12 TL /F1 10 Tf 0 0 0 rg (long running command can be executed in the background as threads or processes: just declare) Tj T* 0 Tw (them in the lists ) Tj /F4 10 Tf (thcommands ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (mpcommands ) Tj /F1 10 Tf (respectively.) Tj T* ET
Q
Q
q
Q
Q
q
-1 0 0 1 62.69291 717.0236 cm
+1 0 0 1 62.69291 384.0236 cm
Q
q
-1 0 0 1 62.69291 717.0236 cm
+1 0 0 1 62.69291 384.0236 cm
Q
q
-1 0 0 1 62.69291 699.0236 cm
+1 0 0 1 62.69291 366.0236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Moreover, remember that ) Tj /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 666.0236 cm
+1 0 0 1 62.69291 333.0236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Appendix: custom annotation objects) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 636.0236 cm
+1 0 0 1 62.69291 303.0236 cm
q
BT 1 0 0 1 0 16.82 Tm .578651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses an ) Tj /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 606.0236 cm
+1 0 0 1 62.69291 273.0236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .083735 Tw (Advanced users can implement their own annotation objects. For instance, here is an example of how you) Tj T* 0 Tw (could implement annotations for positional arguments:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 476.8236 cm
+1 0 0 1 62.69291 143.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -4178,14 +5191,31 @@ Q
Q
Q
q
-1 0 0 1 62.69291 456.8236 cm
+1 0 0 1 62.69291 123.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 279.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 (19) Tj T* -235.3849 0 Td ET
+Q
+Q
+
+endstream
+
+endobj
+% 'R152': class PDFStream
+152 0 obj
+% page stream
+<< /Length 2033 >>
+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
@@ -4206,14 +5236,14 @@ Q
Q
Q
q
-1 0 0 1 62.69291 259.6236 cm
+1 0 0 1 62.69291 575.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 130.4236 cm
+1 0 0 1 62.69291 446.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -4234,336 +5264,370 @@ Q
Q
Q
q
-1 0 0 1 62.69291 98.42362 cm
+1 0 0 1 62.69291 402.6236 cm
q
-BT 1 0 0 1 0 16.82 Tm .713516 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can go on and define ) Tj /F4 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (Flag ) Tj /F1 10 Tf (classes, if you like. Using custom annotation objects you ) Tj T* 0 Tw .17528 Tw (could do advanced things like extracting the annotations from a configuration file or from a database, but I) Tj T* 0 Tw ET
+BT 1 0 0 1 0 28.82 Tm .713516 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can go on and define ) Tj /F4 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F4 10 Tf (Flag ) Tj /F1 10 Tf (classes, if you like. Using custom annotation objects you) Tj T* 0 Tw .17528 Tw (could do advanced things like extracting the annotations from a configuration file or from a database, but I) Tj T* 0 Tw (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
Q
Q
q
1 0 0 1 56.69291 56.69291 cm
q
0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL 235.3849 0 Td (20) Tj T* -235.3849 0 Td ET
Q
Q
endstream
endobj
-% 'R130': class PDFStream
-130 0 obj
-% page stream
-<< /Length 379 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 753.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
-Q
-Q
-q
-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
-% 'R131': class PDFPageLabels
-131 0 obj
+% 'R153': class PDFPageLabels
+153 0 obj
% Document Root
<< /Nums [ 0
- 132 0 R
+ 154 0 R
1
- 133 0 R
+ 155 0 R
2
- 134 0 R
+ 156 0 R
3
- 135 0 R
+ 157 0 R
4
- 136 0 R
+ 158 0 R
5
- 137 0 R
+ 159 0 R
6
- 138 0 R
+ 160 0 R
7
- 139 0 R
+ 161 0 R
8
- 140 0 R
+ 162 0 R
9
- 141 0 R
+ 163 0 R
10
- 142 0 R
+ 164 0 R
11
- 143 0 R
+ 165 0 R
12
- 144 0 R
+ 166 0 R
13
- 145 0 R
+ 167 0 R
14
- 146 0 R
+ 168 0 R
15
- 147 0 R ] >>
-endobj
-% 'R132': class PDFPageLabel
-132 0 obj
+ 169 0 R
+ 16
+ 170 0 R
+ 17
+ 171 0 R
+ 18
+ 172 0 R
+ 19
+ 173 0 R ] >>
+endobj
+% 'R154': class PDFPageLabel
+154 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R133': class PDFPageLabel
-133 0 obj
+% 'R155': class PDFPageLabel
+155 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R134': class PDFPageLabel
-134 0 obj
+% 'R156': class PDFPageLabel
+156 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R135': class PDFPageLabel
-135 0 obj
+% 'R157': class PDFPageLabel
+157 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R136': class PDFPageLabel
-136 0 obj
+% 'R158': class PDFPageLabel
+158 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R137': class PDFPageLabel
-137 0 obj
+% 'R159': class PDFPageLabel
+159 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R138': class PDFPageLabel
-138 0 obj
+% 'R160': class PDFPageLabel
+160 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R139': class PDFPageLabel
-139 0 obj
+% 'R161': class PDFPageLabel
+161 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R140': class PDFPageLabel
-140 0 obj
+% 'R162': class PDFPageLabel
+162 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R141': class PDFPageLabel
-141 0 obj
+% 'R163': class PDFPageLabel
+163 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R142': class PDFPageLabel
-142 0 obj
+% 'R164': class PDFPageLabel
+164 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R143': class PDFPageLabel
-143 0 obj
+% 'R165': class PDFPageLabel
+165 0 obj
% None
<< /S /D
/St 12 >>
endobj
-% 'R144': class PDFPageLabel
-144 0 obj
+% 'R166': class PDFPageLabel
+166 0 obj
% None
<< /S /D
/St 13 >>
endobj
-% 'R145': class PDFPageLabel
-145 0 obj
+% 'R167': class PDFPageLabel
+167 0 obj
% None
<< /S /D
/St 14 >>
endobj
-% 'R146': class PDFPageLabel
-146 0 obj
+% 'R168': class PDFPageLabel
+168 0 obj
% None
<< /S /D
/St 15 >>
endobj
-% 'R147': class PDFPageLabel
-147 0 obj
+% 'R169': class PDFPageLabel
+169 0 obj
% None
<< /S /D
/St 16 >>
endobj
+% 'R170': class PDFPageLabel
+170 0 obj
+% None
+<< /S /D
+ /St 17 >>
+endobj
+% 'R171': class PDFPageLabel
+171 0 obj
+% None
+<< /S /D
+ /St 18 >>
+endobj
+% 'R172': class PDFPageLabel
+172 0 obj
+% None
+<< /S /D
+ /St 19 >>
+endobj
+% 'R173': class PDFPageLabel
+173 0 obj
+% None
+<< /S /D
+ /St 20 >>
+endobj
xref
-0 148
+0 174
0000000000 65535 f
0000000113 00000 n
-0000000270 00000 n
-0000000435 00000 n
-0000000610 00000 n
-0000000791 00000 n
-0000001043 00000 n
-0000001293 00000 n
-0000001551 00000 n
-0000001712 00000 n
-0000001906 00000 n
-0000002148 00000 n
-0000002390 00000 n
-0000002632 00000 n
-0000002874 00000 n
-0000003116 00000 n
-0000003359 00000 n
-0000003602 00000 n
-0000003845 00000 n
-0000004088 00000 n
-0000004331 00000 n
-0000004574 00000 n
-0000004803 00000 n
-0000004989 00000 n
-0000005232 00000 n
-0000005475 00000 n
-0000005718 00000 n
-0000005961 00000 n
-0000006204 00000 n
-0000006447 00000 n
-0000006690 00000 n
-0000006933 00000 n
-0000007176 00000 n
-0000007419 00000 n
-0000007662 00000 n
-0000007904 00000 n
-0000008156 00000 n
-0000008408 00000 n
-0000008660 00000 n
-0000008912 00000 n
-0000009164 00000 n
-0000009407 00000 n
-0000009644 00000 n
-0000010256 00000 n
-0000010513 00000 n
-0000010831 00000 n
-0000011088 00000 n
-0000011330 00000 n
-0000011642 00000 n
-0000011939 00000 n
-0000012191 00000 n
-0000012443 00000 n
-0000012695 00000 n
-0000012955 00000 n
-0000013207 00000 n
-0000013468 00000 n
-0000013720 00000 n
-0000013981 00000 n
-0000014218 00000 n
-0000014608 00000 n
-0000014859 00000 n
-0000015095 00000 n
-0000015422 00000 n
-0000015694 00000 n
-0000015946 00000 n
-0000016183 00000 n
-0000016519 00000 n
-0000016771 00000 n
-0000017028 00000 n
-0000017265 00000 n
-0000017601 00000 n
-0000017860 00000 n
-0000018112 00000 n
-0000018364 00000 n
-0000018623 00000 n
-0000018882 00000 n
-0000019134 00000 n
-0000019391 00000 n
-0000019643 00000 n
-0000019902 00000 n
-0000020161 00000 n
-0000020413 00000 n
-0000020665 00000 n
-0000020917 00000 n
-0000021169 00000 n
-0000021407 00000 n
-0000021837 00000 n
-0000022134 00000 n
-0000022384 00000 n
-0000022622 00000 n
-0000022935 00000 n
-0000023232 00000 n
-0000023469 00000 n
-0000023787 00000 n
-0000024058 00000 n
-0000024296 00000 n
-0000024623 00000 n
-0000024861 00000 n
-0000025165 00000 n
-0000025448 00000 n
-0000025611 00000 n
-0000025891 00000 n
-0000026020 00000 n
-0000026196 00000 n
-0000026416 00000 n
-0000026622 00000 n
-0000026817 00000 n
-0000027015 00000 n
-0000027217 00000 n
-0000027411 00000 n
-0000027606 00000 n
-0000027811 00000 n
-0000028020 00000 n
-0000028208 00000 n
-0000028392 00000 n
-0000028638 00000 n
-0000037365 00000 n
-0000041701 00000 n
-0000045945 00000 n
-0000052177 00000 n
-0000058154 00000 n
-0000062905 00000 n
-0000066988 00000 n
-0000071475 00000 n
-0000076675 00000 n
-0000080981 00000 n
-0000086431 00000 n
-0000090428 00000 n
-0000096181 00000 n
-0000102403 00000 n
-0000107218 00000 n
-0000107703 00000 n
-0000108015 00000 n
-0000108094 00000 n
-0000108173 00000 n
-0000108252 00000 n
-0000108331 00000 n
-0000108410 00000 n
-0000108489 00000 n
-0000108568 00000 n
-0000108647 00000 n
-0000108726 00000 n
-0000108806 00000 n
-0000108886 00000 n
-0000108966 00000 n
-0000109046 00000 n
-0000109126 00000 n
-0000109206 00000 n
+0000000257 00000 n
+0000000422 00000 n
+0000000597 00000 n
+0000000778 00000 n
+0000001030 00000 n
+0000001280 00000 n
+0000001538 00000 n
+0000001699 00000 n
+0000001893 00000 n
+0000002135 00000 n
+0000002377 00000 n
+0000002619 00000 n
+0000002861 00000 n
+0000003103 00000 n
+0000003346 00000 n
+0000003589 00000 n
+0000003832 00000 n
+0000004075 00000 n
+0000004318 00000 n
+0000004561 00000 n
+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
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\275o9\206\006\260\252\254St*\243\362\376\031\013) (\275o9\206\006\260\252\254St*\243\362\376\031\013)]
+ [(\317\011\333\344\342}}2\233\022:\30301\344\211) (\317\011\333\344\342}}2\233\022:\30301\344\211)]
- /Info 100 0 R
- /Root 99 0 R
- /Size 148 >>
+ /Info 115 0 R
+ /Root 114 0 R
+ /Size 174 >>
startxref
-109255
+136632
%%EOF
diff --git a/plac/doc/plac_adv.txt b/plac/doc/plac_adv.txt
index d8a421c..9ff1dab 100644
--- a/plac/doc/plac_adv.txt
+++ b/plac/doc/plac_adv.txt
@@ -1,9 +1,9 @@
-Testing and scripting your applications with plac
+Advanced usages of plac
=========================================================
:Author: Michele Simionato
:E-mail: michele.simionato@gmail.com
-:Date: June 2010
+:Date: July 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``
@@ -11,7 +11,9 @@ Testing and scripting your applications with plac
:Requires: Python 2.5+
*The present document discusses a few of the advanced use
-cases for plac. It assumes you have already read an understood the
+cases for plac. It shows how to write interactive and non-interactive
+interpreters with plac, and how to use plac for testing and scripting a generic
+application. It assumes you have already read an understood the
basic documentation.*
.. contents::
@@ -77,13 +79,14 @@ and non-interactively::
$ python shelve_interpreter.py .clear # non-interactive use
cleared the shelve
-Here is an usage session, using rlwrap_ to enable readline features
-(rlwrap_ is available in Unix-like systems)::
+Here is an usage session::
- $ rlwrap python shelve_interpreter.py -i # interactive use
- usage: shelve_interpreter.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
+ $ python shelve_interpreter.py -i # interactive use
+ A simple interface to a shelve. Use .help to see the available commands.
+ i> .help
+ Commands: .help, .showall, .clear, .delete
+ <param> ...
+ <param=value> ...
i> a=1
setting a=1
i> a
@@ -106,20 +109,22 @@ reads commands from the console and send them to the
underlying interpreter, until the user send a CTRL-D
command (CTRL-Z in Windows). There is a default
argument ``prompt='i> '`` which
-can be used to change the prompt. The message displayed
-by default is the argparse-provided usage message, but can be
-customized by setting an ``.intro`` attribute on the main function.
+can be used to change the prompt. The text displayed at the beginning
+of the interactive session is the docstring of the main function.
+``plac`` also understands command abbreviations: in this example
+``del`` is an abbreviation for ``delete``. In case of ambiguous
+abbreviations plac_ raises a ``NameError``.
-Notice that ``plac.Interpreter`` is available only if you are using a recent
-version of Python (>= 2.5), because it is a context manager object
-which uses extended generators internally.
-
-You can conveniently test your application in interactive mode.
-However manual testing is a poor substitute for automatic testing.
+Finally I must notice that the ``plac.Interpreter`` is available only if you
+are using a recent version of Python (>= 2.5), because it is a context
+manager object which uses extended generators internally.
Testing a plac application
-----------------------------------------------------------
+You can conveniently test your application in interactive mode.
+However manual testing is a poor substitute for automatic testing.
+
In principle, one could write automatic tests for the
``ishelve`` application by using ``plac.call`` directly:
@@ -143,8 +148,7 @@ the ``check`` method of ``Interpreter`` objects:
The method ``.check(given_input, expected_output)`` works on strings
and raises an ``AssertionError`` if the output produced by the
interpreter is different from the expected output for the given input.
-
-``AssertionError`` is catched by tools like ``py.test`` and
+Notice that ``AssertionError`` is catched by tools like ``py.test`` and
``nosetests`` and actually ``plac`` tests are intended to be run with
such tools.
@@ -175,7 +179,7 @@ are still too low-level for my taste. The ``Interpreter`` class provides
support for doctest-style tests, a.k.a. *plac easy tests*.
By using plac easy tests you can cut and paste your interactive session and
-turn it into a runnable automatics test!
+turn it into a runnable automatics test.
Consider for instance the following file ``ishelve.placet`` (the ``.placet``
extension is a mnemonic for plac easy tests):
@@ -197,28 +201,30 @@ You can test ``ishelve.placet`` file by calling the
Internally ``Interpreter.doctests`` invokes something like ``Interpreter.check``
multiple times inside the same context and compare the output with the
-expected output: if even a check fails, the whole test fail. The
-easy tests supported by ``plac`` are *not* unittests: they should be
-used to model user interaction when the order of the operations
-matters. Since the single subtests in a ``.placet`` file are not
-independent, it makes sense to exit immediately at the first failure.
-
-The support for doctests in plac_ comes nearly for free, thanks to the shlex_
-module in the standard library, which is able to parse simple
+expected output: if even a check fails, the whole test fail.
+
+You should realize tha the easy tests supported by ``plac`` are *not*
+unittests: they are functional tests. They model then user interaction and the
+order of the operations generally matters. The single subtests in a
+``.placet`` file are not independent and it makes sense to exit
+immediately at the first failure.
+
+The support for doctests in plac_ comes nearly for free, thanks to the
+shlex_ module in the standard library, which is able to parse simple
languages as the ones you can implement with plac_. In particular,
thanks to shlex_, plac_ is able to recognize comments (the default
-comment character is ``#``), continuation lines, escape sequences and
-more. Look at the shlex_ documentation if you need to customize how
-the language is interpreted.
+comment character is ``#``), escape sequences and more. Look at the
+shlex_ documentation if you need to customize how the language is
+interpreted.
In addition, I have implemented from scratch some support for line number
recognition, so that if a test fail you get the line number of the
failing command. This is especially useful if your tests are
-stored in external files (plac easy tests does not need to be in
+stored in external files, even if plac easy tests does not need to be in
a file: you can just pass to the ``.doctest`` method a list of
-strings corresponding to the lines of the file).
+strings corresponding to the lines of the file.
-At the present plac easy tests do not use any code from the doctest
+At the present plac_ does not use any code from the doctest
module, but the situation may change in the future (it would be nice
if plac_ could reuse doctests directives like ELLIPSIS).
@@ -243,7 +249,7 @@ or ``py.test`` as follow::
Here you should notice that usage of ``plac.import_main``, an utility
which is able to import the main function of the script specified in
-the shabng line. You can use both the full path name of the
+the shebang line. You can use both the full path name of the
tool, or a relative path name. In this case the runner look at the
environment variable ``PLACPATH`` and it searches
the plac tool in the directories specified there (``PLACPATH`` is just
@@ -267,15 +273,15 @@ background in case of unexpected errors. The implementation of
interpreters *wrap the errors, but does not eat them*: the errors are
always accessible and can be re-raised on demand.
-In particular consider the following batch file, which contains a syntax
-error (``.dl`` instead of ``.del``):
+The exception is the case of invalid commands, which are skipped.
+Consider for instance the following batch file, which contains a
+mispelled command (``.dl`` instead of ``.del``):
.. include:: ishelve.plac
:literal:
-If you execute the batch file, the interpreter will raise a ``SystemExit``
-with an appropriated error message at the ``.dl`` line and the last command
-will *not* be executed::
+If you execute the batch file, the interpreter will print a ``.dl: not found``
+at the ``.dl`` line and will continue::
$ python -c "import plac, ishelve
plac.Interpreter(ishelve.main).execute(open('ishelve.plac'), verbose=True)"
@@ -290,20 +296,23 @@ will *not* be executed::
i> .del a
deleted a
i> .dl b
- unrecognized arguments: .dl
+ 2
+ .dl: not found
+ i> .show
+ b=2
The ``verbose`` flag is there to show the lines which are being interpreted
(prefixed by ``i>``). This is done on purpose, so that you can cut and paste
the output of the batch script and turn it into a ``.placet`` test
(cool, isn't it?).
-Containers of commands
+Implementing subcommands
----------------------------------------
When I discussed the ``ishelve`` implementation in the `basic
documentation`_, I said that it looked like a poor man implementation
of an object system as a chain of elifs; I also said that plac_ was
-able to do better. Here I will substantiate my claim.
+able to do much better than that. Here I will substantiate my claim.
plac_ is actually able to infer a set of subparsers from a
generic container of commands. This is useful if you want to
@@ -314,9 +323,10 @@ Technically a container of commands is any object with a ``.commands`` attribute
listing a set of functions or methods which are valid commands. A command
container may have initialization/finalization hooks (``__enter__/__exit__``)
and dispatch hooks (``__missing__``, invoked for invalid command names).
+Moreover, only when using command containers plac_ is able to provide
+automatic autocompletion of commands.
-Using this feature the shelve interface can be rewritten in a more
-object-oriented way as follows:
+Rhe shelve interface can be rewritten in an object-oriented way as follows:
.. include:: ishelve2.py
:literal:
@@ -333,14 +343,31 @@ implemented any error checking in the ``show`` and ``delete`` methods
on purpose, to verify that plac_ works correctly in the presence of
exceptions.
+When working with command containers, plac_ automatically adds two
+special commands to the set of provided commands: ``.help``
+and ``.last_tb``. The ``.help`` command is the easier to understand:
+when invoked without arguments it displays the list of available commands
+with the same formatting of the cmd_ module; when invoked with the name of
+a command it displays the usage message for that command.
+The ``.last_tb`` command is useful when debugging: in case of errors,
+it allows you to display the traceback of the last executed command.
+
Here is a session of usage on an Unix-like operating system::
- $ rlwrap python ishelve2.py
- Operating on /home/micheles/conf.shelve. Available commands:
- set
- show
- showall
- delete
+ $ python ishelve2.py
+ A minimal interface over a shelve object.
+ Operating on /home/micheles/conf.shelve.
+ .help to see the available commands.
+ i> .help
+
+ special commands
+ ================
+ .help .last_tb
+
+ custom commands
+ ===============
+ delete set show showall
+
i> delete
deleting everything
i> set a pippo
@@ -358,75 +385,108 @@ Here is a session of usage on an Unix-like operating system::
i> showall
b = lippo
i> delete a
- DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
+ deleting a
+ KeyError: 'a'
+ i> .last_tb
+ File "/usr/local/lib/python2.6/dist-packages/plac-0.6.0-py2.6.egg/plac_ext.py", line 190, in _wrap
+ for value in genobj:
+ File "./ishelve2.py", line 37, in delete
+ del self.sh[name] # no error checking
+ File "/usr/lib/python2.6/shelve.py", line 136, in __delitem__
+ del self.dict[key]
i>
Notice that in interactive mode the traceback is hidden, unless
you pass the ``verbose`` flag to the ``Interpreter.interact`` method.
-The interactive mode of ``plac`` can be used as a replacement of the
-``cmd`` module in the standard library. There are a few differences,
-however. For instance you miss tab completion, even if use rlwrap_
-(you get persistent command history for free, however). This is not
-a big issue, since ``plac`` understands command abbreviations.
-
-If an abbreviation is ambiguous, plac_ warns you::
-
- $ rlwrap python ishelve2.py
- usage: plac_runner.py ishelve2.py [-h] {delete,set,showall,show} ...
- i> sh
- NameError: Ambiguous command 'sh': matching ['showall', 'show']
+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:
+
+.. include:: sql_interface.py
+ :literal:
-For ``cmd`` lovers
---------------------------------------------------------
+Here is an example of usage::
+
+ $ python sql_interface.py <some dsn>
+ sql> SELECT a.* FROM TABLE1 AS a INNER JOIN TABLE2 AS b ON a.id = b.id
+ ...
+
+You can check that entering just ``sel`` and pressing TAB the readline library
+completes the ``SELECT`` keyword for you and makes it upper case; idem for
+``FROM``, ``INNER``, ``JOIN`` and even for the names of the tables. An
+obvious improvement is to read the names of the tables by introspecting
+the database: actually you can even read the names of the views and of
+the columns, and have full autocompletion. All the entered commands
+and recorded and saved in the file ``~/.sql_interface.history`` when
+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).
+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
+not need to be the GPL (actually it is a BSD
+do-whatever-you-want-with-it licence).
+
+The interactive mode of ``plac`` can be used as a replacement of the
+cmd_ module in the standard library. It is actually better than cmd_:
+for instance, the ``.help`` command is more powerful, since it
+provides information about the arguments accepted by the given command::
+
+ i> .help set
+ usage: set name value
+
+ set name value
+
+ positional arguments:
+ name
+ value
-I have been using the cmd_ module of the standard library for years.
-I have also written a much enhanced ``cmd2`` module which we are using
-internally at work and from which I have taken some ideas used in plac_.
-In many ways plac_ makes the cmd_ module obsolete,
-but I realize why many nostalgic souls would still use cmd_, especially
-until plac_ does not grow real auto-completion features, instead of
-relying on rlwrap_. But there must not be competition between plac_
-and cmd_: actually the two can happily work togethere. For this
-reason I have put in the ``plac_ext`` module a few lines of code
-for gluing together cmd_ and plac_, the ``cmd_interface``.
-Using the ``cmd_interface`` is quite trivial: give to it a plac
-command container and you will get in exchange a ``cmd.Cmd`` object:
-
-.. include:: cmd_ex.py
- :literal:
-
-Here is an example of interactive session::
-
- $ python cmd_ex.py
- (Cmd) help
+ i> .help delete
+ usage: delete [name]
- Documented commands (type help <topic>):
- ========================================
- delete set show showall
+ delete given parameter (or everything)
- Undocumented commands:
- ======================
- EOF help
+ positional arguments:
+ name
+
+ i> .help show
+ usage: show [names [names ...]]
- (Cmd) set a 1
- setting a=1
- (Cmd) show a
- a = 1
- (Cmd) showall
- a = 1
- (Cmd) delete b
- KeyError: 'b'
- (Cmd) EOF [or CTRL-D]
-
-Internally the ``cmd_interface`` builds a ``cmd.Cmd`` class and adds
-to it the ``do_`` methods corresponding to the commands in the container,
-then it returns a ``cmd.Cmd`` instance.
-
-The ``cmd_interface`` is just a proof of concept: it is there so that you
-can study the source code and see an example of integration of plac_
-with a different framework. It may change and even go away in future
-releases of plac_.
+ show given parameters
+
+ positional arguments:
+ names
+
+As you can imagine, the help message is provided by the underlying argparse_
+subparser (there is a subparser for each command). plac_ commands accept
+options, flags, varargs, keyword arguments, arguments with defaults,
+arguments with a fixed number of choices, type conversion and all the
+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
+future releases. It was meaningful in the past, when plac_ did not support
+readline.
+
+Notice that if an abbreviation is ambiguous, plac_ warns you::
+
+ i> sh
+ NameError: Ambiguous command 'sh': matching ['showall', 'show']
The plac runner
--------------------------------------------------------
@@ -464,18 +524,17 @@ plac runner does not eat the traceback.
The runner can also be used to run Python modules in interactive
mode and non-interactive mode. If you put this alias in your bashrc
- ``alias plac="rlwrap plac_runner.py"``
+ ``alias plac="plac_runner.py"``
(or you define a suitable ``plac.bat`` script in Windows) you can
run the ``ishelve2.py`` script in interactive mode as
follows::
$ plac -i ishelve2.py
- Operating on /home/micheles/conf.shelve. Available commands:
- set
- show
- showall
- delete
+ A minimal interface over a shelve object.
+ Operating on /home/micheles/conf.shelve.
+ .help to see the available commands.
+
i> del
deleting everything
i> set a 1
@@ -690,6 +749,163 @@ The same trick also work for processes; you could run the interpreter
loop in a separate process and send commands to it via the Queue
class provided by the multiprocessing_ module.
+Long running commands
+---------------------------------------
+
+As we saw, by default a plac_ interpreter blocks until
+the command terminates. This is an issue, in the sense that it makes
+the interactive experience quite painful for long running commands. An
+example is better than a thousand words, so consider the following
+fake importer:
+
+.. include:: importer1.py
+ :literal:
+
+If you run the ``import_file`` command, you will have to wait for 200 seconds
+before entering a new command::
+
+ $ python importer1.py dsn
+ A fake importer with an import_file command
+ i> import_file file1
+ Imported 100 lines
+ Imported 200 lines
+ Imported 300 lines
+ ... <wait 3+ minutes>
+ Imported 10000 lines
+ closing the file
+
+Being unable to enter any other command is quite annoying: in such situation one
+would like to run the long running commands in the background, to keep
+the interface responsive. plac_ provides two ways to reach this goal: threads
+and processes.
+
+Threaded commands
+-----------------------------------------
+
+The most familiar way to execute a task in the background (even if not
+necessarily the best way) is to run it into a separated thread. In our
+example it is sufficient to replace the line
+
+ ``commands = ['import_file']``
+
+with
+
+ ``thcommands = ['import_file']``
+
+to tell to the plac_ interpreter that the command ``import_file`` should be
+run into a separated thread. Here is an example session::
+
+ i> import_file file1
+ <ThreadedTask 1 [import_file file1] RUNNING>
+
+The import task started in a separated thread. You can see the
+progress of the task by using the special command ``.output``::
+
+ i> .output 1
+ <ThreadedTask 1 [import_file file1] RUNNING>
+ Imported 100 lines
+ Imported 200 lines
+
+If you look after a while, you will get more lines of output::
+
+ i> .output 1
+ <ThreadedTask 1 [import_file file1] RUNNING>
+ Imported 100 lines
+ Imported 200 lines
+ Imported 300 lines
+ Imported 400 lines
+
+If you look after a time long enough, the task will be finished::
+
+ i> .output 1
+ <ThreadedTask 1 [import_file file1] FINISHED>
+
+You can launch many tasks one after the other::
+
+ i> import_file file2
+ <ThreadedTask 5 [import_file file2] RUNNING>
+ i> import_file file3
+ <ThreadedTask 6 [import_file file3] RUNNING>
+
+The ``.list`` command displays all the running tasks::
+
+ i> .list
+ <ThreadedTask 5 [import_file file2] RUNNING>
+ <ThreadedTask 6 [import_file file3] RUNNING>
+
+It is even possible to kill a task::
+
+ i> .kill 5
+ <ThreadedTask 5 [import_file file2] TOBEKILLED>
+ # wait a bit ...
+ closing the file
+ i> .output 5
+ <ThreadedTask 5 [import_file file2] KILLED>
+
+You should notice that since at the Python level it is impossible to kill
+a thread, the ``.kill`` commands works by setting the status of the task to
+``TOBEKILLED``. Internally the generator corresponding to the command
+is executed in the thread and the status is checked at each iteration:
+when the status become ``TOBEKILLED`` a ``GeneratorExit`` exception is
+raised and the thread terminates (softly, so that the ``finally`` clause
+is honored). In our example the generator is yielding
+back control once every 100 iterations, i.e. every two seconds (not much).
+In order to get a responsive interface it is a good idea to yield more
+often, for instance every 10 iterations (i.e. 5 times per second),
+as in the following code:
+
+.. include:: importer2.py
+ :literal:
+
+Running commands as external processes
+-----------------------------------------
+
+Threads are not loved much in the Python world and actually most people
+prefer to use processes instead. For this reason plac_ provides the
+option to execute long running commands as external processes. Unfortunately
+the current implementation only works in Unix-like operating systems
+(including Mac OS X) because it relies on fork via the multiprocessing_
+module.
+
+In our example, to enable the feature it is sufficient to replace the line
+
+ ``thcommands = ['import_file']``
+
+with
+
+ ``mpcommands = ['import_file']``.
+
+The user experience is exactly the same as with threads and you will not see any
+difference at the user interface level::
+
+ i> import_file file3
+ <MPTask 1 [import_file file3] SUBMITTED>
+ i> .kill 1
+ <MPTask 1 [import_file file3] RUNNING>
+ closing the file
+ i> .o 1
+ <MPTask 1 [import_file file3] KILLED>
+ Imported 100 lines
+ Imported 200 lines
+ i>
+
+Still, using processes is quite different than using threads: in
+particular, when using processes you can only yield pickleable values
+and you cannot re-raise an exception first raised in a different
+process, because traceback objects are not pickleable. Moreover,
+you cannot rely on automatic sharing of your objects.
+
+On the plus side, when using processes you do not need to worry about
+killing a command: they are killed immediately using a SIGTERM signal,
+and there is not a ``TOBEKILLED`` mechanism. Moreover, the killing is
+guaranteed to be soft: internally a command receiving a SIGTERM raises
+a ``TerminatedProcess`` exception which is trapped in the generator
+loop, so that the command is closed properly.
+
+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.
+
Summary
-------------------------------------------------------
@@ -711,7 +927,11 @@ rules are quite simple:
or use the ``Interpreter.doctest`` feature;
4. if you need to go at a lower level, you may need to call the
- ``Interpreter.send`` method.
+ ``Interpreter.send`` method which returns a (finished) ``Task`` object.
+
+5. long running command can be executed in the background as threads or
+ processes: just declare them in the lists ``thcommands`` and ``mpcommands``
+ respectively.
Moreover, remember that ``plac_runner.py`` is your friend.
@@ -767,8 +987,3 @@ pretty well for most users.
.. _distutils: http://docs.python.org/distutils/
.. _cmd: http://docs.python.org/library/cmd.html
.. _rlwrap: http://freshmeat.net/projects/rlwrap/
-
-.. If the script is invoked with no arguments, the default help is
- invoked: this is done by invoking the argparse_ low-level method
- ``.format_help``. In other words, the recognition of the couple
- ``-h/--help`` is disabled, but the original help feature is still there.
diff --git a/plac/doc/shelve_interpreter.help b/plac/doc/shelve_interpreter.help
index e8c5320..f666445 100644
--- a/plac/doc/shelve_interpreter.help
+++ b/plac/doc/shelve_interpreter.help
@@ -1,8 +1,9 @@
usage: shelve_interpreter.py [-h] [-interactive]
[subcommands [subcommands ...]]
-This script works both interactively and non-interactively. Use .help to see
-the internal commands.
+ This script works both interactively and non-interactively.
+ Use .help to see the internal commands.
+
positional arguments:
subcommands the commands of the underlying ishelve interpreter
diff --git a/plac/doc/sql_interface.py b/plac/doc/sql_interface.py
new file mode 100644
index 0000000..d045d03
--- /dev/null
+++ b/plac/doc/sql_interface.py
@@ -0,0 +1,25 @@
+import os, plac
+from sqlalchemy.ext.sqlsoup import SqlSoup
+
+SQLKEYWORDS = set(['select', 'from', 'inner', 'join', 'outer', 'left', 'right']
+ ) # and many others
+DBTABLES = set(['table1', 'table2']) # you can read them from the db schema
+
+COMPLETIONS = SQLKEYWORDS | DBTABLES
+
+class SqlInterface(object):
+ commands = ['SELECT']
+ def __init__(self, dsn):
+ self.soup = SqlSoup(dsn)
+ def SELECT(self, *args):
+ sql = 'SELECT ' + ' '.join(args)
+ 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'),
+ case_sensitive=False)
+
+if __name__ == '__main__':
+ plac.Interpreter(plac.call(SqlInterface)).interact(rl_input)
diff --git a/plac/doc/test_plac.py b/plac/doc/test_plac.py
index 450f9ff..86c600b 100644
--- a/plac/doc/test_plac.py
+++ b/plac/doc/test_plac.py
@@ -41,6 +41,13 @@ def check_help(name):
####################### tests ############################
+def test_expected_help():
+ for fname in os.listdir('.'):
+ if fname.endswith('.help'):
+ name = fname[:-5]
+ if name not in ('vcs', 'ishelve'):
+ yield check_help, fname[:-5]
+
p1 = parser_from(lambda delete, *args: None,
delete=('delete a file', 'option'))
@@ -114,13 +121,6 @@ def test_kwargs():
expect(SystemExit, plac.call, main, ['arg1', 'arg2', 'a=1', 'opt=2'])
-def test_expected_help():
- for fname in os.listdir('.'):
- if fname.endswith('.help'):
- name = fname[:-5]
- if name not in ('vcs', 'ishelve'):
- yield check_help, fname[:-5]
-
class Cmds(object):
add_help = False
commands = 'help', 'commit'
@@ -151,17 +151,24 @@ def test_doctest():
failure, tot= doctest.testfile('plac.txt', module_relative=False)
assert not failure, failure
+failing_scripts = set(['ishelve2.plac'])
+
+def check_script(args):
+ if failing_scripts.intersection(args):
+ assert subprocess.call(args) > 0, ( # expected failure
+ 'Unexpected success for %s' % ' '.join(args))
+ else:
+ assert subprocess.call(args) == 0 , 'Failed %s' % ' '.join(args)
+
def test_batch():
for batch in os.listdir('.'):
if batch.endswith('.plac'):
- args = [sys.executable, '../plac_runner.py', '-b', batch]
- yield subprocess.call, args
+ yield check_script, ['plac_runner.py', '-b', batch]
def test_placet():
for placet in os.listdir('.'):
if placet.endswith('.placet'):
- args = [sys.executable, '../plac_runner.py', '-t', placet]
- yield subprocess.call, args
+ yield check_script, ['plac_runner.py', '-t', placet]
if __name__ == '__main__':
n = 0
diff --git a/plac/plac.py b/plac/plac.py
index ce53890..4427ea2 100644
--- a/plac/plac.py
+++ b/plac/plac.py
@@ -24,7 +24,7 @@
## DAMAGE.
"""
-See doc/plac.pdf for the documentation.
+See doc/plac.pdf, doc/plac_adv.pdf for the documentation.
"""
__version__ = '0.6.0'
@@ -32,4 +32,4 @@ __version__ = '0.6.0'
from plac_core import *
if sys.version >= '2.5':
- from plac_ext import Interpreter, import_main
+ from plac_ext import Interpreter, import_main, ReadlineInput
diff --git a/plac/plac_core.py b/plac/plac_core.py
index 365732c..89cb9a7 100644
--- a/plac/plac_core.py
+++ b/plac/plac_core.py
@@ -13,6 +13,7 @@ try:
set
except NameError: # Python 2.3
from sets import Set as set
+from gettext import gettext as _
def annotations(**ann):
"""
@@ -29,7 +30,7 @@ def annotations(**ann):
for argname in ann:
if argname not in args:
raise NameError(
- 'Annotating non-existing argument: %s' % argname)
+ _('Annotating non-existing argument: %s') % argname)
f.__annotations__ = ann
return f
return annotate
@@ -72,33 +73,36 @@ PARSER_CFG = getfullargspec(argparse.ArgumentParser.__init__).args[1:]
def pconf(obj):
"Extracts the configuration of the underlying ArgumentParser from obj"
- cfg = dict(description=obj.__doc__)
+ cfg = dict(description=obj.__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
for name in dir(obj):
if name in PARSER_CFG: # argument of ArgumentParser
cfg[name] = getattr(obj, name)
return cfg
-def parser_from(obj):
+parser_registry = {}
+
+def parser_from(obj, **confparams):
"""
obj can be a callable or an object with a .commands attribute.
Returns an ArgumentParser.
- """
- if hasattr(obj, 'func'): # added by the task manager
- obj = obj.func
- if hasattr(obj, 'p'): # the underlying parser has been generated already
- return obj.p
- parser = obj.p = ArgumentParser(**pconf(obj))
- if hasattr(obj, 'commands'): # a command container
- for cmd in obj.commands:
- parser.addsubparser(cmd, getattr(obj, cmd))
+ """
+ try: # the underlying parser has been generated already
+ return parser_registry[obj]
+ except KeyError: # generate a new parser
+ pass
+ conf = pconf(obj).copy()
+ conf.update(confparams)
+ parser_registry[obj] = parser = ArgumentParser(**conf)
+ if hasattr(obj, 'commands') and obj.commands and not inspect.isclass(obj):
+ # a command container instance
+ parser.addsubcommands(obj.commands, obj, 'subcommands')
parser.missing = getattr(
obj, '__missing__',
lambda name: parser.error('No command %r' % name))
- parser.func = lambda : None
- parser.argspec = getfullargspec(parser.func)
- return parser
else:
- return parser._populated(obj)
+ parser.populate_from(obj)
+ return parser
def _extract_kwargs(args):
"Returns two lists: regular args and name=value args"
@@ -124,14 +128,14 @@ def _match_cmd(abbrev, commands):
return matches[0]
elif n > 1:
raise NameError(
- 'Ambiguous command %r: matching %s' % (abbrev, matches))
+ _('Ambiguous command %r: matching %s' % (abbrev, matches)))
class ArgumentParser(argparse.ArgumentParser):
"""
An ArgumentParser with .func and .argspec attributes, and possibly
.commands and .subparsers.
"""
- def consume(self, args, ignore_extra=False):
+ def consume(self, args):
"""Call the underlying function with the args. Works also for
command containers, by dispatching to the right subparser."""
arglist = list(args)
@@ -139,70 +143,74 @@ class ArgumentParser(argparse.ArgumentParser):
if hasattr(self, 'subparsers'):
subp, cmd = self._extract_subparser_cmd(arglist)
if subp is None and cmd is not None:
- return self.missing(cmd)
+ return cmd, self.missing(cmd)
elif subp is not None: # use the subparser
self = subp
- if self.argspec.varkw:
- arglist, kwargs = _extract_kwargs(arglist)
+ if hasattr(self, 'argspec') and self.argspec.varkw:
+ arglist, kwargs = _extract_kwargs(arglist) # modify arglist!
else:
kwargs = {}
- if ignore_extra: # ignore unrecognized arguments
- ns, self.extra_args = self.parse_known_args(arglist)
+ if hasattr(self, 'argspec') and self.argspec.varargs:
+ # ignore unrecognized arguments
+ ns, extraopts = self.parse_known_args(arglist)
else:
- ns = self.parse_args(arglist)
+ ns, extraopts = self.parse_args(arglist), []
args = [getattr(ns, a) for a in self.argspec.args]
varargs = getattr(ns, self.argspec.varargs or '', [])
collision = set(self.argspec.args) & set(kwargs)
if collision:
- self.error('colliding keyword arguments: %s' % ' '.join(collision))
- return cmd, self.func(*(args + varargs), **kwargs)
+ self.error(
+ _('colliding keyword arguments: %s') % ' '.join(collision))
+ return cmd, self.func(*(args + varargs + extraopts), **kwargs)
def _extract_subparser_cmd(self, arglist):
- "Extract the subparser from the first recognized argument"
- prefix = self.prefix_chars[0]
+ "Extract the right subparser from the first recognized argument"
+ optprefix = self.prefix_chars[0]
name_parser_map = self.subparsers._name_parser_map
for i, arg in enumerate(arglist):
- if not arg.startswith(prefix):
+ if not arg.startswith(optprefix):
cmd = _match_cmd(arg, name_parser_map)
del arglist[i]
- return name_parser_map.get(cmd), cmd
+ return name_parser_map.get(cmd), cmd or arg
return None, None
- def addsubparser(self, cmd, func):
- "Add a subparser for a command"
+ def addsubcommands(self, commands, obj, title=None, cmdprefix=''):
+ "Extract a list of subcommands from obj and add them to the parser"
+ if hasattr(obj, cmdprefix) and obj.cmdprefix in self.prefix_chars:
+ raise ValueError(_('The prefix %r is already taken!' % cmdprefix))
if not hasattr(self, 'subparsers'):
- self.subparsers = self.add_subparsers(
- title='subcommands', help='-h to get additional help')
- subp = self.subparsers.add_parser(cmd, **pconf(func))
- return subp._populated(func)
+ self.subparsers = self.add_subparsers(title=title)
+ elif title:
+ self.add_argument_group(title=title) # populate ._action_groups
+ prefixlen = len(getattr(obj, 'cmdprefix', ''))
+ for cmd in commands:
+ func = getattr(obj, cmd[prefixlen:]) # strip the prefix
+ self.subparsers.add_parser(
+ cmd, add_help=False, **pconf(func)).populate_from(func)
def _set_func_argspec(self, obj):
"""Extracts the signature from a callable object and adds an .argspec
attribute to the parser. Also adds a .func reference to the object."""
- self.func = obj
if inspect.isfunction(obj):
self.argspec = getfullargspec(obj)
- obj.p = self
elif inspect.ismethod(obj):
self.argspec = getfullargspec(obj)
- obj.im_func.p = self
del self.argspec.args[0] # remove first argument
elif inspect.isclass(obj):
self.argspec = getfullargspec(obj.__init__)
- obj.__init__.im_func.p = self
del self.argspec.args[0] # remove first argument
elif hasattr(obj, '__call__'):
self.argspec = getfullargspec(obj.__call__)
- obj.__call__.im_func.p = self
del self.argspec.args[0] # remove first argument
else:
- raise TypeError('Could not determine signature of %r' % obj)
+ raise TypeError(_('Could not determine the signature of %r') % obj)
+ self.func = obj
+ parser_registry[obj] = self
- def _populated(self, func):
+ def populate_from(self, func):
"""
Extract the arguments from the attributes of the passed function
- and return a populated ArgumentParser instance. As a side
- effect, adds a .p attribute to func.
+ and return a populated ArgumentParser instance.
"""
self._set_func_argspec(func)
f = self.argspec
@@ -226,7 +234,7 @@ class ArgumentParser(argparse.ArgumentParser):
shortlong = (prefix + name,)
elif default is NONE: # required argument
self.add_argument(name, help=a.help, type=a.type,
- choices=a.choices, metavar=metavar)
+ choices=a.choices, metavar=metavar)
else: # default argument
self.add_argument(
name, nargs='?', help=a.help, default=dflt,
@@ -239,35 +247,45 @@ class ArgumentParser(argparse.ArgumentParser):
choices=a.choices, metavar=metavar, *shortlong)
elif a.kind == 'flag':
if default is not NONE and default is not False:
- raise TypeError('Flag %r wants default False, got %r' %
+ raise TypeError(_('Flag %r wants default False, got %r') %
(name, default))
self.add_argument(action='store_true', help=a.help, *shortlong)
if f.varargs:
a = Annotation.from_(f.annotations.get(f.varargs, ()))
self.add_argument(f.varargs, nargs='*', help=a.help, default=[],
- type=a.type, metavar=a.metavar)
+ type=a.type, metavar=a.metavar)
if f.varkw:
a = Annotation.from_(f.annotations.get(f.varkw, ()))
self.add_argument(f.varkw, nargs='*', help=a.help, default={},
- type=a.type, metavar=a.metavar)
- return self
+ type=a.type, metavar=a.metavar)
+
+ def help_cmd(self, cmd):
+ "Return the help message for a subcommand"
+ p = self.subparsers._name_parser_map.get(cmd)
+ if p is None:
+ return _('Unknown command %s' % cmd)
+ else:
+ return p.format_help()
+
+ def print_actions(self):
+ "Useful for debugging"
+ print(self)
+ for a in self._actions:
+ print(a)
def iterable(obj):
"Any object with an __iter__ method which is not a string"
return hasattr(obj, '__iter__') and not isinstance(obj, basestring)
-def call(obj, arglist=sys.argv[1:], ignore_extra=False):
+def call(obj, arglist=sys.argv[1:], eager=True):
"""
If obj is a function or a bound method, parse the given arglist
- by using the argument parser inferred from the annotations of obj
+ by using the parser inferred from the annotations of obj
and call obj with the parsed arguments.
If obj is an object with attribute .commands, dispatch to the
- associated subparser. Returns a list or an atomic object.
- If ignore_extra is True, unrecognized arguments are stored
- in the attribute .extra_args of the associated parser for
- later processing.
+ associated subparser.
"""
- cmd, result = parser_from(obj).consume(arglist, ignore_extra)
- if iterable(result):
+ cmd, result = parser_from(obj).consume(arglist)
+ if iterable(result) and eager: # listify the result
return list(result)
return result
diff --git a/plac/plac_ext.py b/plac/plac_ext.py
index 37051fe..69d7772 100644
--- a/plac/plac_ext.py
+++ b/plac/plac_ext.py
@@ -1,12 +1,27 @@
# this module requires Python 2.5+
from __future__ import with_statement
+from contextlib import contextmanager
from operator import attrgetter
+from gettext import gettext as _
import imp, inspect, os, sys, cmd, shlex, subprocess
-import itertools, traceback, time, select, multiprocessing, signal
+import itertools, traceback, time, select, multiprocessing, signal, threading
import plac_core
+try:
+ import readline
+except ImportError:
+ readline = None
############################# generic utils ################################
+@contextmanager
+def stdout(fileobj):
+ orig_stdout = sys.stdout
+ sys.stdout = fileobj
+ try:
+ yield
+ finally:
+ sys.stdout = orig_stdout
+
def write(x):
"Write str(x) on stdout and flush, no newline added"
sys.stdout.write(str(x))
@@ -34,12 +49,82 @@ def less(text):
use_less = (sys.platform != 'win32') # unices
+class TerminatedProcess(Exception):
+ pass
+
+def terminatedProcess(signum, frame):
+ raise TerminatedProcess
+
+def namedpipe(fname):
+ "Return a line iterator reading from a named pipe every twice per second"
+ try:
+ os.mkfifo(fname)
+ except OSError: # already there
+ pass
+ with open(fname) as fifo:
+ while True:
+ line = fifo.readline()
+ if line == 'EOF\n':
+ break
+ elif line:
+ yield line
+ time.sleep(.5)
+ os.remove(fname)
+
+########################### readline support #############################
+
+class ReadlineInput(object):
+ """
+ An iterable with a .readline method reading from stdin with readline
+ features enabled, if possible. Otherwise return sys.stdin itself.
+ """
+ def __init__(self, completions, prompt='', 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):
+ try:
+ if self.histfile:
+ readline.read_history_file(self.histfile)
+ except IOError: # the first time
+ pass
+ return self
+
+ def __exit__(self, etype, exc, tb):
+ if self.histfile:
+ readline.write_history_file(self.histfile)
+
+ def complete(self, kw, state):
+ # state is 0, 1, 2, ... and increases by hitting TAB
+ if not self.case_sensitive:
+ kw = kw.upper()
+ try:
+ return [k for k in self.completions if k.startswith(kw)][state]
+ except IndexError: # no completions
+ return # exit
+
+ def readline(self):
+ try:
+ return raw_input(self.prompt) + '\n'
+ except EOFError:
+ return ''
+
+ def __iter__(self):
+ return iter(self.readline, '')
+
########################### import management ################################
try:
PLACDIRS = os.environ.get('PLACPATH', '.').split(':')
except:
- raise ValueError('Ill-formed PLACPATH: got %PLACPATHs' % os.environ)
+ raise ValueError(_('Ill-formed PLACPATH: got %PLACPATHs') % os.environ)
def import_main(path, *args, **pconf):
"""
@@ -52,28 +137,23 @@ def import_main(path, *args, **pconf):
if os.path.exists(fullpath):
break
else: # no break
- raise ImportError('Cannot find %s', path)
+ raise ImportError(_('Cannot find %s'), path)
else:
fullpath = path
name, ext = os.path.splitext(os.path.basename(fullpath))
- tool = imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1)).main
+ main = imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1)).main
if args:
- cmd, tool = plac_core.parser_from(tool).consume(args)
- elif inspect.isclass(tool):
- tool = tool() # instantiate it
- vars(tool).update(pconf)
- plac_core.parser_from(tool) # raise a TypeError if not
+ cmd, tool = plac_core.parser_from(main).consume(args)
+ else:
+ tool = main
+ # set the parser configuration and possibly raise a TypeError early
+ plac_core.parser_from(tool, **pconf)
return tool
-######################## Tasks management ##########################
-
-class TerminatedProcess(Exception):
- pass
-
-def terminatedProcess(signum, frame):
- raise TerminatedProcess
+############################## Task classes ##############################
-class Task(object):
+# base class not instantiated directly
+class BaseTask(object):
"""
A task is a wrapper over a generator object with signature
Task(no, arglist, genobj), attributes
@@ -85,16 +165,17 @@ class Task(object):
.exc
.tb
.status
+ .synchronous
and methods .run and .kill.
"""
- STATES = 'SUBMITTED', 'RUNNING', 'FINISHED', 'ABORTED', 'KILLED'
- synchronous = True # may be overridden in subclasses
+ STATES = ('SUBMITTED', 'RUNNING', 'TOBEKILLED', 'KILLED', 'FINISHED',
+ 'ABORTED')
def __init__(self, no, arglist, genobj):
self.no = no
self.arglist = arglist
self._genobj = self._wrap(genobj)
- self.str, self.etype, self.exc, self.tb = '*', None, None, None
+ self.str, self.etype, self.exc, self.tb = '', None, None, None
self.status = 'SUBMITTED'
self.outlist = []
@@ -102,19 +183,21 @@ class Task(object):
"""
Wrap the genobj into a generator managing the exceptions,
populating the .outlist, setting the .status and yielding None.
+ stringify_tb must be True if the traceback must be sent to a process.
"""
self.status = 'RUNNING'
try:
for value in genobj:
- if value is not None:
+ if self.status == 'TOBEKILLED': # exit from the loop
+ raise GeneratorExit
+ if value is not None: # add output
self.outlist.append(value)
yield
except (GeneratorExit, TerminatedProcess): # soft termination
self.status = 'KILLED'
- except: # unexpect exception
+ except: # unexpected exception
self.etype, self.exc, tb = sys.exc_info()
self.tb = self.traceback if stringify_tb else tb
- # needed when sending the traceback to a process
self.status = 'ABORTED'
else: # regular exit
self.status = 'FINISHED'
@@ -126,21 +209,15 @@ class Task(object):
pass
def kill(self):
- "Kill softly the task by closing the inner generator"
- self._genobj.close()
+ "Set a TOBEKILLED status"
+ self.status = 'TOBEKILLED'
def wait(self):
- "Wait for the task to finish: overridden in MPTask"
-
- def __str__(self):
- "Returns 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
+ "Wait for the task to finish: to be overridden"
@property
def traceback(self):
+ "Return the traceback as a (possibly empty) string"
if self.tb is None:
return ''
elif isinstance(self.tb, basestring):
@@ -148,35 +225,158 @@ class Task(object):
else:
return ''.join(traceback.format_tb(self.tb))
+ def __str__(self):
+ "String representation containing class name, number, arglist, status"
+ return '<%s %d [%s] %s>' % (
+ self.__class__.__name__, self.no,
+ ' '.join(self.arglist), self.status)
+
+########################## 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
+
+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)
+
+ def run(self):
+ "Run the task into a thread"
+ self.thread.start()
+
+ def wait(self):
+ "Block until the thread ends"
+ self.thread.join()
+
+######################### multiprocessing tasks ##########################
+
+def sharedattr(name):
+ "Return a property to be attached to an object with a .ns attribute"
+ def get(self):
+ return getattr(self.ns, name)
+ def set(self, value):
+ setattr(self.ns, name, value)
+ return property(get, set)
+
+class MPTask(BaseTask):
+ """
+ A task running as an external process. The current implementation
+ only works on Unix-like systems, where multiprocessing use forks.
+ """
+
+ synchronous = False
+ _mp_manager = None
+
+ str = sharedattr('str')
+ etype = sharedattr('etype')
+ exc = sharedattr('exc')
+ tb = sharedattr('tb')
+ status = sharedattr('status')
+
+ def __init__(self, no, arglist, genobj):
+ if self.__class__._mp_manager is None: # the first time
+ self.__class__._mp_manager = multiprocessing.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.proc = multiprocessing.Process(target=super(MPTask, self).run)
+
+ def run(self):
+ "Run the task into an external process"
+ self.proc.start()
+
+ def wait(self):
+ "Block until the external process ends"
+ self.proc.join()
+
+ def kill(self):
+ """Kill the process with a SIGTERM inducing a TerminatedProcess
+ exception in the children"""
+ self.proc.terminate()
+
+######################### Task Manager #######################
+
+class HelpSummary(object):
+ "Build the help summary consistently with the cmd module"
+ @classmethod
+ def make(cls, obj, specialcommands):
+ c = cmd.Cmd(stdout=cls())
+ c.stdout.write('\n')
+ c.print_topics('special commands',
+ sorted(specialcommands), 15, 80)
+ c.print_topics('custom commands',
+ sorted(obj.syncommands), 15, 80)
+ c.print_topics('commands run in external processes',
+ sorted(obj.mpcommands), 15, 80)
+ c.print_topics('threaded commands',
+ sorted(obj.thcommands), 15, 80)
+ return c.stdout
+ def __init__(self):
+ self._ls = []
+ def write(self, s):
+ self._ls.append(s)
+ def __str__(self):
+ return ''.join(self._ls)
+
class TaskManager(object):
- specialcommands = set(['_help', '_kill', '_list', '_output', '_last_tb'])
+ """
+ Store the given commands into a task registry. Provides methods to
+ manage the submitted tasks.
+ """
+ cmdprefix = '.'
+ specialcommands = set(['.help', '.last_tb'])
def __init__(self, obj):
self.obj = obj
- self._extract_commands_from(obj)
self.registry = {} # {taskno : task}
+ if obj.mpcommands or obj.thcommands:
+ self.specialcommands.update(['.kill', '.list', '.output'])
+ self.helpsummary = HelpSummary.make(obj, self.specialcommands)
signal.signal(signal.SIGTERM, terminatedProcess)
- def _extract_commands_from(self, obj):
- "Make sure self has the right command attributes"
- for attrname in ('commands', 'asyncommands', 'mpcommands'):
- try:
- sequence = getattr(obj, attrname)
- except AttributeError:
- sequence = []
- if not isinstance(sequence, set):
- sequence = set(sequence)
- setattr(self, attrname, sequence)
- self.commands.update(self.asyncommands, self.mpcommands)
- for cmd in self.commands:
- setattr(self, cmd, getattr(obj, cmd))
- if self.commands:
- self.commands.update(self.specialcommands)
- self.add_help = False
-
def run_task(self, task):
"Run the task and update the registry"
- if not task.synchronous:
+ if not task.arglist:
+ return
+ cmd = task.arglist[0]
+ if cmd not in self.specialcommands:
self.registry[task.no] = task
task.run()
@@ -187,15 +387,14 @@ class TaskManager(object):
task.kill()
task.wait()
- def _get_latest(self, taskno=-1, status=None, synchronous=False):
+ def _get_latest(self, taskno=-1, status=None):
"Get the latest submitted task from the registry"
assert taskno < 0, 'You must pass a negative number'
if status:
tasks = [t for t in self.registry.itervalues()
- if t.status == status and t.synchronous == synchronous]
+ if t.status == status]
else:
- tasks = [t for t in self.registry.itervalues()
- if t.synchronous == synchronous]
+ tasks = [t for t in self.registry.itervalues()]
tasks.sort(key=attrgetter('no'))
if len(tasks) >= abs(taskno):
return tasks[taskno]
@@ -204,7 +403,7 @@ class TaskManager(object):
@plac_core.annotations(
taskno=('task to kill', 'positional', None, int))
- def _kill(self, taskno=-1):
+ def kill(self, taskno=-1):
'kill the given task (-1 to kill the latest running task)'
if taskno < 0:
task = self._get_latest(taskno, status='RUNNING')
@@ -220,12 +419,11 @@ class TaskManager(object):
yield 'Already finished %s' % task
return
task.kill()
- yield 'Killed task %s' % task
+ yield task
@plac_core.annotations(
- status=('list of tasks with a given status', 'positional',
- None, str, Task.STATES))
- def _list(self, status='RUNNING'):
+ status=('', 'positional', None, str, BaseTask.STATES))
+ def list(self, status='RUNNING'):
'list tasks with a given status'
for task in self.registry.values():
if task.status == status:
@@ -233,7 +431,7 @@ class TaskManager(object):
@plac_core.annotations(
taskno=('task number', 'positional', None, int))
- def _output(self, taskno=-1):
+ def output(self, taskno=-1):
'show the output of a given task'
if taskno < 0:
task = self._get_latest(taskno)
@@ -252,81 +450,38 @@ class TaskManager(object):
else:
yield outstr
- def _last_tb(self):
- task = self._get_latest(synchronous=True)
- yield task.traceback + '\n'
-
- def _help(self, cmd=None):
- yield cmd_help(self.obj.asyncommands)
- #yield self.p.format_help()
-
-######################## Process management ##########################
-
-def sharedattr(name):
- "Return a property to be attached to an object with a .ns attribute"
- def get(self):
- return getattr(self.ns, name)
- def set(self, value):
- setattr(self.ns, name, value)
- return property(get, set)
-
-class MPTask(Task):
- """
- A task running as an external process. The current implementation
- only works on Unix-like systems, where multiprocessing use forks.
- """
-
- synchronous = False
- _mp_manager = None
-
- str = sharedattr('str')
- etype = sharedattr('etype')
- exc = sharedattr('exc')
- tb = sharedattr('tb')
- status = sharedattr('status')
-
- def __init__(self, no, arglist, genobj):
- if self.__class__._mp_manager is None: # the first time
- self.__class__._mp_manager = multiprocessing.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, None
- self.status = 'SUBMITTED'
- self._genobj = self._wrap(genobj, stringify_tb=True)
- self.proc = multiprocessing.Process(target=super(MPTask, self).run)
-
- def run(self):
- "Run the task into an external process"
- self.proc.start()
-
- def wait(self):
- "Block until the external process ends"
- self.proc.join()
+ @plac_core.annotations(
+ taskno=('task number', 'positional', None, int))
+ def last_tb(self, taskno=-1):
+ "show the traceback of a given task, if any"
+ task = self._get_latest(taskno)
+ if task:
+ yield task.traceback
+ else:
+ yield 'Nothing to show'
- def kill(self):
- """Kill the process with a SIGTERM inducing a TerminatedProcess
- exception in the children"""
- self.proc.terminate()
+ def help(self, cmd=None):
+ "show help about a given command"
+ if cmd is None:
+ yield str(self.helpsummary)
+ else:
+ yield plac_core.parser_from(self.obj).help_cmd(cmd)
- def __str__(self):
- return '<%s %d [%s] %s>' % (
- self.__class__.__name__, self.no,
- ' '.join(self.arglist), self.status)
+########################### SyncProcess ##############################
-class SyncProcess(subprocess.Popen):
+class Process(subprocess.Popen):
"Start the interpreter specified by the params in a subprocess"
def __init__(self, params):
code = '''import plac
-plac.Interpreter(plac.import_main(*%s), prompt='i>\\n').interact()
+plac.Interpreter(plac.import_main(*%s)).interact(prompt='i>\\n')
''' % params
subprocess.Popen.__init__(
self, [sys.executable, '-u', '-c', code],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def close(self):
+ "Close stdin and stdout"
self.stdin.close()
self.stdout.close()
@@ -340,63 +495,11 @@ plac.Interpreter(plac.import_main(*%s), prompt='i>\\n').interact()
return out[:-1] + ' ' # remove last newline
def send(self, line):
- "Send a line (adding a newline) to the subprocess"
+ """Send a line (adding a newline) to the underlying subprocess
+ and wait for the answer"""
self.stdin.write(line + os.linesep)
return self.recv()
-############################# asynchronous utilities #########################
-
-# eventloop inspired to monocle (http://github.com/saucelabs/monocle)
-class EventLoop(object):
- """
- A trivial event loop with a monocle-consistent interface, i.e. methods
- queue_task, run and halt.
- """
- def __init__(self):
- self._running = True
- self._queue = []
-
- def queue_task(self, delay, callable, *args, **kw):
- when = time.time() + delay
- self._queue.append((when, callable, args, kw))
- self._queue.sort(reverse=True) # the last is the most recent
-
- def run(self):
- while self._running:
- if self._queue: # there is always the select in queue
- when = self._queue[-1][0]
- if when <= time.time():
- task = self._queue.pop()
- task[1](*task[2], **task[3])
- time.sleep(0.05)
-
- def halt(self):
- self._running = False
-
-class AsynTask(Task):
- "Lightweight wrapper over a generator running into an event loop"
-
- synchronous = False
- eventloop = EventLoop()
- delay = 0
-
- def run(self):
- "Run the asyntask inside an eventloop"
- eventloop = self.eventloop
- delay = self.delay
- def next_and_reschedule(): # unless stop iteration
- try:
- self._genobj.next()
- except StopIteration: # error management inside _wrap
- return
- eventloop.queue_task(delay, next_and_reschedule)
- eventloop.queue_task(delay, next_and_reschedule)
-
- def __str__(self):
- return '<%s %d [%s] %s>' % (
- self.__class__.__name__, self.no,
- ' '.join(self.arglist), self.status)
-
########################### the Interpreter #############################
class Interpreter(object):
@@ -404,42 +507,68 @@ class Interpreter(object):
A context manager with a .send method and a few utility methods:
execute, test and doctest.
"""
- counter = itertools.count(1)
-
- def __init__(self, obj, commentchar='#', prompt='i> ',
- loop=AsynTask.eventloop):
+ def __init__(self, obj, commentchar='#'):
self.obj = obj
+ try:
+ self.name = obj.__module__
+ except AttributeError:
+ self.name = 'plac'
self.commentchar = commentchar
- self.prompt = prompt
- self.eventloop = loop
+ self._set_commands(obj)
self.tm = TaskManager(obj)
- try:
- self.p = plac_core.parser_from(obj)
- except TypeError: # obj is not callable
- self.p = plac_core.parser_from(self.tm)
- self.p.error = lambda msg: sys.exit(msg) # patch the parser
+ self.parser = plac_core.parser_from(obj, prog='', add_help=False)
+ if self.commands:
+ self.commands.update(self.tm.specialcommands)
+ self.parser.addsubcommands(self.tm.specialcommands, self.tm,
+ title='special commands')
+ if obj.mpcommands:
+ self.parser.addsubcommands(obj.mpcommands, obj,
+ title='commands run in external processes')
+ if obj.thcommands:
+ self.parser.addsubcommands(obj.thcommands, obj,
+ title='threaded commands')
+ self.parser.error = lambda msg: sys.exit(msg) # patch the parser
self._interpreter = None
+ def _set_commands(self, obj):
+ "Make sure obj has the right command attributes as Python sets"
+ for attrname in ('commands', 'syncommands', 'mpcommands', 'thcommands'):
+ try:
+ sequence = getattr(obj, attrname)
+ except AttributeError:
+ sequence = []
+ if not isinstance(sequence, set):
+ sequence = set(sequence)
+ setattr(obj, attrname, sequence)
+ obj.syncommands.update(obj.commands)
+ self.commands = obj.commands
+ self.commands.update(obj.syncommands)
+ self.commands.update(obj.mpcommands)
+ self.commands.update(obj.thcommands)
+
def __enter__(self):
self._interpreter = self._make_interpreter()
self._interpreter.send(None)
return self
- def maketask(self, line):
+ def __exit__(self, *exc):
+ self.close()
+
+ def make_task(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 '
- 'use the with statement' % self)
+ raise RuntimeError(_('%r not initialized: probably you forgot to '
+ 'use the with statement') % self)
if isinstance(line, basestring):
arglist = shlex.split(line, self.commentchar)
- else:
+ else: # expects a list of strings
arglist = line
return self._interpreter.send(arglist)
def send(self, line):
"Send a line to the underlying interpreter and return the result"
- task = self.maketask(line)
- Task.run(task) # blocking
+ task = self.make_task(line)
+ BaseTask.run(task) # blocking
return task
def close(self):
@@ -447,9 +576,6 @@ class Interpreter(object):
self.tm.close()
self._interpreter.close()
- def __exit__(self, *exc):
- self.close()
-
def _make_interpreter(self):
"The interpreter main loop, from lists of arguments to task objects"
enter = getattr(self.obj, '__enter__', lambda : None)
@@ -460,19 +586,18 @@ class Interpreter(object):
for no in itertools.count(1):
arglist = yield task
try:
- cmd, result = self.p.consume(arglist)
+ cmd, result = self.parser.consume(arglist)
except: # i.e. SystemExit for invalid command
- task = Task(no, arglist, gen_exc(*sys.exc_info()))
+ task = SynTask(no, arglist, gen_exc(*sys.exc_info()))
continue
- if not plac_core.iterable(result):
- task = Task(no, arglist, gen_value(result))
- elif cmd in self.tm.asyncommands:
- task = AsynTask(no, arglist, result)
- task.eventloop = self.eventloop
- elif cmd in self.tm.mpcommands:
+ 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)
+ elif cmd in self.obj.thcommands:
+ task = ThreadedTask(no, arglist, result)
else: # blocking task
- task = Task(no, arglist, result)
+ task = SynTask(no, arglist, result)
except GeneratorExit: # regular exit
exit(None, None, None)
except: # exceptional exit
@@ -484,6 +609,7 @@ class Interpreter(object):
output = self.send(given_input).str # blocking
ok = (output == expected_output)
if not ok:
+ # the message here is not internationalized on purpose
msg = 'input: %s\noutput: %s\nexpected: %s' % (
given_input, output, expected_output)
raise AssertionError(msg)
@@ -505,7 +631,7 @@ class Interpreter(object):
positions.append(len(lines) + 1) # last position
return zip(inputs, self._getoutputs(lines, positions), positions)
- def doctest(self, lineiter, put=write, verbose=False):
+ def doctest(self, lineiter, verbose=False):
"""
Parse a text containing doctests in a context and tests of all them.
Raise an error even if a single doctest if broken. Use this for
@@ -514,103 +640,61 @@ class Interpreter(object):
with self:
for input, output, no in self._parse_doctest(lineiter):
if verbose:
- put('i> %s\n' % input)
- put('-> %s\n' % output)
- out = self.send(input)
- if not out.str == output:
- msg = 'line %d: input: %s\noutput: %s\nexpected: %s\n' % (
- no + 1, input, out, output)
- put(msg)
- raise out.etype, out.exc, out.tb
-
- def execute(self, lineiter, put=write, verbose=False):
+ write('i> %s\n' % input)
+ write('-> %s\n' % output)
+ task = self.send(input) # blocking
+ if not str(task) == output:
+ msg = 'line %d: input: %s\noutput: %s\nexpected: %s\n' % (
+ no + 1, input, task, output)
+ write(msg)
+ 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.
"""
with self:
for line in lineiter:
if verbose:
- put('i> ' + line)
- output = self.send(line)
- if output.etype: # there was an error
- raise output.etype, output.exc, output.tb
- put('%s\n' % output.str)
-
- def interact(self, stdin=sys.stdin, put=write, verbose=False):
+ write('i> ' + line)
+ 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)
+
+ def interact(self, stdin=sys.stdin, prompt='i> ', verbose=False):
"""
Starts an interactive command loop reading commands from the
consolle. Using rlwrap is recommended.
"""
+ if stdin is sys.stdin and readline: # use readline
+ # print '.%s.history' % self.name
+ stdin = ReadlineInput(
+ self.commands, prompt, histfile='.%s.history' % self.name,
+ case_sensitive=True)
self.stdin = stdin
- self.put = put
- try:
- put(self.obj.intro + '\n')
- except AttributeError: # no intro
- put(self.p.format_usage() + '\n')
- put(self.prompt)
+ self.prompt = getattr(stdin, 'prompt', prompt)
+ self.verbose = verbose
+ intro = self.obj.__doc__ or ''
+ write(intro + '\n')
with self:
- if self.tm.asyncommands:
- loop.queue_task(0, self._dispatch_async_input)
- loop.run()
+ if self.stdin is sys.stdin: # do not close stdin automatically
+ self._manage_input()
else:
- while True:
- line = stdin.readline() # including \n
- if not line:
- break
- task = self.maketask(line)
- self.tm.run_task(task)
- if verbose and task.synchronous and task.etype:
- put(task.traceback + '\n')
- put(str(task) + '\n')
- put(self.prompt)
-
- def _dispatch_async_input(self):
- i, o, e = select.select([self.stdin], [], [], 0)
- if i:
- line = i[0].readline() # including \n
- if not line: # stdin was closed
- self.loop.halt()
- return
- task = self.maketask(line)
+ with self.stdin:
+ 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
+ if not line:
+ break
+ task = self.make_task(line)
self.tm.run_task(task)
- self.put('%s\n' % task)
- self.put(self.prompt)
- self.loop.queue_task(0, self._dispatch_async_input) # reschedule
-
-################################## others ####################################
-
-def cmd_interface(obj):
- "Returns a cmd.Cmd wrapper over the command container"
- i = Interpreter(obj)
- def default(self, line):
- print(i.send(line))
- dic = dict(preloop=lambda self: i.__enter__(),
- postloop=lambda self: i.__exit__(),
- do_EOF=lambda self, line: True,
- default=default,
- intro=getattr(i, 'intro', None))
- for command in obj.commands:
- method = getattr(obj, command)
- def do_func(self, line, command=command):
- print(i.send(command + ' ' + line))
- do_func.__doc__ = method.__doc__
- do_func.__name__ = method.__name__
- dic['do_' + command] = do_func
- clsname = '_%s_' % obj.__class__.__name__
- cls = type(clsname, (cmd.Cmd, object), dic)
- return cls()
-
-class FakeOut(object):
- def __init__(self):
- self._s = ''
- def write(self, s):
- self._s += s
- def __str__(self):
- return self._s
-
-def cmd_help(cmds, displaywidth=80, cmd=cmd.Cmd(stdout=FakeOut())):
- cmd.stdout.write("%s\n" % str(cmd.doc_leader))
- cmd.print_topics(cmd.doc_header, cmds, 15,80)
- #cmd.print_topics(cmd.misc_header, helps,15,80)
- #cmd.print_topics(cmd.undoc_header, cmds_undoc, 15,80)
- return cmd.stdout
+ if self.verbose and task.synchronous and task.etype:
+ write(task.traceback)
+ if task.etype or not task.synchronous:
+ write(str(task) + '\n')