summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichele Simionato <michele.simionato@gmail.com>2011-06-13 09:57:28 +0200
committerMichele Simionato <michele.simionato@gmail.com>2011-06-13 09:57:28 +0200
commit68e5691ddde6246cd22c7cc6e0b189f2b343e661 (patch)
treeafd0a525154b6540d048e039acea9db6da870e9a
parent2c9a92feab665152a02a7482bff9d31bafa2996e (diff)
downloadmicheles-68e5691ddde6246cd22c7cc6e0b189f2b343e661.tar.gz
Removed the plac project, which now has its own page
-rw-r--r--plac/CHANGES.txt47
-rw-r--r--plac/MANIFEST.in1
-rw-r--r--plac/Makefile10
-rw-r--r--plac/README.txt58
-rw-r--r--plac/df.css400
-rw-r--r--plac/doc/annotations.py9
-rw-r--r--plac/doc/cmd_ex.py6
-rw-r--r--plac/doc/dbcli.help13
-rw-r--r--plac/doc/dbcli.py29
-rw-r--r--plac/doc/example0.py6
-rw-r--r--plac/doc/example1.py14
-rw-r--r--plac/doc/example10.help10
-rw-r--r--plac/doc/example10.py20
-rw-r--r--plac/doc/example11.help9
-rw-r--r--plac/doc/example11.py13
-rw-r--r--plac/doc/example12.help9
-rw-r--r--plac/doc/example12.py18
-rw-r--r--plac/doc/example13.help13
-rw-r--r--plac/doc/example13.py28
-rw-r--r--plac/doc/example2.py12
-rw-r--r--plac/doc/example3.help9
-rw-r--r--plac/doc/example3.py8
-rw-r--r--plac/doc/example4.py15
-rw-r--r--plac/doc/example5.help11
-rw-r--r--plac/doc/example5.py9
-rw-r--r--plac/doc/example6.help8
-rw-r--r--plac/doc/example6.py6
-rw-r--r--plac/doc/example7.help10
-rw-r--r--plac/doc/example7.py11
-rw-r--r--plac/doc/example7_.help10
-rw-r--r--plac/doc/example7_.py11
-rw-r--r--plac/doc/example8.help9
-rw-r--r--plac/doc/example8.py8
-rw-r--r--plac/doc/example8_.help9
-rw-r--r--plac/doc/example8_.py6
-rw-r--r--plac/doc/example9.help8
-rw-r--r--plac/doc/example9.py9
-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/importer_ui.py30
-rw-r--r--plac/doc/ishelve.help18
-rw-r--r--plac/doc/ishelve.plac7
-rw-r--r--plac/doc/ishelve.placet13
-rw-r--r--plac/doc/ishelve.py54
-rw-r--r--plac/doc/ishelve2.help8
-rw-r--r--plac/doc/ishelve2.plac4
-rw-r--r--plac/doc/ishelve2.placet9
-rw-r--r--plac/doc/ishelve2.py43
-rw-r--r--plac/doc/ishelve3.py5
-rw-r--r--plac/doc/picalculator.py63
-rw-r--r--plac/doc/plac.el77
-rw-r--r--plac/doc/plac.html3026
-rw-r--r--plac/doc/plac.pdf13201
-rw-r--r--plac/doc/plac.txt2
-rw-r--r--plac/doc/plac_adv.txt1266
-rw-r--r--plac/doc/plac_core.txt793
-rw-r--r--plac/doc/read_stdin.py13
-rw-r--r--plac/doc/server_ex.py9
-rw-r--r--plac/doc/shelve_interpreter.help13
-rw-r--r--plac/doc/shelve_interpreter.py19
-rw-r--r--plac/doc/sql_interface.py29
-rw-r--r--plac/doc/test_ishelve.py12
-rw-r--r--plac/doc/test_ishelve_more.py11
-rw-r--r--plac/doc/test_pi.py11
-rw-r--r--plac/doc/test_plac.py231
-rw-r--r--plac/doc/test_runp.py24
-rw-r--r--plac/doc/test_server.py38
-rw-r--r--plac/doc/vcs.help12
-rw-r--r--plac/doc/vcs.py30
-rw-r--r--plac/plac.py40
-rw-r--r--plac/plac_core.py312
-rw-r--r--plac/plac_ext.py1063
-rw-r--r--plac/plac_runner.py65
-rw-r--r--plac/setup.py50
75 files changed, 1 insertions, 21544 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt
deleted file mode 100644
index f5560e6..0000000
--- a/plac/CHANGES.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-HISTORY
-----------
-
-0.9.0 Default values are now displayed in the help message by default;
- removed .help and introduced help as an alias for --help;
- added a reference to Argh (2011-06-05)
-0.8.1 Removed a stray newline in the output of plac, as signaled
- by Daniele Pighin; fixed a bug in the doctest method raising
- non-existing exceptions; turned the notification messages into
- unicode strings; removed an ugly SystemExit message
- for invalid commands, signaled by Tuk Bredsdorff (2011-04-11)
-0.8.0 Added a monitor framework and a TkMonitor (2011-02-16)
-0.7.6 Fixed the error propagation in ``Interpreter.__exit__``.
- Added a note about commandline and marrow.script in the documentation
- (2011-01-13)
-0.7.5 Fixed a bug with the help of subcommands, signaled by Paul Jacobson;
- added the ability to save the output of a command into a file; postponed
- the import of the readline module to avoid buffering issues; fixed a
- bug with the traceback when in multiprocessing mode (2011-01-01)
-0.7.4 Fixed the plac_runner switches -i and -s; fixed a bug with multiline
- output and issue with nosetest (2010-09-04)
-0.7.3 Put the documentation in a single document; added runp (2010-08-31)
-0.7.2 Interpreter.call does not start an interpreter automagically anymore;
- better documented and added tests for the metavar concept (2010-08-31)
-0.7.1 A few bug fixes (2010-08-11)
-0.7.0 Improved and documented the support for parallel programming;
- added an asynchronous server; added plac.Interpreter.call (2010-08-07)
-0.6.1 Fixed the history file location; added the ability to pass a split
- function; added two forgotten files; added a reference to cmd2 by
- Catherine Devlin (2010-07-12)
-0.6.0 Improved the interactive experience with full readline support and
- custom help. Added support for long running command, via threads and
- processes (2010-07-11)
-0.5.0 Gigantic release. Introduced smart options, added an Interpreter class
- and the command container concept. Made the split plac/plac_core/plac_ext
- and added a plac runner, able to run scripts, batch files and doctests.
- Removed the default formatter class (2010-06-20)
-0.4.3 Fixed the installation procedure to automatically download argparse
- if needed (2010-06-11)
-0.4.2 Added missing .help files, made the tests generative and added a
- note about Clap in the documentation (2010-06-04)
-0.4.1 Changed the default formatter class and fixed a bug in the
- display of the default arguments. Added more stringent tests. (2010-06-03)
-0.4.0 abbrev is now optional. Added a note about CLIArgs and opterate.
- Added keyword arguments recognition. ``plac.call`` now returns the
- the output of the main function. (2010-06-03)
-0.3.0 Initial version. (2010-06-02)
diff --git a/plac/MANIFEST.in b/plac/MANIFEST.in
deleted file mode 100644
index 0f93421..0000000
--- a/plac/MANIFEST.in
+++ /dev/null
@@ -1 +0,0 @@
-include *.txt doc/*.py doc/*.help doc/*.txt doc/*.html doc/*.pdf
diff --git a/plac/Makefile b/plac/Makefile
deleted file mode 100644
index c1e79ab..0000000
--- a/plac/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-default:
- make doc/plac.pdf
-doc/plac.pdf: doc/plac.txt doc/plac_core.txt doc/plac_adv.txt
- cd doc; rst2pdf --footer=###Page### plac.txt; rst2html --stylesheet=$(HOME)/md/gcodedev/df.css plac.txt plac.html
-upload:
- python3.2 setup.py register sdist upload
-2:
- python setup.py build; sudo python setup.py install; sudo rm -rf dist
-3:
- python3.2 setup.py build; sudo python3.2 setup.py install; sudo rm -rf dist
diff --git a/plac/README.txt b/plac/README.txt
deleted file mode 100644
index 989a43d..0000000
--- a/plac/README.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-Installation
--------------
-
-If you are lazy, just perform
-
-::
-
- $ easy_install -U plac
-
-which will install the module on your system (and possibly argparse
-too, if it is not already installed). Notice that Python 3 requires
-the easy_install version of the distribute_ project.
-
-If you prefer to install the full distribution from source, including
-the documentation, download the tarball_, unpack it and run
-
-::
-
- $ python setup.py install
-
-in the main directory, possibly as superuser.
-
-.. _tarball: http://pypi.python.org/pypi/plac
-.. _distribute: http://packages.python.org/distribute/
-
-Testing
---------
-
-Run
-
-::
-
- $ python doc/test_plac.py
-
-or
-
-::
-
- $ nosetests doc
-
-or
-
-::
-
- $ py.test doc
-
-Some tests will fail if sqlalchemy is not installed.
-Run an ``easy_install -U sqlalchemy`` or just ignore them.
-
-Documentation
---------------
-
-The source code and the documentation are hosted on Google code.
-Here is the full documentation in HTML and PDF form:
-
-http://plac.googlecode.com/hg/doc/plac.html
-
-http://plac.googlecode.com/hg/doc/plac.pdf
diff --git a/plac/df.css b/plac/df.css
deleted file mode 100644
index afa58bb..0000000
--- a/plac/df.css
+++ /dev/null
@@ -1,400 +0,0 @@
-.first {
- margin-top: 0 }
-
-.last {
- margin-bottom: 0 }
-
-a.toc-backref {
- text-decoration: none ;
- color: black }
-
-dd {
- margin-bottom: 0.5em }
-
-div.abstract {
- margin: 2em 5em }
-
-div.abstract p.topic-title {
- font-weight: bold ;
- text-align: center }
-
-div.attention, div.caution, div.danger, div.error, div.hint,
-div.important, div.note, div.tip, div.warning {
- margin: 2em ;
- border: medium outset ;
- padding: 1em }
-
-div.attention p.admonition-title, div.caution p.admonition-title,
-div.danger p.admonition-title, div.error p.admonition-title,
-div.warning p.admonition-title {
- color: red ;
- font-weight: bold ;
- font-family: sans-serif }
-
-div.hint p.admonition-title, div.important p.admonition-title,
-div.note p.admonition-title, div.tip p.admonition-title {
- font-weight: bold ;
- font-family: sans-serif }
-
-div.dedication {
- margin: 2em 5em ;
- text-align: center ;
- font-style: italic }
-
-div.dedication p.topic-title {
- font-weight: bold ;
- font-style: normal }
-
-div.figure {
- margin-left: 2em }
-
-div.footer, div.header {
- font-size: smaller }
-
-div.system-messages {
- margin: 5em }
-
-div.system-messages h1 {
- color: red }
-
-div.system-message {
- border: medium outset ;
- padding: 1em }
-
-div.system-message p.system-message-title {
- color: red ;
- font-weight: bold }
-
-div.topic {
- margin: 2em }
-
-hr {
- width: 75% }
-
-ol.simple, ul.simple {
- margin-bottom: 1em }
-
-ol.arabic {
- list-style: decimal }
-
-ol.loweralpha {
- list-style: lower-alpha }
-
-ol.upperalpha {
- list-style: upper-alpha }
-
-ol.lowerroman {
- list-style: lower-roman }
-
-ol.upperroman {
- list-style: upper-roman }
-
-p.caption {
- font-style: italic }
-
-p.credits {
- font-style: italic ;
- font-size: smaller }
-
-p.label {
- white-space: nowrap }
-
-p.topic-title {
- font-weight: bold }
-
-pre.address {
- margin-bottom: 0 ;
- margin-top: 0 ;
- font-family: serif ;
- font-size: 100% }
-
-pre.line-block {
- font-family: serif ;
- font-size: 100% }
-
-pre.literal-block, pre.doctest-block {
- background-color: #eeeeee }
-
-span.classifier {
- font-family: sans-serif ;
- font-style: oblique }
-
-span.classifier-delimiter {
- font-family: sans-serif ;
- font-weight: bold }
-
-span.interpreted {
- font-family: sans-serif }
-
-span.option-argument {
- font-style: italic }
-
-span.pre {
- white-space: pre }
-
-span.problematic {
- color: red }
-
-table {
- margin-top: 0.5em ;
- margin-bottom: 0.5em }
-
-table.citation {
- border-left: solid thin gray ;
- padding-left: 0.5ex }
-
-table.docinfo {
- margin: 2em 4em }
-
-table.footnote {
- border-left: solid thin black ;
- padding-left: 0.5ex }
-
-td, th {
- padding-left: 0.5em ;
- padding-right: 0.5em ;
- vertical-align: top }
-
-th.docinfo-name, th.field-name {
- font-weight: bold ;
- text-align: left ;
- white-space: nowrap }
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
- font-size: 100% }
-
-tt {
- background-color: #eeeeee }
-
-ul.auto-toc {
- list-style-type: none }
-
-
-/*
-Additional styles for "modern"-style of DocFactory.
-
-:Author: Gunnar Schwant
-:Contact: g.schwant@gmx.de
-*/
-
-.first {
- font-size: 10pt }
-
-.last {
- font-size: 10pt }
-
-a {
- text-decoration: none }
-
-a.reference {
- color: #00009F }
-
-a:hover {
- background-color: #00009F ;
- color: white }
-
-body {
- font-family: arial,helvetica,univers ;
- font-size: 10pt ;
- padding-top: 0.6cm ;
- margin-left:0.5cm ;
- margin-right:0.5cm ;
- margin-bottom:0.5cm }
-
-dd {
- font-size: 10pt ;
- padding-top: 0.1cm
-}
-
-dt {
- font-size: 10pt ;
- font-weight: bold ;
- background-color: #6FC7FB ;
- padding-left: 0.1cm ;
- padding-top: 0.1cm ;
- padding-bottom: 0.1cm }
-
-div.abstract {
- font-size: 10pt }
-
-div.abstract p.topic-title {
- font-size: 10pt }
-
-div.attention, div.caution, div.danger, div.error, div.hint,
-div.important, div.note, div.tip, div.warning {
- font-size: 10pt }
-
-div.attention p.admonition-title, div.caution p.admonition-title,
-div.danger p.admonition-title, div.error p.admonition-title,
-div.warning p.admonition-title, div.hint p.admonition-title,
-div.important p.admonition-title, div.note p.admonition-title,
-div.tip p.admonition-title {
- margin-top: 0em ;
- font-size: 12pt ;
- font-family: arial,helvetica,univers }
-
-div.dedication {
- font-size: 10pt }
-
-div.dedication p.topic-title {
- font-size: 10pt }
-
-div.figure {
- font-size: 10pt }
-
-div.footer, div.header {
- font-size: 8pt }
-
-div.system-messages {
- font-size: 10pt }
-
-div.system-messages h1 {
- font-size: 12pt }
-
-div.system-message {
- font-size: 10pt }
-
-div.system-message p.system-message-title {
- font-size: 10pt }
-
-div.topic {
- font-size: 10pt }
-
-h1, h2, h3, h4, h5, h6 {
- padding-top: 0.5cm ;
- page-break-after: avoid ;
- font-family: arial,helvetica,univers }
-
-h1 {
- font-size: 18pt }
-
-h1.title {
- color: white ;
- background-color: #00009F ;
- padding-top: 0cm }
-
-h2 {
- font-size: 16pt }
-
-h2.subtitle {
- padding-top: 0cm }
-
-h3 {
- font-size: 14pt }
-
-h4 {
- font-size: 12pt }
-
-h5, h6 {
- font-size: 10pt }
-
-hr {
- width: 100%;
- page-break-after: always }
-
-li {
- padding-top: 1mm ;
- padding-bottom: 1mm }
-
-ol.simple, ul.simple {
- font-size: 10pt }
-
-ol.arabic {
- font-size: 10pt }
-
-ol.loweralpha {
- font-size: 10pt }
-
-ol.upperalpha {
- font-size: 10pt }
-
-ol.lowerroman {
- font-size: 10pt }
-
-ol.upperroman {
- font-size: 10pt }
-
-p.caption {
- font-size: 10pt }
-
-p.credits {
- font-style: italic ;
- font-size: 8pt }
-
-p.label {
- font-size: 10pt }
-
-p.topic-title {
- font-size: 10pt }
-
-pre.address {
- font-family: arial,helvetica,univers ;
- font-size: 10pt }
-
-pre.line-block {
- font-size: 10pt }
-
-pre.literal-block, pre.doctest-block {
- border-width: 1pt ;
- border-style: solid ;
- border-color: #999999 ;
- color: #0000C0 ;
- background-color: #ffffe0 ;
- font-size: 9pt }
-
-span.classifier {
- font-size: 10pt ;
- font-family: arial,helvetica,univers }
-
-span.classifier-delimiter {
- font-size: 10pt ;
- font-family: arial,helvetica,univers }
-
-span.field-argument {
- font-size: 10pt }
-
-span.interpreted {
- font-size: 10pt ;
- font-family: arial,helvetica,univers }
-
-span.option-argument {
- font-size: 10pt }
-
-span.problematic {
- font-size: 10pt }
-
-table {
- font-size: 10pt ;
- border-collapse: collapse ;
- border-width: 1.5pt ;
- border-color: #003366 }
-
-table.citation {
- font-size: 10pt }
-
-table.docinfo {
- font-size: 10pt }
-
-table.footnote {
- font-size: 8pt ;
- text-align: left }
-
-table.table {
- width: 100% }
-
-th {
- border-width: 1.5pt }
-
-td {
- border-width: 1pt }
-
-td, th {
- font-size: 10pt ;
- border-style: thin ;
- border-color: #003366 }
-
-td.docinfo-name, th.field-name {
- font-size: 10pt }
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
- font-size: 10pt }
diff --git a/plac/doc/annotations.py b/plac/doc/annotations.py
deleted file mode 100644
index 0de2140..0000000
--- a/plac/doc/annotations.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# annotations.py
-class Positional(object):
- def __init__(self, help='', type=None, choices=None, metavar=None):
- self.help = help
- self.kind = 'positional'
- self.abbrev = None
- self.type = type
- self.choices = choices
- self.metavar = metavar
diff --git a/plac/doc/cmd_ex.py b/plac/doc/cmd_ex.py
deleted file mode 100644
index 4af18c4..0000000
--- a/plac/doc/cmd_ex.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# cmd_ext.py
-from plac_ext import cmd_interface
-import ishelve2
-
-if __name__ == '__main__':
- cmd_interface(ishelve2.main()).cmdloop()
diff --git a/plac/doc/dbcli.help b/plac/doc/dbcli.help
deleted file mode 100644
index 8c0c18a..0000000
--- a/plac/doc/dbcli.help
+++ /dev/null
@@ -1,13 +0,0 @@
-usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
-
-A script to run queries and SQL scripts on a database
-
-positional arguments:
- db Connection string
- scripts SQL scripts
-
-optional arguments:
- -h, --help show this help message and exit
- -H, --header Header
- -c SQL, --sqlcmd SQL SQL command
- -d |, --delimiter | Column separator
diff --git a/plac/doc/dbcli.py b/plac/doc/dbcli.py
deleted file mode 100644
index d5db392..0000000
--- a/plac/doc/dbcli.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# dbcli.py
-import plac
-from sqlalchemy.ext.sqlsoup import SqlSoup
-
-@plac.annotations(
- db=("Connection string", 'positional', None, SqlSoup),
- header=("Header", 'flag', 'H'),
- sqlcmd=("SQL command", 'option', 'c', str, None, "SQL"),
- delimiter=("Column separator", 'option', 'd'),
- scripts="SQL scripts",
- )
-def main(db, header, sqlcmd, delimiter="|", *scripts):
- "A script to run queries and SQL scripts on a database"
- yield 'Working on %s' % db.bind.url
-
- if sqlcmd:
- result = db.bind.execute(sqlcmd)
- if header: # print the header
- yield delimiter.join(result.keys())
- for row in result: # print the rows
- yield delimiter.join(map(str, row))
-
- for script in scripts:
- db.bind.execute(file(script).read())
- yield 'executed %s' % script
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
diff --git a/plac/doc/example0.py b/plac/doc/example0.py
deleted file mode 100644
index af345c3..0000000
--- a/plac/doc/example0.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def main(arg: "required argument"):
- "do something with arg"
- print('Got %s' % arg)
-
-if __name__ == '__main__':
- import plac; plac.call(main) # passes sys.argv[1:] to main
diff --git a/plac/doc/example1.py b/plac/doc/example1.py
deleted file mode 100644
index f024090..0000000
--- a/plac/doc/example1.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# example1.py
-def main(dsn):
- "Do something with the database"
- # ...
-
-if __name__ == '__main__':
- import sys
- n = len(sys.argv[1:])
- if n == 0:
- sys.exit('usage: python %s dsn' % sys.argv[0])
- elif n == 1:
- main(sys.argv[1])
- else:
- sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:]))
diff --git a/plac/doc/example10.help b/plac/doc/example10.help
deleted file mode 100644
index 8ac3e9f..0000000
--- a/plac/doc/example10.help
+++ /dev/null
@@ -1,10 +0,0 @@
-usage: example10.py [-h] {add,mul} [n [n ...]]
-
-A script to add and multiply numbers
-
-positional arguments:
- {add,mul} The name of an operator
- n A number
-
-optional arguments:
- -h, --help show this help message and exit
diff --git a/plac/doc/example10.py b/plac/doc/example10.py
deleted file mode 100644
index 02bbdc6..0000000
--- a/plac/doc/example10.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# example10.py
-import plac
-
-@plac.annotations(
-operator=("The name of an operator", 'positional', None, str, ['add', 'mul']),
-numbers=("A number", 'positional', None, float, None, "n"))
-def main(operator, *numbers):
- "A script to add and multiply numbers"
- if operator == 'mul':
- op = float.__mul__
- result = 1.0
- else: # operator == 'add'
- op = float.__add__
- result = 0.0
- for n in numbers:
- result = op(result, n)
- return result
-
-if __name__ == '__main__':
- print(plac.call(main))
diff --git a/plac/doc/example11.help b/plac/doc/example11.help
deleted file mode 100644
index 4d36924..0000000
--- a/plac/doc/example11.help
+++ /dev/null
@@ -1,9 +0,0 @@
-usage: example11.py [-h] i n [rest [rest ...]]
-
-positional arguments:
- i This is an int
- n This is a float
- rest Other arguments
-
-optional arguments:
- -h, --help show this help message and exit
diff --git a/plac/doc/example11.py b/plac/doc/example11.py
deleted file mode 100644
index 07fbd40..0000000
--- a/plac/doc/example11.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# example11.py
-import plac
-from annotations import Positional
-
-@plac.annotations(
- i=Positional("This is an int", int),
- n=Positional("This is a float", float),
- rest=Positional("Other arguments"))
-def main(i, n, *rest):
- print(i, n, rest)
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example12.help b/plac/doc/example12.help
deleted file mode 100644
index fec1715..0000000
--- a/plac/doc/example12.help
+++ /dev/null
@@ -1,9 +0,0 @@
-usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]
-
-positional arguments:
- args default arguments
- kw keyword arguments
-
-optional arguments:
- -h, --help show this help message and exit
- -opt OPT some option
diff --git a/plac/doc/example12.py b/plac/doc/example12.py
deleted file mode 100644
index 56c65a9..0000000
--- a/plac/doc/example12.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# example12.py
-import plac
-
-@plac.annotations(
- opt=('some option', 'option'),
- args='default arguments',
- kw='keyword arguments')
-def main(opt, *args, **kw):
- if opt:
- yield 'opt=%s' % opt
- if args:
- yield 'args=%s' % str(args)
- if kw:
- yield 'kw=%s' % kw
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
diff --git a/plac/doc/example13.help b/plac/doc/example13.help
deleted file mode 100644
index c931651..0000000
--- a/plac/doc/example13.help
+++ /dev/null
@@ -1,13 +0,0 @@
-usage: example13.py [-h] {status,commit,checkout,help} ...
-
-A Fake Version Control System
-
-optional arguments:
- -h, --help show this help message and exit
-
-subcommands:
- {status,commit,checkout,help}
- checkout
- commit
- status
- help
diff --git a/plac/doc/example13.py b/plac/doc/example13.py
deleted file mode 100644
index 9982fdd..0000000
--- a/plac/doc/example13.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import plac
-
-class FVCS(object):
- "A Fake Version Control System"
- commands = 'checkout', 'commit', 'status', 'help'
- add_help = True
-
- @plac.annotations(
- name=('a recognized command', 'positional', None, str, commands))
- def help(self, name):
- print(plac.parser_from(self).help_cmd(name))
-
- @plac.annotations(
- url=('url of the source code', 'positional'))
- def checkout(self, url):
- print('checkout', url)
-
- def commit(self):
- print('commit')
-
- @plac.annotations(quiet=('summary information', 'flag'))
- def status(self, quiet):
- print('status', quiet)
-
-main = FVCS()
-
-if __name__ == '__main__':
- plac.call(main)
diff --git a/plac/doc/example2.py b/plac/doc/example2.py
deleted file mode 100644
index 56aa23c..0000000
--- a/plac/doc/example2.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# example2.py
-def main(dsn):
- "Do something on the database"
- print(dsn)
- # ...
-
-if __name__ == '__main__':
- import argparse
- p = argparse.ArgumentParser()
- p.add_argument('dsn')
- arg = p.parse_args()
- main(arg.dsn)
diff --git a/plac/doc/example3.help b/plac/doc/example3.help
deleted file mode 100644
index dd4d96a..0000000
--- a/plac/doc/example3.help
+++ /dev/null
@@ -1,9 +0,0 @@
-usage: example3.py [-h] dsn
-
-Do something with the database
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
diff --git a/plac/doc/example3.py b/plac/doc/example3.py
deleted file mode 100644
index 979b1a4..0000000
--- a/plac/doc/example3.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# example3.py
-def main(dsn):
- "Do something with the database"
- print(dsn)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example4.py b/plac/doc/example4.py
deleted file mode 100644
index 2e76b65..0000000
--- a/plac/doc/example4.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# example4.py
-from datetime import datetime
-
-def main(dsn, table='product', today=datetime.today()):
- "Do something on the database"
- print(dsn, table, today)
-
-if __name__ == '__main__':
- import sys
- args = sys.argv[1:]
- if not args:
- sys.exit('usage: python %s dsn' % sys.argv[0])
- elif len(args) > 2:
- sys.exit('Unrecognized arguments: %s' % ' '.join(argv[2:]))
- main(*args)
diff --git a/plac/doc/example5.help b/plac/doc/example5.help
deleted file mode 100644
index ff57733..0000000
--- a/plac/doc/example5.help
+++ /dev/null
@@ -1,11 +0,0 @@
-usage: example5.py [-h] dsn [table] [today]
-
-Do something on the database
-
-positional arguments:
- dsn
- table [product]
- today [YYYY-MM-DD]
-
-optional arguments:
- -h, --help show this help message and exit
diff --git a/plac/doc/example5.py b/plac/doc/example5.py
deleted file mode 100644
index a2de37c..0000000
--- a/plac/doc/example5.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# example5.py
-from datetime import date
-
-def main(dsn, table='product', today=date.today()):
- "Do something on the database"
- print(dsn, table, today)
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example6.help b/plac/doc/example6.help
deleted file mode 100644
index c950e78..0000000
--- a/plac/doc/example6.help
+++ /dev/null
@@ -1,8 +0,0 @@
-usage: example6.py [-h] [-command COMMAND] dsn
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
- -command COMMAND SQL query
diff --git a/plac/doc/example6.py b/plac/doc/example6.py
deleted file mode 100644
index 045839b..0000000
--- a/plac/doc/example6.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# example6.py
-def main(dsn, command: ("SQL query", 'option')):
- print('executing %r on %s' % (command, dsn))
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example7.help b/plac/doc/example7.help
deleted file mode 100644
index 0834858..0000000
--- a/plac/doc/example7.help
+++ /dev/null
@@ -1,10 +0,0 @@
-usage: example7.py [-h] dsn [scripts [scripts ...]]
-
-Run the given scripts on the database
-
-positional arguments:
- dsn
- scripts
-
-optional arguments:
- -h, --help show this help message and exit
diff --git a/plac/doc/example7.py b/plac/doc/example7.py
deleted file mode 100644
index 475799d..0000000
--- a/plac/doc/example7.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# example7.py
-from datetime import datetime
-
-def main(dsn, *scripts):
- "Run the given scripts on the database"
- for script in scripts:
- print('executing %s' % script)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example7_.help b/plac/doc/example7_.help
deleted file mode 100644
index 6f0f4b8..0000000
--- a/plac/doc/example7_.help
+++ /dev/null
@@ -1,10 +0,0 @@
-usage: example7_.py [-h] dsn [scripts [scripts ...]]
-
-Run the given scripts on the database
-
-positional arguments:
- dsn Database dsn
- scripts SQL scripts
-
-optional arguments:
- -h, --help show this help message and exit
diff --git a/plac/doc/example7_.py b/plac/doc/example7_.py
deleted file mode 100644
index 550a524..0000000
--- a/plac/doc/example7_.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# example7_.py
-from datetime import datetime
-
-def main(dsn: "Database dsn", *scripts: "SQL scripts"):
- "Run the given scripts on the database"
- for script in scripts:
- print('executing %s' % script)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example8.help b/plac/doc/example8.help
deleted file mode 100644
index a566258..0000000
--- a/plac/doc/example8.help
+++ /dev/null
@@ -1,9 +0,0 @@
-usage: example8.py [-h] [-c COMMAND] dsn
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
- -c COMMAND, --command COMMAND
- SQL query
diff --git a/plac/doc/example8.py b/plac/doc/example8.py
deleted file mode 100644
index 1dad399..0000000
--- a/plac/doc/example8.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# example8.py
-def main(command: ("SQL query", 'option', 'c'), dsn):
- if command:
- print('executing %s on %s' % (command, dsn))
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example8_.help b/plac/doc/example8_.help
deleted file mode 100644
index 872b05a..0000000
--- a/plac/doc/example8_.help
+++ /dev/null
@@ -1,9 +0,0 @@
-usage: example8_.py [-h] [-command select * from table] dsn
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
- -command select * from table
- SQL query
diff --git a/plac/doc/example8_.py b/plac/doc/example8_.py
deleted file mode 100644
index cdea364..0000000
--- a/plac/doc/example8_.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# example8_.py
-def main(dsn, command: ("SQL query", 'option')='select * from table'):
- print('executing %r on %s' % (command, dsn))
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/example9.help b/plac/doc/example9.help
deleted file mode 100644
index cda79b3..0000000
--- a/plac/doc/example9.help
+++ /dev/null
@@ -1,8 +0,0 @@
-usage: example9.py [-h] [-v] dsn
-
-positional arguments:
- dsn connection string
-
-optional arguments:
- -h, --help show this help message and exit
- -v, --verbose prints more info
diff --git a/plac/doc/example9.py b/plac/doc/example9.py
deleted file mode 100644
index 8e39eff..0000000
--- a/plac/doc/example9.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# example9.py
-
-def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'):
- if verbose:
- print('connecting to %s' % dsn)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
diff --git a/plac/doc/importer1.py b/plac/doc/importer1.py
deleted file mode 100644
index 233f910..0000000
--- a/plac/doc/importer1.py
+++ /dev/null
@@ -1,20 +0,0 @@
-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.call(FakeImporter)
diff --git a/plac/doc/importer2.py b/plac/doc/importer2.py
deleted file mode 100644
index 61a99b1..0000000
--- a/plac/doc/importer2.py
+++ /dev/null
@@ -1,22 +0,0 @@
-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.call(FakeImporter)
diff --git a/plac/doc/importer3.py b/plac/doc/importer3.py
deleted file mode 100644
index 2a47e99..0000000
--- a/plac/doc/importer3.py
+++ /dev/null
@@ -1,20 +0,0 @@
-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.call(FakeImporter)
diff --git a/plac/doc/importer_ui.py b/plac/doc/importer_ui.py
deleted file mode 100644
index 0a5cc9c..0000000
--- a/plac/doc/importer_ui.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from __future__ import with_statement
-from Tkinter import *
-from importer3 import FakeImporter
-
-def taskwidget(root, task, tick=500):
- "A Label widget showing the output of a task every 500 ms"
- sv = StringVar(root)
- lb = Label(root, textvariable=sv)
- def show_outlist():
- try:
- out = task.outlist[-1]
- except IndexError: # no output yet
- out = ''
- sv.set('%s %s' % (task, out))
- root.after(tick, show_outlist)
- root.after(0, show_outlist)
- return lb
-
-def monitor(tasks):
- root = Tk()
- for task in tasks:
- task.run()
- taskwidget(root, task).pack()
- root.mainloop()
-
-if __name__ == '__main__':
- import plac
- with plac.Interpreter(plac.call(FakeImporter)) as i:
- tasks = [i.submit('import_file f1'), i.submit('import_file f2')]
- monitor(tasks)
diff --git a/plac/doc/ishelve.help b/plac/doc/ishelve.help
deleted file mode 100644
index d68a099..0000000
--- a/plac/doc/ishelve.help
+++ /dev/null
@@ -1,18 +0,0 @@
-usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
-
-Simple interface to a shelve
-
-positional arguments:
- params names of the parameters in the shelve
- setters setters param=value
-
-optional arguments:
- .help show help
- .showall show all parameters in the shelve
- .clear clear the shelve
- .delete DELETE delete an element
- .filename /home/micheles/conf.shelve
- filename of the shelve
-
diff --git a/plac/doc/ishelve.plac b/plac/doc/ishelve.plac
deleted file mode 100644
index ef378b3..0000000
--- a/plac/doc/ishelve.plac
+++ /dev/null
@@ -1,7 +0,0 @@
-#!ishelve.py
-.clear
-a=1 b=2
-.show
-.del a
-.dl b
-.show
diff --git a/plac/doc/ishelve.placet b/plac/doc/ishelve.placet
deleted file mode 100644
index 2637223..0000000
--- a/plac/doc/ishelve.placet
+++ /dev/null
@@ -1,13 +0,0 @@
-#!ishelve.py
-i> .clear # start from a clean state
-cleared the shelve
-i> a=1
-setting a=1
-i> a
-1
-i> .del a
-deleted a
-i> a
-a: not found
-i> .cler # spelling error
-.cler: not found
diff --git a/plac/doc/ishelve.py b/plac/doc/ishelve.py
deleted file mode 100644
index 4f22ba2..0000000
--- a/plac/doc/ishelve.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# ishelve.py
-import os, shelve, plac
-
-DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
-
-@plac.annotations(
- help=('show help', 'flag'),
- showall=('show all parameters in the shelve', 'flag'),
- clear=('clear the shelve', 'flag'),
- delete=('delete an element', 'option'),
- filename=('filename of the shelve', 'option'),
- params='names of the parameters in the shelve',
- setters='setters param=value')
-def main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
- *params, **setters):
- "A simple interface to a shelve. Use .help to see the available commands."
- sh = shelve.open(filename)
- try:
- if not any([help, showall, clear, delete, params, setters]):
- yield 'no arguments passed, use .help to see the available commands'
- elif help: # custom help
- yield 'Commands: .help, .showall, .clear, .delete'
- yield '<param> ...'
- yield '<param=value> ...'
- elif showall:
- for param, name in sh.items():
- yield '%s=%s' % (param, name)
- elif clear:
- sh.clear()
- yield 'cleared the shelve'
- elif delete:
- try:
- del sh[delete]
- except KeyError:
- yield '%s: not found' % delete
- else:
- yield 'deleted %s' % delete
- for param in params:
- try:
- yield sh[param]
- except KeyError:
- yield '%s: not found' % param
- for param, value in setters.items():
- sh[param] = value
- yield 'setting %s=%s' % (param, value)
- finally:
- sh.close()
-
-main.add_help = False # there is a custom help, remove the default one
-main.prefix_chars = '.' # use dot-prefixed commands
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
diff --git a/plac/doc/ishelve2.help b/plac/doc/ishelve2.help
deleted file mode 100644
index ade74c0..0000000
--- a/plac/doc/ishelve2.help
+++ /dev/null
@@ -1,8 +0,0 @@
-usage: ishelve2.py [-h] [-configfile CONFIGFILE]
-
-A minimal interface over a shelve object.
-
-optional arguments:
- -h, --help show this help message and exit
- -configfile CONFIGFILE
- path name of the shelve
diff --git a/plac/doc/ishelve2.plac b/plac/doc/ishelve2.plac
deleted file mode 100644
index 5ca0064..0000000
--- a/plac/doc/ishelve2.plac
+++ /dev/null
@@ -1,4 +0,0 @@
-#!ishelve2.py:ShelveInterface -c ~/conf.shelve
-set a 1
-del a
-del a # intentional error
diff --git a/plac/doc/ishelve2.placet b/plac/doc/ishelve2.placet
deleted file mode 100644
index fdc89c4..0000000
--- a/plac/doc/ishelve2.placet
+++ /dev/null
@@ -1,9 +0,0 @@
-#!ishelve2.py:ShelveInterface -configfile=~/test.shelve
-i> del
-deleting everything
-i> set a 1
-setting a=1
-i> set b 2
-setting b=2
-i> show a
-a = 1
diff --git a/plac/doc/ishelve2.py b/plac/doc/ishelve2.py
deleted file mode 100644
index dbc5d68..0000000
--- a/plac/doc/ishelve2.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# ishelve2.py
-import shelve, os, sys, plac
-
-class ShelveInterface(object):
- "A minimal interface over a shelve object."
- commands = 'set', 'show', 'showall', 'delete'
- @plac.annotations(
- configfile=('path name of the shelve', 'option'))
- def __init__(self, configfile):
- self.configfile = configfile or '~/conf.shelve'
- self.fname = os.path.expanduser(self.configfile)
- self.__doc__ += '\nOperating on %s.\nUse 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):
- "set name value"
- yield 'setting %s=%s' % (name, value)
- self.sh[name] = value
- def show(self, *names):
- "show given parameters"
- for name in names:
- yield '%s = %s' % (name, self.sh[name]) # no error checking
- def showall(self):
- "show all parameters"
- for name in self.sh:
- yield '%s = %s' % (name, self.sh[name])
- def delete(self, name=None):
- "delete given parameter (or everything)"
- if name is None:
- yield 'deleting everything'
- self.sh.clear()
- else:
- yield 'deleting %s' % name
- del self.sh[name] # no error checking
-
-main = ShelveInterface # useful for the tests
-
-if __name__ == '__main__':
- plac.Interpreter.call(ShelveInterface)
diff --git a/plac/doc/ishelve3.py b/plac/doc/ishelve3.py
deleted file mode 100644
index c37178b..0000000
--- a/plac/doc/ishelve3.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# ishelve3.py
-from ishelve2 import ShelveInterface as main
-
-if __name__ == '__main__':
- import plac; plac.Interpreter.call(main)
diff --git a/plac/doc/picalculator.py b/plac/doc/picalculator.py
deleted file mode 100644
index 75df2b2..0000000
--- a/plac/doc/picalculator.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from __future__ import with_statement
-from random import random
-import multiprocessing
-import plac
-
-class PiCalculator(object):
- """Compute pi in parallel with threads or processes"""
-
- @plac.annotations(
- npoints=('number of integration points', 'positional', None, int),
- mode=('sequential|parallel|threaded', 'option', 'm', str, 'SPT'))
- def __init__(self, npoints, mode='S'):
- self.npoints = npoints
- if mode == 'P':
- self.mpcommands = ['calc_pi']
- elif mode == 'T':
- self.thcommands = ['calc_pi']
- elif mode == 'S':
- self.commands = ['calc_pi']
- self.n_cpu = multiprocessing.cpu_count()
-
- def submit_tasks(self):
- self.i = plac.Interpreter(self).__enter__()
- return [self.i.submit('calc_pi %d' % (self.npoints / self.n_cpu))
- for _ in range(self.n_cpu)]
-
- def close(self):
- self.i.close()
-
- @plac.annotations(
- npoints=('npoints', 'positional', None, int))
- def calc_pi(self, npoints):
- counts = 0
- for j in xrange(npoints):
- n, r = divmod(j, 1000000)
- if r == 0:
- yield '%dM iterations' % n
- x, y = random(), random()
- if x*x + y*y < 1:
- counts += 1
- yield (4.0 * counts)/npoints
-
- def run(self):
- tasks = self.i.tasks()
- for t in tasks:
- t.run()
- try:
- total = 0
- for task in tasks:
- total += task.result
- except: # the task was killed
- print tasks
- return
- return total / self.n_cpu
-
-if __name__ == '__main__':
- pc = plac.call(PiCalculator)
- pc.submit_tasks()
- try:
- import time; t0 = time.time()
- print '%f in %f seconds ' % (pc.run(), time.time() - t0)
- finally:
- pc.close()
diff --git a/plac/doc/plac.el b/plac/doc/plac.el
deleted file mode 100644
index 0eddcbb..0000000
--- a/plac/doc/plac.el
+++ /dev/null
@@ -1,77 +0,0 @@
-;;; Emacs-plac integration: add the following to your .emacs
-
-(define-generic-mode 'plac-mode
- '("#") ; comment chars
- '(); highlighted commands
- nil
- '(".plac\\'"); file extensions
- nil)
-
-(add-hook 'plac-mode-hook (lambda () (local-set-key [f4] 'plac-start)))
-(add-hook 'plac-mode-hook (lambda () (local-set-key [f5] 'plac-send)))
-(add-hook 'plac-mode-hook (lambda () (local-set-key [f6] 'plac-stop)))
-
-(defconst terminator 59); ASCII code for the semicolon
-(defvar *plac-process* nil)
-
-(defun plac-start ()
- "Start an inferior plac process by inferring the script to use from the
- shebang line"
- (interactive)
- (let ((shebang-line
- (save-excursion
- (goto-line 1) (end-of-line)
- (buffer-substring-no-properties 3 (point)))))
- (if *plac-process* (princ "plac already started")
- (setq *plac-process*
- (start-process
- "plac" "*plac*" "plac_runner.py" "-m" shebang-line))))
- (display-buffer "*plac*"))
-
-;(defun plac-send ()
-; "Send the current region to the inferior plac process"
-; (interactive)
-; (save-excursion (set-buffer "*plac*") (erase-buffer))
-; (process-send-region *plac-process* (region-beginning) (region-end)))
-
-(defun current-paragraph-beg-end ()
- "Returns the extrema of the current paragraph, delimited by semicolons"
- (interactive)
- (save-excursion
- (let ((beg (save-excursion (goto-line 2) (point))); skip the shebang
- (end (point-max)))
- ;; go backward
- (while (> (point) beg)
- (goto-char (1- (point)))
- (if (= terminator (following-char))
- (setq beg (point))))
- (if (= terminator (following-char))
- (setq beg (1+ beg)))
- ;; go forward
- (while (< (point) end)
- (goto-char (1+ (point)))
- (if (= 59 (following-char))
- (setq end (point))))
- (if (= 59 (following-char))
- (setq end (1+ end)))
- (list beg end))))
-
-(defun plac-send ()
- "Send the current region to the inferior plac process"
- (interactive)
- (save-excursion (set-buffer "*plac*") (erase-buffer))
- (let ((p (apply 'buffer-substring-no-properties (current-paragraph-beg-end))))
- (message p)
- (process-send-string *plac-process* (concat p "\n"))))
- ;(switch-to-buffer-other-window "*plac*")))
- ;(save-excursion (set-buffer "*plac*")
- ; (set-window-start (selected-window) 1 nil))))
-
-(defun plac-stop ()
- "Stop the inferior plac process by sending to it an EOF"
- (interactive)
- (process-send-eof *plac-process*)
- (setq *plac-process* nil)
- "killed *plac-process*")
-
-(provide 'plac)
diff --git a/plac/doc/plac.html b/plac/doc/plac.html
index a0c738c..d53ecf8 100644
--- a/plac/doc/plac.html
+++ b/plac/doc/plac.html
@@ -5,3034 +5,10 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title></title>
-<style type="text/css">
-
-.first {
- margin-top: 0 }
-
-.last {
- margin-bottom: 0 }
-
-a.toc-backref {
- text-decoration: none ;
- color: black }
-
-dd {
- margin-bottom: 0.5em }
-
-div.abstract {
- margin: 2em 5em }
-
-div.abstract p.topic-title {
- font-weight: bold ;
- text-align: center }
-
-div.attention, div.caution, div.danger, div.error, div.hint,
-div.important, div.note, div.tip, div.warning {
- margin: 2em ;
- border: medium outset ;
- padding: 1em }
-
-div.attention p.admonition-title, div.caution p.admonition-title,
-div.danger p.admonition-title, div.error p.admonition-title,
-div.warning p.admonition-title {
- color: red ;
- font-weight: bold ;
- font-family: sans-serif }
-
-div.hint p.admonition-title, div.important p.admonition-title,
-div.note p.admonition-title, div.tip p.admonition-title {
- font-weight: bold ;
- font-family: sans-serif }
-
-div.dedication {
- margin: 2em 5em ;
- text-align: center ;
- font-style: italic }
-
-div.dedication p.topic-title {
- font-weight: bold ;
- font-style: normal }
-
-div.figure {
- margin-left: 2em }
-
-div.footer, div.header {
- font-size: smaller }
-
-div.system-messages {
- margin: 5em }
-
-div.system-messages h1 {
- color: red }
-
-div.system-message {
- border: medium outset ;
- padding: 1em }
-
-div.system-message p.system-message-title {
- color: red ;
- font-weight: bold }
-
-div.topic {
- margin: 2em }
-
-hr {
- width: 75% }
-
-ol.simple, ul.simple {
- margin-bottom: 1em }
-
-ol.arabic {
- list-style: decimal }
-
-ol.loweralpha {
- list-style: lower-alpha }
-
-ol.upperalpha {
- list-style: upper-alpha }
-
-ol.lowerroman {
- list-style: lower-roman }
-
-ol.upperroman {
- list-style: upper-roman }
-
-p.caption {
- font-style: italic }
-
-p.credits {
- font-style: italic ;
- font-size: smaller }
-
-p.label {
- white-space: nowrap }
-
-p.topic-title {
- font-weight: bold }
-
-pre.address {
- margin-bottom: 0 ;
- margin-top: 0 ;
- font-family: serif ;
- font-size: 100% }
-
-pre.line-block {
- font-family: serif ;
- font-size: 100% }
-
-pre.literal-block, pre.doctest-block {
- background-color: #eeeeee }
-
-span.classifier {
- font-family: sans-serif ;
- font-style: oblique }
-
-span.classifier-delimiter {
- font-family: sans-serif ;
- font-weight: bold }
-
-span.interpreted {
- font-family: sans-serif }
-
-span.option-argument {
- font-style: italic }
-
-span.pre {
- white-space: pre }
-
-span.problematic {
- color: red }
-
-table {
- margin-top: 0.5em ;
- margin-bottom: 0.5em }
-
-table.citation {
- border-left: solid thin gray ;
- padding-left: 0.5ex }
-
-table.docinfo {
- margin: 2em 4em }
-
-table.footnote {
- border-left: solid thin black ;
- padding-left: 0.5ex }
-
-td, th {
- padding-left: 0.5em ;
- padding-right: 0.5em ;
- vertical-align: top }
-
-th.docinfo-name, th.field-name {
- font-weight: bold ;
- text-align: left ;
- white-space: nowrap }
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
- font-size: 100% }
-
-tt {
- background-color: #eeeeee }
-
-ul.auto-toc {
- list-style-type: none }
-
-
-/*
-Additional styles for "modern"-style of DocFactory.
-
-:Author: Gunnar Schwant
-:Contact: g.schwant@gmx.de
-*/
-
-.first {
- font-size: 10pt }
-
-.last {
- font-size: 10pt }
-
-a {
- text-decoration: none }
-
-a.reference {
- color: #00009F }
-
-a:hover {
- background-color: #00009F ;
- color: white }
-
-body {
- font-family: arial,helvetica,univers ;
- font-size: 10pt ;
- padding-top: 0.6cm ;
- margin-left:0.5cm ;
- margin-right:0.5cm ;
- margin-bottom:0.5cm }
-
-dd {
- font-size: 10pt ;
- padding-top: 0.1cm
-}
-
-dt {
- font-size: 10pt ;
- font-weight: bold ;
- background-color: #6FC7FB ;
- padding-left: 0.1cm ;
- padding-top: 0.1cm ;
- padding-bottom: 0.1cm }
-
-div.abstract {
- font-size: 10pt }
-
-div.abstract p.topic-title {
- font-size: 10pt }
-
-div.attention, div.caution, div.danger, div.error, div.hint,
-div.important, div.note, div.tip, div.warning {
- font-size: 10pt }
-
-div.attention p.admonition-title, div.caution p.admonition-title,
-div.danger p.admonition-title, div.error p.admonition-title,
-div.warning p.admonition-title, div.hint p.admonition-title,
-div.important p.admonition-title, div.note p.admonition-title,
-div.tip p.admonition-title {
- margin-top: 0em ;
- font-size: 12pt ;
- font-family: arial,helvetica,univers }
-
-div.dedication {
- font-size: 10pt }
-
-div.dedication p.topic-title {
- font-size: 10pt }
-
-div.figure {
- font-size: 10pt }
-
-div.footer, div.header {
- font-size: 8pt }
-
-div.system-messages {
- font-size: 10pt }
-
-div.system-messages h1 {
- font-size: 12pt }
-
-div.system-message {
- font-size: 10pt }
-
-div.system-message p.system-message-title {
- font-size: 10pt }
-
-div.topic {
- font-size: 10pt }
-
-h1, h2, h3, h4, h5, h6 {
- padding-top: 0.5cm ;
- page-break-after: avoid ;
- font-family: arial,helvetica,univers }
-
-h1 {
- font-size: 18pt }
-
-h1.title {
- color: white ;
- background-color: #00009F ;
- padding-top: 0cm }
-
-h2 {
- font-size: 16pt }
-
-h2.subtitle {
- padding-top: 0cm }
-
-h3 {
- font-size: 14pt }
-
-h4 {
- font-size: 12pt }
-
-h5, h6 {
- font-size: 10pt }
-
-hr {
- width: 100%;
- page-break-after: always }
-
-li {
- padding-top: 1mm ;
- padding-bottom: 1mm }
-
-ol.simple, ul.simple {
- font-size: 10pt }
-
-ol.arabic {
- font-size: 10pt }
-
-ol.loweralpha {
- font-size: 10pt }
-
-ol.upperalpha {
- font-size: 10pt }
-
-ol.lowerroman {
- font-size: 10pt }
-
-ol.upperroman {
- font-size: 10pt }
-
-p.caption {
- font-size: 10pt }
-
-p.credits {
- font-style: italic ;
- font-size: 8pt }
-
-p.label {
- font-size: 10pt }
-
-p.topic-title {
- font-size: 10pt }
-
-pre.address {
- font-family: arial,helvetica,univers ;
- font-size: 10pt }
-
-pre.line-block {
- font-size: 10pt }
-
-pre.literal-block, pre.doctest-block {
- border-width: 1pt ;
- border-style: solid ;
- border-color: #999999 ;
- color: #0000C0 ;
- background-color: #ffffe0 ;
- font-size: 9pt }
-
-span.classifier {
- font-size: 10pt ;
- font-family: arial,helvetica,univers }
-
-span.classifier-delimiter {
- font-size: 10pt ;
- font-family: arial,helvetica,univers }
-
-span.field-argument {
- font-size: 10pt }
-
-span.interpreted {
- font-size: 10pt ;
- font-family: arial,helvetica,univers }
-
-span.option-argument {
- font-size: 10pt }
-
-span.problematic {
- font-size: 10pt }
-
-table {
- font-size: 10pt ;
- border-collapse: collapse ;
- border-width: 1.5pt ;
- border-color: #003366 }
-
-table.citation {
- font-size: 10pt }
-
-table.docinfo {
- font-size: 10pt }
-
-table.footnote {
- font-size: 8pt ;
- text-align: left }
-
-table.table {
- width: 100% }
-
-th {
- border-width: 1.5pt }
-
-td {
- border-width: 1pt }
-
-td, th {
- font-size: 10pt ;
- border-style: thin ;
- border-color: #003366 }
-
-td.docinfo-name, th.field-name {
- font-size: 10pt }
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
- font-size: 10pt }
-
-</style>
</head>
<body>
-<div class="document">
-
-
-<div class="section" id="plac-parsing-the-command-line-the-easy-way">
-<h1><a class="toc-backref" href="#id15">Plac: Parsing the Command Line the Easy Way</a></h1>
-<table class="docutils field-list" frame="void" rules="none">
-<col class="field-name" />
-<col class="field-body" />
-<tbody valign="top">
-<tr class="field"><th class="field-name">Author:</th><td class="field-body">Michele Simionato</td>
-</tr>
-<tr class="field"><th class="field-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato&#64;gmail.com">michele.simionato&#64;gmail.com</a></td>
-</tr>
-<tr class="field"><th class="field-name">Date:</th><td class="field-body">June 2011</td>
-</tr>
-<tr class="field"><th class="field-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="field-name">Project page:</th><td class="field-body"><a class="reference external" href="http://plac.googlecode.com/hg/doc/plac.html">http://plac.googlecode.com/hg/doc/plac.html</a></td>
-</tr>
-<tr class="field"><th class="field-name">Requires:</th><td class="field-body">Python 2.3+</td>
-</tr>
-<tr class="field"><th class="field-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install <span class="pre">-U</span> plac</tt></td>
-</tr>
-<tr class="field"><th class="field-name">License:</th><td class="field-body">BSD license</td>
-</tr>
-</tbody>
-</table>
-<div class="contents topic" id="contents">
-<p class="topic-title first">Contents</p>
-<ul class="simple">
-<li><a class="reference internal" href="#plac-parsing-the-command-line-the-easy-way" id="id15">Plac: Parsing the Command Line the Easy Way</a><ul>
-<li><a class="reference internal" href="#the-importance-of-scaling-down" id="id16">The importance of scaling down</a></li>
-<li><a class="reference internal" href="#scripts-with-required-arguments" id="id17">Scripts with required arguments</a></li>
-<li><a class="reference internal" href="#scripts-with-default-arguments" id="id18">Scripts with default arguments</a></li>
-<li><a class="reference internal" href="#scripts-with-options-and-smart-options" id="id19">Scripts with options (and smart options)</a></li>
-<li><a class="reference internal" href="#scripts-with-flags" id="id20">Scripts with flags</a></li>
-<li><a class="reference internal" href="#plac-for-python-2-x-users" id="id21">plac for Python 2.X users</a></li>
-<li><a class="reference internal" href="#more-features" id="id22">More features</a></li>
-<li><a class="reference internal" href="#a-realistic-example" id="id23">A realistic example</a></li>
-<li><a class="reference internal" href="#keyword-arguments" id="id24">Keyword arguments</a></li>
-<li><a class="reference internal" href="#final-example-a-shelve-interface" id="id25">Final example: a shelve interface</a></li>
-<li><a class="reference internal" href="#plac-vs-argparse" id="id26">plac vs argparse</a></li>
-<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id27">plac vs the rest of the world</a></li>
-<li><a class="reference internal" href="#the-future" id="id28">The future</a></li>
-<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id29">Trivia: the story behind the name</a></li>
-</ul>
-</li>
-<li><a class="reference internal" href="#advanced-usages-of-plac" id="id30">Advanced usages of plac</a><ul>
-<li><a class="reference internal" href="#introduction" id="id31">Introduction</a></li>
-<li><a class="reference internal" href="#from-scripts-to-interactive-applications" id="id32">From scripts to interactive applications</a></li>
-<li><a class="reference internal" href="#testing-a-plac-application" id="id33">Testing a plac application</a></li>
-<li><a class="reference internal" href="#plac-easy-tests" id="id34">Plac easy tests</a></li>
-<li><a class="reference internal" href="#plac-batch-scripts" id="id35">Plac batch scripts</a></li>
-<li><a class="reference internal" href="#implementing-subcommands" id="id36">Implementing subcommands</a></li>
-<li><a class="reference internal" href="#plac-interpreter-call" id="id37">plac.Interpreter.call</a></li>
-<li><a class="reference internal" href="#readline-support" id="id38">Readline support</a></li>
-<li><a class="reference internal" href="#the-plac-runner" id="id39">The plac runner</a></li>
-<li><a class="reference internal" href="#a-non-class-based-example" id="id40">A non class-based example</a></li>
-<li><a class="reference internal" href="#writing-your-own-plac-runner" id="id41">Writing your own plac runner</a></li>
-<li><a class="reference internal" href="#long-running-commands" id="id42">Long running commands</a></li>
-<li><a class="reference internal" href="#threaded-commands" id="id43">Threaded commands</a></li>
-<li><a class="reference internal" href="#running-commands-as-external-processes" id="id44">Running commands as external processes</a></li>
-<li><a class="reference internal" href="#managing-the-output-of-concurrent-commands" id="id45">Managing the output of concurrent commands</a></li>
-<li><a class="reference internal" href="#monitor-support" id="id46">Monitor support</a></li>
-<li><a class="reference internal" href="#parallel-computing-with-plac" id="id47">Parallel computing with plac</a></li>
-<li><a class="reference internal" href="#the-plac-server" id="id48">The plac server</a></li>
-<li><a class="reference internal" href="#summary" id="id49">Summary</a></li>
-<li><a class="reference internal" href="#appendix-custom-annotation-objects" id="id50">Appendix: custom annotation objects</a></li>
-</ul>
-</li>
-</ul>
-</div>
-<div class="section" id="the-importance-of-scaling-down">
-<h2><a class="toc-backref" href="#id16">The importance of scaling down</a></h2>
-<p>There is no want of command line arguments parsers in the Python
-world. The standard library alone contains three different modules:
-<a class="reference external" href="http://docs.python.org/library/getopt.html">getopt</a> (from the stone age),
-<a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> (from Python 2.3) and <a class="reference external" href="http://argparse.googlecode.com">argparse</a> (from Python 2.7). All of
-them are quite powerful and especially <a class="reference external" href="http://argparse.googlecode.com">argparse</a> is an industrial
-strength solution; unfortunately, all of them feature a non-zero learning
-curve and a certain verbosity. They do not scale down well, at
-least in my opinion.</p>
-<p>It should not be necessary to stress the importance <a class="reference external" href="http://www.welton.it/articles/scalable_systems">scaling down</a>;
-nevertheless, a lot of people are obsessed with features and concerned with
-the possibility of scaling up, forgetting the equally important
-issue of scaling down. This is an old meme in
-the computing world: programs should address the common cases simply and
-simple things should be kept simple, while at the same keeping
-difficult things possible. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> adhere as much as possible to this
-philosophy and it is designed to handle well the simple cases, while
-retaining the ability to handle complex cases by relying on the
-underlying power of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>.</p>
-<p>Technically <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is just a simple wrapper over <a class="reference external" href="http://argparse.googlecode.com">argparse</a> which hides
-most of its complexity by using a declarative interface: the argument
-parser is inferred rather than written down by imperatively. Still, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is
-surprisingly scalable upwards, even without using the underlying
-<a class="reference external" href="http://argparse.googlecode.com">argparse</a>. I have been using Python for 8 years and in my experience
-it is extremely unlikely that you will ever need to go beyond the
-features provided by the declarative interface of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: they should
-be more than enough for 99.9% of the use cases.</p>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is targetting especially unsophisticated users,
-programmers, sys-admins, scientists and in general people writing
-throw-away scripts for themselves, choosing the command line
-interface because it is the quick and simple. Such users are not
-interested in features, they are interested in a small learning curve:
-they just want to be able to write a simple command line tool from a
-simple specification, not to build a command-line parser by
-hand. Unfortunately, the modules in the standard library forces them
-to go the hard way. They are designed to implement power user tools
-and they have a non-trivial learning curve. On the contrary, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-is designed to be simple to use and extremely concise, as the examples
-below will show.</p>
-</div>
-<div class="section" id="scripts-with-required-arguments">
-<h2><a class="toc-backref" href="#id17">Scripts with required arguments</a></h2>
-<p>Let me start with the simplest possible thing: a script that takes a
-single argument and does something to it. It cannot get simpler
-than that, unless you consider a script without command-line
-arguments, where there is nothing to parse. Still, it is a use
-case <em>extremely common</em>: I need to write scripts like that nearly
-every day, I wrote hundreds of them in the last few years and I have
-never been happy. Here is a typical example of code I have been
-writing by hand for years:</p>
-<pre class="literal-block">
-# example1.py
-def main(dsn):
- &quot;Do something with the database&quot;
- print(dsn)
- # ...
-
-if __name__ == '__main__':
- import sys
- n = len(sys.argv[1:])
- if n == 0:
- sys.exit('usage: python %s dsn' % sys.argv[0])
- elif n == 1:
- main(sys.argv[1])
- else:
- sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:]))
-
-</pre>
-<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (nine lines)
-is essentially boilerplate that should not exist. Actually I think
-the language should recognize the main function and pass to it the
-command-line arguments automatically; unfortunaly this is unlikely to
-happen. I have been writing boilerplate like this in hundreds of
-scripts for years, and every time I <em>hate</em> it. The purpose of using a
-scripting language is convenience and trivial things should be
-trivial. Unfortunately the standard library does not help for this
-incredibly common use case. Using <a class="reference external" href="http://docs.python.org/library/getopt.html">getopt</a> and <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> does not help,
-since they are intended to manage options and not positional
-arguments; the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module helps a bit and it is able to reduce
-the boilerplate from nine lines to six lines:</p>
-<pre class="literal-block">
-# example2.py
-def main(dsn):
- &quot;Do something on the database&quot;
- print(dsn)
- # ...
-
-if __name__ == '__main__':
- import argparse
- p = argparse.ArgumentParser()
- p.add_argument('dsn')
- arg = p.parse_args()
- main(arg.dsn)
-
-</pre>
-<p>However, it just feels too complex to instantiate a class and to
-define a parser by hand for such a trivial task.</p>
-<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> module is designed to manage well such use cases, and it is able
-to reduce the original nine lines of boiler plate to two lines. With the
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> module all you need to write is</p>
-<pre class="literal-block">
-# example3.py
-def main(dsn):
- &quot;Do something with the database&quot;
- print(dsn)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> module provides for free (actually the work is done by the
-underlying <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module) a nice usage message:</p>
-<pre class="literal-block">
-$ python example3.py -h
-</pre>
-<pre class="literal-block">
-usage: example3.py [-h] dsn
-
-Do something with the database
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
-
-</pre>
-<p>Moreover <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> manages the case of missing arguments and of too many arguments.
-This is only the tip of the iceberg: <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to do much more than that.</p>
-</div>
-<div class="section" id="scripts-with-default-arguments">
-<h2><a class="toc-backref" href="#id18">Scripts with default arguments</a></h2>
-<p>The need to have suitable defaults for command-line scripts is quite
-common. For instance I have encountered this use case at work hundreds
-of times:</p>
-<pre class="literal-block">
-# example4.py
-from datetime import datetime
-
-def main(dsn, table='product', today=datetime.today()):
- &quot;Do something on the database&quot;
- print(dsn, table, today)
-
-if __name__ == '__main__':
- import sys
- args = sys.argv[1:]
- if not args:
- sys.exit('usage: python %s dsn' % sys.argv[0])
- elif len(args) &gt; 2:
- sys.exit('Unrecognized arguments: %s' % ' '.join(argv[2:]))
- main(*args)
-
-</pre>
-<p>Here I want to perform a query on a database table, by extracting the
-most recent data: it makes sense for <tt class="docutils literal">today</tt> to be a default argument.
-If there is a most used table (in this example a table called <tt class="docutils literal">'product'</tt>)
-it also makes sense to make it a default argument. Performing the parsing
-of the command-line arguments by hand takes 8 ugly lines of boilerplate
-(using <a class="reference external" href="http://argparse.googlecode.com">argparse</a> would require about the same number of lines).
-With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal">__main__</tt> block reduces to the usual two lines:</p>
-<pre class="literal-block">
-if __name__ == '__main__':
- import plac; plac.call(main)
-</pre>
-<p>In other words, six lines of boilerplate have been removed, and we get
-the usage message for free:</p>
-<pre class="literal-block">
-usage: example5.py [-h] dsn [table] [today]
-
-Do something on the database
-
-positional arguments:
- dsn
- table [product]
- today [YYYY-MM-DD]
-
-optional arguments:
- -h, --help show this help message and exit
-
-</pre>
-<p>Notice that by default <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> prints the string representation
-of the default values (with square brackets) in the usage message.
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> manages transparently even the case when you want to pass a
-variable number of arguments. Here is an example, a script running
-on a database a series of SQL scripts:</p>
-<pre class="literal-block">
-# example7.py
-from datetime import datetime
-
-def main(dsn, *scripts):
- &quot;Run the given scripts on the database&quot;
- for script in scripts:
- print('executing %s' % script)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<p>Here is the usage message:</p>
-<pre class="literal-block">
-usage: example7.py [-h] dsn [scripts [scripts ...]]
-
-Run the given scripts on the database
-
-positional arguments:
- dsn
- scripts
-
-optional arguments:
- -h, --help show this help message and exit
-
-</pre>
-<p>The examples here should have made clear that <em>plac is able to figure out
-the command-line arguments parser to use from the signature of the main
-function</em>. This is the whole idea behind <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: if the intent is clear,
-let's the machine take care of the details.</p>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to an old Python Cookbook recipe of mine (<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a>), in
-the sense that it delivers the programmer from the burden of writing
-the parser, but is less of a hack: instead of extracting the parser
-from the docstring of the module, it extracts it from the signature of
-the <tt class="docutils literal">main</tt> function.</p>
-<p>The idea comes from the <cite>function annotations</cite> concept, a new
-feature of Python 3. An example is worth a thousand words, so here
-it is:</p>
-<pre class="literal-block">
-# example7_.py
-from datetime import datetime
-
-def main(dsn: &quot;Database dsn&quot;, *scripts: &quot;SQL scripts&quot;):
- &quot;Run the given scripts on the database&quot;
- for script in scripts:
- print('executing %s' % script)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<p>Here the arguments of the <tt class="docutils literal">main</tt> function have been annotated with
-strings which are intented to be used in the help message:</p>
-<pre class="literal-block">
-usage: example7_.py [-h] dsn [scripts [scripts ...]]
-
-Run the given scripts on the database
-
-positional arguments:
- dsn Database dsn
- scripts SQL scripts
-
-optional arguments:
- -h, --help show this help message and exit
-
-</pre>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize much more complex annotations, as
-I will show in the next paragraphs.</p>
-</div>
-<div class="section" id="scripts-with-options-and-smart-options">
-<h2><a class="toc-backref" href="#id19">Scripts with options (and smart options)</a></h2>
-<p>It is surprising how few command-line scripts with options I have
-written over the years (probably less than a hundred), compared to the
-number of scripts with positional arguments I wrote (certainly more
-than a thousand of them). Still, this use case cannot be neglected.
-The standard library modules (all of them) are quite verbose when it
-comes to specifying the options and frankly I have never used them
-directly. Instead, I have always relied on the
-<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, which provides a convenient wrapper over
-<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a>. Alternatively, in the simplest cases, I have just
-performed the parsing by hand. In <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the parser is inferred by the
-function annotations. Here is an example:</p>
-<pre class="literal-block">
-# example8.py
-def main(command: (&quot;SQL query&quot;, 'option', 'c'), dsn):
- if command:
- print('executing %s on %s' % (command, dsn))
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<p>Here the argument <tt class="docutils literal">command</tt> has been annotated with the tuple
-<tt class="docutils literal">(&quot;SQL query&quot;, 'option', 'c')</tt>: the first string is the help string
-which will appear in the usage message, the second string tells <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-that <tt class="docutils literal">command</tt> is an option and the third string that there is also
-a short form of the option <tt class="docutils literal"><span class="pre">-c</span></tt>, the long form being <tt class="docutils literal"><span class="pre">--command</span></tt>.
-The usage message is the following:</p>
-<pre class="literal-block">
-usage: example8.py [-h] [-c COMMAND] dsn
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
- -c COMMAND, --command COMMAND
- SQL query
-
-</pre>
-<p>Here are two examples of usage:</p>
-<pre class="literal-block">
-$ python3 example8.py -c&quot;select * from table&quot; dsn
-executing select * from table on dsn
-
-$ python3 example8.py --command=&quot;select * from table&quot; dsn
-executing select * from table on dsn
-</pre>
-<p>The third argument in the function annotation can be omitted: in such
-case it will be assumed to be <tt class="docutils literal">None</tt>. The consequence is that
-the usual dichotomy between long and short options (GNU-style options)
-disappears: we get <em>smart options</em>, which have the single character prefix
-of short options and behave like both long and short options, since
-they can be abbreviated. Here is an example featuring smart options:</p>
-<pre class="literal-block">
-# example6.py
-def main(dsn, command: (&quot;SQL query&quot;, 'option')):
- print('executing %r on %s' % (command, dsn))
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<pre class="literal-block">
-usage: example6.py [-h] [-command COMMAND] dsn
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
- -command COMMAND SQL query
-
-</pre>
-<p>The following are all valid invocations ot the script:</p>
-<pre class="literal-block">
-$ python3 example6.py -c &quot;select&quot; dsn
-executing 'select' on dsn
-$ python3 example6.py -com &quot;select&quot; dsn
-executing 'select' on dsn
-$ python3 example6.py -command=&quot;select&quot; dsn
-executing 'select' on dsn
-</pre>
-<p>Notice that the form <tt class="docutils literal"><span class="pre">-command=SQL</span></tt> is recognized only for the full
-option, not for its abbreviations:</p>
-<pre class="literal-block">
-$ python3 example6.py -com=&quot;select&quot; dsn
-usage: example6.py [-h] [-command COMMAND] dsn
-example6.py: error: unrecognized arguments: -com=select
-</pre>
-<p>If the option is not passed, the variable <tt class="docutils literal">command</tt>
-will get the value <tt class="docutils literal">None</tt>. However, it is possible to specify a non-trivial
-default. Here is an example:</p>
-<pre class="literal-block">
-# example8_.py
-def main(dsn, command: (&quot;SQL query&quot;, 'option')='select * from table'):
- print('executing %r on %s' % (command, dsn))
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<p>Notice that the default value appears in the help message:</p>
-<pre class="literal-block">
-usage: example8_.py [-h] [-command select * from table] dsn
-
-positional arguments:
- dsn
-
-optional arguments:
- -h, --help show this help message and exit
- -command select * from table
- SQL query
-
-</pre>
-<p>When you run the script and you do not pass the <tt class="docutils literal"><span class="pre">-command</span></tt> option, the
-default query will be executed:</p>
-<pre class="literal-block">
-$ python3 example8_.py dsn
-executing 'select * from table' on dsn
-</pre>
-</div>
-<div class="section" id="scripts-with-flags">
-<h2><a class="toc-backref" href="#id20">Scripts with flags</a></h2>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize flags, i.e. boolean options which are
-<tt class="docutils literal">True</tt> if they are passed to the command line and <tt class="docutils literal">False</tt>
-if they are absent. Here is an example:</p>
-<pre class="literal-block">
-# example9.py
-
-def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'):
- if verbose:
- print('connecting to %s' % dsn)
- # ...
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<pre class="literal-block">
-usage: example9.py [-h] [-v] dsn
-
-positional arguments:
- dsn connection string
-
-optional arguments:
- -h, --help show this help message and exit
- -v, --verbose prints more info
-
-</pre>
-<pre class="literal-block">
-$ python3 example9.py -v dsn
-connecting to dsn
-</pre>
-<p>Notice that it is an error trying to specify a default for flags: the
-default value for a flag is always <tt class="docutils literal">False</tt>. If you feel the need to
-implement non-boolean flags, you should use an option with two
-choices, as explained in the &quot;more features&quot; section.</p>
-<p>For consistency with the way the usage message is printed, I suggest
-you to follow the Flag-Option-Required-Default (FORD) convention: in
-the <tt class="docutils literal">main</tt> function write first the flag arguments, then the option
-arguments, then the required arguments and finally the default
-arguments. This is just a convention and you are not forced to use it,
-except for the default arguments (including the varargs) which must
-stay at the end as it is required by the Python syntax.</p>
-<p>I also suggests to specify a one-character abbreviation for flags: in
-this way you can use the GNU-style composition of flags (i.e. <tt class="docutils literal"><span class="pre">-zxvf</span></tt>
-is an abbreviation of <tt class="docutils literal"><span class="pre">-z</span> <span class="pre">-x</span> <span class="pre">-v</span> <span class="pre">-f</span></tt>). I usually do not provide
-the one-character abbreviation for options, since it does not make sense
-to compose them.</p>
-</div>
-<div class="section" id="plac-for-python-2-x-users">
-<h2><a class="toc-backref" href="#id21">plac for Python 2.X users</a></h2>
-<p>I do not use Python 3. At work we are just starting to think about
-migrating to Python 2.6. It will take years before we
-think to migrate to Python 3. I am pretty much sure most Pythonistas
-are in the same situation. Therefore <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides a way to work
-with function annotations even in Python 2.X (including Python 2.3).
-There is no magic involved; you just need to add the annotations
-by hand. For instance the annotated function declaration</p>
-<pre class="literal-block">
-def main(dsn: &quot;Database dsn&quot;, *scripts: &quot;SQL scripts&quot;):
- ...
-</pre>
-<p>is equivalent to the following code:</p>
-<pre class="literal-block">
-def main(dsn, *scripts):
- ...
-main.__annotations__ = dict(
- dsn=&quot;Database dsn&quot;,
- scripts=&quot;SQL scripts&quot;)
-</pre>
-<p>One should be careful to match the keys of the annotation dictionary
-with the names of the arguments in the annotated function; for lazy
-people with Python 2.4 available the simplest way is to use the
-<tt class="docutils literal">plac.annotations</tt> decorator that performs the check for you:</p>
-<pre class="literal-block">
-&#64;plac.annotations(
- dsn=&quot;Database dsn&quot;,
- scripts=&quot;SQL scripts&quot;)
-def main(dsn, *scripts):
- ...
-</pre>
-<p>In the rest of this article I will assume that you are using Python 2.X with
-X &gt;= 4 and I will use the <tt class="docutils literal">plac.annotations</tt> decorator. Notice however
-that the core features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> run even on Python 2.3.</p>
-</div>
-<div class="section" id="more-features">
-<h2><a class="toc-backref" href="#id22">More features</a></h2>
-<p>One of the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is to have a learning curve of <em>minutes</em> for
-its core features, compared to the learning curve of <em>hours</em> of
-<a class="reference external" href="http://argparse.googlecode.com">argparse</a>. In order to reach this goal, I have <em>not</em> sacrificed all
-the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually a lot of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> power persists
-in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Until now, I have only showed simple annotations, but in
-general an annotation is a 6-tuple of the form</p>
-<blockquote>
-<tt class="docutils literal">(help, kind, abbrev, type, choices, metavar)</tt></blockquote>
-<p>where <tt class="docutils literal">help</tt> is the help message, <tt class="docutils literal">kind</tt> is a string in the set {
-<tt class="docutils literal">&quot;flag&quot;</tt>, <tt class="docutils literal">&quot;option&quot;</tt>, <tt class="docutils literal">&quot;positional&quot;</tt>}, <tt class="docutils literal">abbrev</tt> is a
-one-character string or <tt class="docutils literal">None</tt>, <tt class="docutils literal">type</tt> is a callable taking a
-string in input,
-<tt class="docutils literal">choices</tt> is a discrete sequence of values and <tt class="docutils literal">metavar</tt> is a string.</p>
-<p><tt class="docutils literal">type</tt> is used to automagically convert the command line arguments
-from the string type to any Python type; by default there is no
-conversion and <tt class="docutils literal">type=None</tt>.</p>
-<p><tt class="docutils literal">choices</tt> is used to restrict the number of the valid
-options; by default there is no restriction i.e. <tt class="docutils literal">choices=None</tt>.</p>
-<p><tt class="docutils literal">metavar</tt> has two meanings. For a positional argument it is used to
-change the argument name in the usage message (and only there). By
-default the metavar is <tt class="docutils literal">None</tt> and the name in the usage message is
-the same as the argument name. For an option
-the <tt class="docutils literal">metavar</tt> is used differently in the usage message, which has
-now the form <tt class="docutils literal"><span class="pre">[--option-name</span> METAVAR]</tt>. If the <tt class="docutils literal">metavar</tt> is <tt class="docutils literal">None</tt>,
-then it is equal to the uppercased name of the argument, unless the
-argument has a default and in such a case is equal to the stringified
-form of the default.</p>
-<p>Here is an example showing many of the features (copied from the
-<a class="reference external" href="http://argparse.googlecode.com">argparse</a> documentation):</p>
-<pre class="literal-block">
-# example10.py
-import plac
-
-&#64;plac.annotations(
-operator=(&quot;The name of an operator&quot;, 'positional', None, str, ['add', 'mul']),
-numbers=(&quot;A number&quot;, 'positional', None, float, None, &quot;n&quot;))
-def main(operator, *numbers):
- &quot;A script to add and multiply numbers&quot;
- if operator == 'mul':
- op = float.__mul__
- result = 1.0
- else: # operator == 'add'
- op = float.__add__
- result = 0.0
- for n in numbers:
- result = op(result, n)
- return result
-
-if __name__ == '__main__':
- print(plac.call(main))
-
-</pre>
-<p>Here is the usage:</p>
-<pre class="literal-block">
-usage: example10.py [-h] {add,mul} [n [n ...]]
-
-A script to add and multiply numbers
-
-positional arguments:
- {add,mul} The name of an operator
- n A number
-
-optional arguments:
- -h, --help show this help message and exit
-
-</pre>
-<p>Notice that the docstring of the <tt class="docutils literal">main</tt> function has been automatically added
-to the usage message. Here are a couple of examples of use:</p>
-<pre class="literal-block">
-$ python example10.py add 1 2 3 4
-10.0
-$ python example10.py mul 1 2 3 4
-24.0
-$ python example10.py ad 1 2 3 4 # a mispelling error
-usage: example10.py [-h] {add,mul} [n [n ...]]
-example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul')
-</pre>
-<p><tt class="docutils literal">plac.call</tt> can also be used in doctests like this:</p>
-<pre class="doctest-block">
-&gt;&gt;&gt; import plac, example10
-&gt;&gt;&gt; plac.call(example10.main, ['add', '1', '2'])
-3.0
-</pre>
-<p><tt class="docutils literal">plac.call</tt> works for generators too:</p>
-<pre class="doctest-block">
-&gt;&gt;&gt; def main(n):
-... for i in range(int(n)):
-... yield i
-&gt;&gt;&gt; plac.call(main, ['3'])
-[0, 1, 2]
-</pre>
-<p>Internally <tt class="docutils literal">plac.call</tt> tries to convert the output of the main function
-into a list, if possible. If the output is not iterable or it is a
-string, it is left unchanged, but if it is iterable it is converted.
-In particular, generator objects are exhausted by <tt class="docutils literal">plac.call</tt>.</p>
-<p>This behavior avoids mistakes like forgetting of applying
-<tt class="docutils literal">list(result)</tt> to the result of <tt class="docutils literal">plac.call</tt>; moreover it makes
-errors visible early, and avoids mistakes in code like the following:</p>
-<pre class="literal-block">
-try:
- result = plac.call(main, args)
-except:
- # do something
-</pre>
-<p>Without the &quot;listify&quot; functionality, a main function returning a
-generator object would not raise any exception until the generator
-is iterated over.</p>
-<p>If you are a fan of lazyness, you can still have it by setting the <tt class="docutils literal">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">
-<h2><a class="toc-backref" href="#id23">A realistic example</a></h2>
-<p>Here is a more realistic script using most of the features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to
-run SQL queries on a database by relying on <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a>. Notice the usage
-of the <tt class="docutils literal">type</tt> feature to automagically convert a SQLAlchemy connection
-string into a <a class="reference external" href="http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html">SqlSoup</a> object:</p>
-<pre class="literal-block">
-# dbcli.py
-import plac
-from sqlalchemy.ext.sqlsoup import SqlSoup
-
-&#64;plac.annotations(
- db=(&quot;Connection string&quot;, 'positional', None, SqlSoup),
- header=(&quot;Header&quot;, 'flag', 'H'),
- sqlcmd=(&quot;SQL command&quot;, 'option', 'c', str, None, &quot;SQL&quot;),
- delimiter=(&quot;Column separator&quot;, 'option', 'd'),
- scripts=&quot;SQL scripts&quot;,
- )
-def main(db, header, sqlcmd, delimiter=&quot;|&quot;, *scripts):
- &quot;A script to run queries and SQL scripts on a database&quot;
- yield 'Working on %s' % db.bind.url
-
- if sqlcmd:
- result = db.bind.execute(sqlcmd)
- if header: # print the header
- yield delimiter.join(result.keys())
- for row in result: # print the rows
- yield delimiter.join(map(str, row))
-
- for script in scripts:
- db.bind.execute(file(script).read())
- yield 'executed %s' % script
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
-
-</pre>
-<p>You can see the <em>yield-is-print</em> pattern here: instead of using
-<tt class="docutils literal">print</tt> in the main function, I use <tt class="docutils literal">yield</tt>, and I perform the
-print in the <tt class="docutils literal">__main__</tt> block. The advantage of the pattern is that
-tests invoking <tt class="docutils literal">plac.call</tt> and checking the result become trivial:
-had I performed the printing in the main function, the test would have
-involved an ugly hack like redirecting <tt class="docutils literal">sys.stdout</tt> to a
-<tt class="docutils literal">StringIO</tt> object.</p>
-<p>Here is the usage message:</p>
-<pre class="literal-block">
-usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
-
-A script to run queries and SQL scripts on a database
-
-positional arguments:
- db Connection string
- scripts SQL scripts
-
-optional arguments:
- -h, --help show this help message and exit
- -H, --header Header
- -c SQL, --sqlcmd SQL SQL command
- -d |, --delimiter | Column separator
-
-</pre>
-<p>You can check for yourself that the script works.</p>
-</div>
-<div class="section" id="keyword-arguments">
-<h2><a class="toc-backref" href="#id24">Keyword arguments</a></h2>
-<p>Starting from release 0.4, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports keyword arguments. In
-practice that means that if your main function has keyword arguments,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> treats specially arguments of the form <tt class="docutils literal">&quot;name=value&quot;</tt> in the
-command line. Here is an example:</p>
-<pre class="literal-block">
-# example12.py
-import plac
-
-&#64;plac.annotations(
- opt=('some option', 'option'),
- args='default arguments',
- kw='keyword arguments')
-def main(opt, *args, **kw):
- if opt:
- yield 'opt=%s' % opt
- if args:
- yield 'args=%s' % str(args)
- if kw:
- yield 'kw=%s' % kw
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
-
-</pre>
-<p>Here is the generated usage message:</p>
-<pre class="literal-block">
-usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]
-
-positional arguments:
- args default arguments
- kw keyword arguments
-
-optional arguments:
- -h, --help show this help message and exit
- -opt OPT some option
-
-</pre>
-<p>Here is how you call the script:</p>
-<pre class="literal-block">
-$ python example12.py -o X a1 a2 name=value
-opt=X
-args=('a1', 'a2')
-kw={'name': 'value'}
-</pre>
-<p>When using keyword arguments, one must be careful to use names which
-are not alreay taken; for instance in this examples the name <tt class="docutils literal">opt</tt>
-is taken:</p>
-<pre class="literal-block">
-$ python example12.py 1 2 kw1=1 kw2=2 opt=0
-usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
-example12.py: error: colliding keyword arguments: opt
-</pre>
-<p>The names taken are the names of the flags, of the options, and of the
-positional arguments, excepted varargs and keywords. This limitation
-is a consequence of the way the argument names are managed in function calls
-by the Python language.</p>
-</div>
-<div class="section" id="final-example-a-shelve-interface">
-<h2><a class="toc-backref" href="#id25">Final example: a shelve interface</a></h2>
-<p>Here is a less trivial example for the keyword arguments feature.
-The use case is the following: suppose we have stored the
-configuration parameters of a given application into a Python shelve
-and we need a command-line tool to edit the shelve.
-A possible implementation using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> could be the following:</p>
-<pre class="literal-block">
-# ishelve.py
-import os, shelve, plac
-
-DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
-
-&#64;plac.annotations(
- help=('show help', 'flag'),
- showall=('show all parameters in the shelve', 'flag'),
- clear=('clear the shelve', 'flag'),
- delete=('delete an element', 'option'),
- filename=('filename of the shelve', 'option'),
- params='names of the parameters in the shelve',
- setters='setters param=value')
-def main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
- *params, **setters):
- &quot;A simple interface to a shelve. Use .help to see the available commands.&quot;
- sh = shelve.open(filename)
- try:
- if not any([help, showall, clear, delete, params, setters]):
- yield 'no arguments passed, use .help to see the available commands'
- elif help: # custom help
- yield 'Commands: .help, .showall, .clear, .delete'
- yield '&lt;param&gt; ...'
- yield '&lt;param=value&gt; ...'
- elif showall:
- for param, name in sh.items():
- yield '%s=%s' % (param, name)
- elif clear:
- sh.clear()
- yield 'cleared the shelve'
- elif delete:
- try:
- del sh[delete]
- except KeyError:
- yield '%s: not found' % delete
- else:
- yield 'deleted %s' % delete
- for param in params:
- try:
- yield sh[param]
- except KeyError:
- yield '%s: not found' % param
- for param, value in setters.items():
- sh[param] = value
- yield 'setting %s=%s' % (param, value)
- finally:
- sh.close()
-
-main.add_help = False # there is a custom help, remove the default one
-main.prefix_chars = '.' # use dot-prefixed commands
-
-if __name__ == '__main__':
- for output in plac.call(main):
- print(output)
-
-</pre>
-<p>A few notes are in order:</p>
-<ol class="arabic simple">
-<li>I have disabled the ordinary help provided by <a class="reference external" href="http://argparse.googlecode.com">argparse</a> and I have
-implemented a custom help command.</li>
-<li>I have changed the prefix character used to recognize the options
-to a dot.</li>
-<li>Keyword arguments recognition (in the <tt class="docutils literal">**setters</tt>) is used to make it
-possible to store a value in the shelve with the syntax
-<tt class="docutils literal">param_name=param_value</tt>.</li>
-<li><tt class="docutils literal">*params</tt> are used to retrieve parameters from the shelve and some
-error checking is performed in the case of missing parameters</li>
-<li>A command to clear the shelve is implemented as a flag (<tt class="docutils literal">.clear</tt>).</li>
-<li>A command to delete a given parameter is implemented as an option
-(<tt class="docutils literal">.delete</tt>).</li>
-<li>There is an option with default (<tt class="docutils literal">.filename=conf.shelve</tt>) to store
-the filename of the shelve.</li>
-<li>All things considered, the code looks like a poor man object oriented
-interface implemented with a chain of elifs instead of methods. Of course,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can do better than that, but let me start from a low-level approach
-first.</li>
-</ol>
-<p>If you run <tt class="docutils literal">ishelve.py</tt> without arguments you get the following
-message:</p>
-<pre class="literal-block">
-$ python ishelve.py
-no arguments passed, use .help to see the available commands
-</pre>
-<p>If you run <tt class="docutils literal">ishelve.py</tt> with the option <tt class="docutils literal">.h</tt> (or any abbreviation
-of <tt class="docutils literal">.help</tt>) you get:</p>
-<pre class="literal-block">
-$ python ishelve.py .h
-Commands: .help, .showall, .clear, .delete
-&lt;param&gt; ...
-&lt;param=value&gt; ...
-</pre>
-<p>You can check by hand that the tool work:</p>
-<pre class="literal-block">
-$ python ishelve.py .clear # start from an empty shelve
-cleared the shelve
-$ python ishelve.py a=1 b=2
-setting a=1
-setting b=2
-$ python ishelve.py .showall
-b=2
-a=1
-$ python ishelve.py .del b # abbreviation for .delete
-deleted b
-$ python ishelve.py a
-1
-$ python ishelve.py b
-b: not found
-$ python ishelve.py .cler # mispelled command
-usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
-ishelve.py: error: unrecognized arguments: .cler
-</pre>
-</div>
-<div class="section" id="plac-vs-argparse">
-<h2><a class="toc-backref" href="#id26">plac vs argparse</a></h2>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is opinionated and by design it does not try to make available
-all of the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> in an easy way. In particular you
-should be aware of the following limitations/differences (the
-following assumes knowledge of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>):</p>
-<ul class="simple">
-<li>plac does not support the destination concept: the destination
-coincides with the name of the argument, always. This restriction
-has some drawbacks. For instance, suppose you want to define a long
-option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal">yield</tt>,
-which is a Python keyword, and since you cannot introduce an
-argument with that name in a function definition, it is impossible
-to implement it. Your choices are to change the name of the long
-option, or to use <a class="reference external" href="http://argparse.googlecode.com">argparse</a> with a suitable destination.</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support &quot;required options&quot;. As the <a class="reference external" href="http://argparse.googlecode.com">argparse</a>
-documentation puts it: <em>Required options are generally considered bad
-form - normal users expect options to be optional. You should avoid
-the use of required options whenever possible.</em> Notice that since
-<a class="reference external" href="http://argparse.googlecode.com">argparse</a> supports them, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can manage them too, but not directly.</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to
-define generalized two-value flags with values different from <tt class="docutils literal">True</tt>
-and <tt class="docutils literal">False</tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but
-since you can use options with two choices instead, and in any case
-the conversion from <tt class="docutils literal">{True, False}</tt> to any couple of values
-can be trivially implemented with a ternary operator
-(<tt class="docutils literal">value1 if flag else value2</tt>), I have removed it (KISS rules!).</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal">nargs</tt> options directly (it uses them internally,
-though, to implement flag recognition). The reason it that all the use
-cases of interest to me are covered by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and did not feel the need
-to increase the learning curve by adding direct support for <tt class="docutils literal">nargs</tt>.</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does support subparsers, but you must read the <a class="reference external" href="in-writing">advanced usage
-document</a> to see how it works.</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support actions directly. This also
-looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Notice however
-that the ability to define your own annotation objects (again, see
-the <a class="reference external" href="in-writing">advanced usage document</a>) may mitigate the need for custom actions.</li>
-</ul>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can leverage directly on many <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features.</p>
-<p>For instance, you can make invisible an argument in the usage message
-simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or
-<tt class="docutils literal">argparse.SUPPRESS</tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a>
-directly.</p>
-<p>It is also possible to pass options to the underlying
-<tt class="docutils literal">argparse.ArgumentParser</tt> object (currently it accepts the default
-arguments <tt class="docutils literal">description</tt>, <tt class="docutils literal">epilog</tt>, <tt class="docutils literal">prog</tt>, <tt class="docutils literal">usage</tt>,
-<tt class="docutils literal">add_help</tt>, <tt class="docutils literal">argument_default</tt>, <tt class="docutils literal">parents</tt>, <tt class="docutils literal">prefix_chars</tt>,
-<tt class="docutils literal">fromfile_prefix_chars</tt>, <tt class="docutils literal">conflict_handler</tt>, <tt class="docutils literal">formatter_class</tt>).
-It is enough to set such attributes on the <tt class="docutils literal">main</tt> function. For
-instance</p>
-<pre class="literal-block">
-def main(...):
- pass
-
-main.add_help = False
-</pre>
-<p>disables the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This
-mechanism does not look particularly elegant, but it works well
-enough. I assume that the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be happy with
-the defaults and would not want to change them; still it is possible
-if she wants to.</p>
-<p>For instance, by setting the <tt class="docutils literal">description</tt> attribute, it is possible
-to add a comment to the usage message (by default the docstring of the
-<tt class="docutils literal">main</tt> function is used as description).</p>
-<p>It is also possible to change the option prefix; for
-instance if your script must run under Windows and you want to use &quot;/&quot;
-as option prefix you can add the line:</p>
-<pre class="literal-block">
-main.prefix_chars='/-'
-</pre>
-<p>The first prefix char (<tt class="docutils literal">/</tt>) is used
-as the default for the recognition of options and flags;
-the second prefix char (<tt class="docutils literal">-</tt>) is kept to keep the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> option
-working: however you can disable it and reimplement it, if you like,
-as seen in the <tt class="docutils literal">ishelve</tt> example.</p>
-<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
-invoking the <tt class="docutils literal">plac.parser_from</tt> utility function:</p>
-<pre class="doctest-block">
-&gt;&gt;&gt; import plac
-&gt;&gt;&gt; def main(arg):
-... pass
-...
-&gt;&gt;&gt; print(plac.parser_from(main)) #doctest: +ELLIPSIS
-ArgumentParser(prog=...)
-</pre>
-<p>Internally <tt class="docutils literal">plac.call</tt> uses <tt class="docutils literal">plac.parser_from</tt> and adds the parser
-to the main function as an attribute. When <tt class="docutils literal">plac.call(func)</tt> is
-invoked multiple time, the parser is re-used and not rebuilt from scratch again.</p>
-<p>I use <tt class="docutils literal">plac.parser_from</tt> in the unit tests of the module, but regular
-users should not need to use it, unless they want to access <em>all</em>
-of the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> directly without calling the main function.</p>
-<p>Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> to
-understand the meaning of the other options. If there is a set of
-options that you use very often, you may consider writing a decorator
-adding such options to the <tt class="docutils literal">main</tt> function for you. For simplicity,
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic except the addition of the <tt class="docutils literal">.p</tt>
-attribute.</p>
-</div>
-<div class="section" id="plac-vs-the-rest-of-the-world">
-<h2><a class="toc-backref" href="#id27">plac vs the rest of the world</a></h2>
-<p>Originally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> boasted about being &quot;the easiest command-line
-arguments parser in the world&quot;. Since then, people started pointing
-out to me various projects which are based on the same idea
-(extracting the parser from the main function signature) and are
-arguably even easier than <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>:</p>
-<ul class="simple">
-<li><a class="reference external" href="http://pypi.python.org/pypi/opterator">opterator</a> by Dusty Phillips</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/CLIArgs">CLIArgs</a> by Pavel Panchekha</li>
-<li><a class="reference external" href="http://pypi.python.org/pypi/commandline">commandline</a> by David Laban</li>
-</ul>
-<p>Luckily for me none of such projects had the idea of using
-function annotations and <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; as a consequence, they are
-no match for the capabilities of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
-<p>Of course, there are tons of other libraries to parse the command
-line. For instance <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> by Matthew Frazier which appeared on PyPI
-just the day before <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>; <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> is fine but it is certainly not
-easier than <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can also 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 and as such it shares many features with the module <a class="reference external" href="http://packages.python.org/cmd2/">cmd2</a> by
-Catherine Devlin. However, this is completely coincidental, since I became
-aware of the <a class="reference external" href="http://packages.python.org/cmd2/">cmd2</a> module only after writing <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
-<p>Command-line argument parsers keep coming out; between the newcomers I
-will notice <a class="reference external" href="https://github.com/pulp/marrow.script">marrow.script</a> by Alice Bevan-McGregor, which is quite
-similar to <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> in spirit, but does not rely on <a class="reference external" href="http://argparse.googlecode.com">argparse</a> at all.
-<a class="reference external" href="http://packages.python.org/argh">Argh</a> by Andrey Mikhaylenko is also worth mentioning: it is also based
-on <a class="reference external" href="http://argparse.googlecode.com">argparse</a>, it came after <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and I must give credit to the author
-for the choice of the name, much funnier than plac!</p>
-</div>
-<div class="section" id="the-future">
-<h2><a class="toc-backref" href="#id28">The future</a></h2>
-<p>Currently the core of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is around 200 lines of code, not counting blanks,
-comments and docstrings. I do not plan to extend the core much in the
-future. The idea is to keep the module short: it is and it should
-remain a little wrapper over <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually I have thought about
-contributing the core back to <a class="reference external" href="http://argparse.googlecode.com">argparse</a> if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> becomes successfull
-and gains a reasonable number of users. For the moment it should be
-considered in alpha status.</p>
-<p>Notice that even if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> has been designed to be simple to use for
-simple stuff, its power should not be underestimated; it is actually a
-quite advanced tool with a domain of applicability which far exceeds
-the realm of command-line arguments parsers.</p>
-<p>Version 0.5 of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> doubled the code base and the documentation: it is
-based on the idea of using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to implement command-line interpreters,
-i.e. something akin to the <tt class="docutils literal">cmd</tt> module in the standard library, only better.
-The new features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> are described in the <a class="reference external" href="in-writing">advanced usage document</a> .
-They are implemented in a separated module (<tt class="docutils literal">plac_ext.py</tt>), since
-they require Python 2.5 to work, whereas <tt class="docutils literal">plac_core.py</tt> only requires
-Python 2.3.</p>
-</div>
-<div class="section" id="trivia-the-story-behind-the-name">
-<h2><a class="toc-backref" href="#id29">Trivia: the story behind the name</a></h2>
-<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> project started very humbly: I just wanted to make
-easy_installable my old <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, and to publish it on PyPI.
-The original name of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was optionparser and the idea behind it was
-to build an <a class="reference external" href="http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser">OptionParser</a> object from the docstring of the module.
-However, before doing that, I decided to check out the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module,
-since I knew it was going into Python 2.7 and Python 2.7 was coming out.
-Soon enough I realized two things:</p>
-<ol class="arabic simple">
-<li>the single greatest idea of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> was unifying the positional arguments
-and the options in a single namespace object;</li>
-<li>parsing the docstring was so old-fashioned, considering the existence
-of functions annotations in Python 3.</li>
-</ol>
-<p>Putting together these two observations with the original idea of inferring the
-parser I decided to build an <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object from function
-annotations. The <tt class="docutils literal">optionparser</tt> name was ruled out, since I was
-now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal">argparse_plus</tt> was also ruled out,
-since the typical usage was completely different from the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> usage.</p>
-<p>I made a research on PyPI and the name <em>clap</em> (Command Line Arguments Parser)
-was not taken, so I renamed everything to clap. After two days
-a <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> module appeared on PyPI &lt;expletives deleted&gt;!</p>
-<p>Having little imagination, I decided to rename everything again to plac,
-an anagram of clap: since it is a non-existing English name, I hope nobody
-will steal it from me!</p>
-<p>That concludes the section about the basic usage of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. You are now ready to
-read about the advanced usage.</p>
-</div>
-</div>
-<div class="section" id="advanced-usages-of-plac">
-<h1><a class="toc-backref" href="#id30">Advanced usages of plac</a></h1>
-<div class="section" id="introduction">
-<h2><a class="toc-backref" href="#id31">Introduction</a></h2>
-<p>One of the design goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is to make it dead easy to write a
-scriptable and testable interface for an application. You can use
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> whenever you have an API with strings in input and strings in
-output, and that includes a <em>huge</em> domain of applications.</p>
-<p>A string-oriented interface is a scriptable interface by
-construction. That means that you can define a command language for
-your application and that it is possible to write scripts which are
-interpretable by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and can be run as batch scripts.</p>
-<p>Actually, at the most general level, you can see <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> as a generic tool to
-write domain specific languages (DSL). With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you
-can test your application interactively as well as with batch
-scripts, and even with the analogous of Python doctests for your
-defined language.</p>
-<p>You can easily replace the <tt class="docutils literal">cmd</tt> module of the standard library and
-you could easily write an application like <a class="reference external" href="http://twill.idyll.org/">twill</a> with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Or you
-could use it to script your building procedure. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> also supports
-parallel execution of multiple commands and can be used as
-task manager and monitor. It is also quite easy to build a GUI
-or a Web application on top of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. When speaking of things
-you can do with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, your imagination is the only limit!</p>
-</div>
-<div class="section" id="from-scripts-to-interactive-applications">
-<h2><a class="toc-backref" href="#id32">From scripts to interactive applications</a></h2>
-<p>Command-line scripts have many advantages, but they are no substitute
-for interactive applications.
-In particular, if you have a script with a large startup time which must be run
-multiple times, it is best to turn it into an interactive application,
-so that the startup is performed only once. <tt class="docutils literal">plac</tt> provides an
-<tt class="docutils literal">Interpreter</tt> class just for this purpose.</p>
-<p>The <tt class="docutils literal">Interpreter</tt> class wraps the main function of a script and
-provides an <tt class="docutils literal">.interact</tt> method to start an interactive interpreter
-reading commands from the console.</p>
-<p>For instance, you can define an interactive interpreter on top of the
-<tt class="docutils literal">ishelve</tt> script introduced in the <a class="reference external" href="http://plac.googlecode.com/hg/doc/plac.html">basic documentation</a> as
-follows:</p>
-<pre class="literal-block">
-# shelve_interpreter.py
-import plac, ishelve
-
-&#64;plac.annotations(
- interactive=('start interactive interface', 'flag'),
- subcommands='the commands of the underlying ishelve interpreter')
-def main(interactive, *subcommands):
- &quot;&quot;&quot;
- This script works both interactively and non-interactively.
- Use .help to see the internal commands.
- &quot;&quot;&quot;
- if interactive:
- plac.Interpreter(ishelve.main).interact()
- else:
- for out in plac.call(ishelve.main, subcommands):
- print(out)
-
-if __name__ == '__main__':
- plac.call(main)
-
-</pre>
-<p>A trick has been used here: the ishelve command-line interface has been
-hidden inside an external interface. They are distinct: for instance
-the external interface recognizes the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> flag whereas the
-internal interface only recognizes the <tt class="docutils literal">.help</tt> command:</p>
-<pre class="literal-block">
-$ python shelve_interpreter.py -h
-</pre>
-<pre class="literal-block">
-usage: shelve_interpreter.py [-h] [-interactive]
- [subcommands [subcommands ...]]
-
- This script works both interactively and non-interactively.
- Use .help to see the internal commands.
-
-
-positional arguments:
- subcommands the commands of the underlying ishelve interpreter
-
-optional arguments:
- -h, --help show this help message and exit
- -interactive start interactive interface
-
-</pre>
-<p>Thanks to this ingenuous trick, the script can be run both interactively
-and non-interactively:</p>
-<pre class="literal-block">
-$ python shelve_interpreter.py .clear # non-interactive use
-cleared the shelve
-</pre>
-<p>Here is an usage session:</p>
-<pre class="literal-block">
-$ 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
-1
-i&gt; b=2
-setting b=2
-i&gt; a b
-1
-2
-i&gt; .del a
-deleted a
-i&gt; a
-a: not found
-i&gt; .show
-b=2
-i&gt; [CTRL-D]
-</pre>
-<p>The <tt class="docutils literal">.interact</tt> method
-reads commands from the console and send them to the
-underlying interpreter, until the user send a CTRL-D
-command (CTRL-Z in Windows). There 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 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">
-<h2><a class="toc-backref" href="#id33">Testing a plac application</a></h2>
-<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">
-# test_ishelve.py
-import plac, ishelve
-
-def test():
- assert plac.call(ishelve.main, ['.clear']) == ['cleared the shelve']
- assert plac.call(ishelve.main, ['a=1']) == ['setting a=1']
- assert plac.call(ishelve.main, ['a']) == ['1']
- assert plac.call(ishelve.main, ['.delete=a']) == ['deleted a']
- assert plac.call(ishelve.main, ['a']) == ['a: not found']
-
-if __name__ == '__main__':
- test()
-
-</pre>
-<p>However, using <tt class="docutils literal">plac.call</tt> is not especially nice. The big
-issue is that <tt class="docutils literal">plac.call</tt> responds to invalid input by printing an
-error message on stderr and by raising a <tt class="docutils literal">SystemExit</tt>: this is
-certainly not a nice thing to do in a test.</p>
-<p>As a consequence of this behavior it is impossible to test for invalid
-commands, unless you wrap the <tt class="docutils literal">SystemExit</tt> exception by
-hand each time (a possibly you do something with the error message in
-stderr too). Luckily, <tt class="docutils literal">plac</tt> offers a better testing support through
-the <tt class="docutils literal">check</tt> method of <tt class="docutils literal">Interpreter</tt> objects:</p>
-<pre class="literal-block">
-# test_ishelve_more.py
-from __future__ import with_statement
-import plac, ishelve
-
-def test():
- with plac.Interpreter(ishelve.main) as i:
- i.check('.clear', 'cleared the shelve')
- i.check('a=1', 'setting a=1')
- i.check('a', '1')
- i.check('.delete=a', 'deleted a')
- i.check('a', 'a: not found')
-
-</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.
-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
-<tt class="docutils literal">plac.call</tt> directly, but they offer a <em>major</em> semantic advantage when things
-go wrong (read exceptions): an <tt class="docutils literal">Interpreter</tt> object internally invokes
-something like <tt class="docutils literal">plac.call</tt>, but it wraps all exceptions, so that <tt class="docutils literal">i.check</tt>
-is guaranteed not to raise any exception except <tt class="docutils literal">AssertionError</tt>.</p>
-<p>Even the <tt class="docutils literal">SystemExit</tt> exception is captured and you can write your test as</p>
-<blockquote>
-<tt class="docutils literal"><span class="pre">i.check('-cler',</span> 'SystemExit: unrecognized arguments: <span class="pre">-cler')</span></tt></blockquote>
-<p>without risk of exiting from the Python interpreter.</p>
-<p>There is a second advantage of interpreters: if the main function contains some
-initialization code and finalization code
-(<tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt> functions) they will be run only
-once at the beginning and at the end of the interpreter loop.
-<tt class="docutils literal">plac.call</tt> instead ignores the initialization/finalization code.</p>
-</div>
-<div class="section" id="plac-easy-tests">
-<h2><a class="toc-backref" href="#id34">Plac easy tests</a></h2>
-<p>Writing your tests in terms of <tt class="docutils literal">Interpreter.check</tt> is certainly an
-improvement over writing them in terms of <tt class="docutils literal">plac.call</tt>, but they
-are still too low-level for my taste. The <tt class="docutils literal">Interpreter</tt> class provides
-support for doctest-style tests, a.k.a. <em>plac easy tests</em>.</p>
-<p>By using plac easy tests you can cut and paste your interactive session and
-turn it into a runnable automatics test.
-Consider for instance the following file <tt class="docutils literal">ishelve.placet</tt> (the <tt class="docutils literal">.placet</tt>
-extension is a mnemonic for plac easy tests):</p>
-<pre class="literal-block">
-#!ishelve.py
-i&gt; .clear # start from a clean state
-cleared the shelve
-i&gt; a=1
-setting a=1
-i&gt; a
-1
-i&gt; .del a
-deleted a
-i&gt; a
-a: not found
-i&gt; .cler # spelling error
-.cler: not found
-
-</pre>
-<p>Notice the precence of the shebang line containing the name of the
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> tool to test (a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> tool is just a Python module with a
-function called <tt class="docutils literal">main</tt>). The shebang is ignored by the interpreter
-(it looks like a comment to it) but it is there so that external
-tools (say a test runner) can infer the plac interpreter
-to use to test the file.</p>
-<p>You can test <tt class="docutils literal">ishelve.placet</tt> file by calling the
-<tt class="docutils literal">.doctest</tt> method of the interpreter, as in this example:</p>
-<pre class="literal-block">
-$ python -c&quot;import plac, ishelve
-plac.Interpreter(ishelve.main).doctest(open('ishelve.placet'), verbose=True)&quot;
-</pre>
-<p>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.</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>), 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. For more flexibility, it is even possible to pass to the
-interpreter a custom split function with signature <tt class="docutils literal">split(line,
-commentchar)</tt>.</p>
-<p>In addition, I have implemented from scratch some support for line number
-recognition, so that if a test fail you get the line number of the
-failing command. This is especially useful if your tests are
-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 <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
-testing tools. For instance, you can integrate your doctests with <tt class="docutils literal">nose</tt>
-or <tt class="docutils literal">py.test</tt> as follow:</p>
-<pre class="literal-block">
-import os, shlex, plac
-
-def test_doct():
- &quot;&quot;&quot;
- Find all the doctests in the current directory and run them with the
- corresponding plac interpreter (the shebang rules!)
- &quot;&quot;&quot;
- placets = [f for f in os.listdir('.') if f.endswith('.placet')]
- for placet in placets:
- lines = list(open(placet))
- assert lines[0].startswith('#!'), 'Missing or incorrect shebang line!'
- firstline = lines[0][2:] # strip the shebang
- main = plac.import_main(*shlex.split(firstline))
- yield plac.Interpreter(main).doctest, lines[1:]
-</pre>
-<p>Here you should notice that usage of <tt class="docutils literal">plac.import_main</tt>, an utility
-which is able to import the main function of the script specified in
-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
-a string containing directory names separated by colons). If the variable
-<tt class="docutils literal">PLACPATH</tt> is not defined, it just looks in the current directory.
-If the plac tool is not found, an <tt class="docutils literal">ImportError</tt> is raised.</p>
-</div>
-<div class="section" id="plac-batch-scripts">
-<h2><a class="toc-backref" href="#id35">Plac batch scripts</a></h2>
-<p>It is pretty easy to realize that an interactive interpreter can
-also be used to run batch scripts: instead of reading the commands from
-the console, it is enough to read the commands from a file.
-<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> interpreters provide an <tt class="docutils literal">.execute</tt> method to perform just that.</p>
-<p>There is just a subtle point to notice: whereas in an interactive loop
-one wants to manage all exceptions, a batch script should not in the
-background in case of unexpected errors. The implementation of
-<tt class="docutils literal">Interpreter.execute</tt> makes sure that any error raised by
-<tt class="docutils literal">plac.call</tt> internally is re-raised. In other words, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
-interpreters <em>wrap the errors, but does not eat them</em>: the errors are
-always accessible and can be re-raised on demand.</p>
-<p>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
-a=1 b=2
-.show
-.del a
-.dl b
-.show
-
-</pre>
-<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;
-i&gt; .clear
-cleared the shelve
-i&gt; a=1 b=2
-setting a=1
-setting b=2
-i&gt; .show
-b=2
-a=1
-i&gt; .del a
-deleted a
-i&gt; .dl b
-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="implementing-subcommands">
-<h2><a class="toc-backref" href="#id36">Implementing subcommands</a></h2>
-<p>When I discussed the <tt class="docutils literal">ishelve</tt> implementation in the <a class="reference external" href="http://plac.googlecode.com/hg/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 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
-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).
-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>The shelve interface can be rewritten in an object-oriented way as follows:</p>
-<pre class="literal-block">
-# ishelve2.py
-import shelve, os, sys, plac
-
-class ShelveInterface(object):
- &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):
- self.configfile = configfile or '~/conf.shelve'
- self.fname = os.path.expanduser(self.configfile)
- self.__doc__ += '\nOperating on %s.\nUse 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)
- self.sh[name] = value
- def show(self, *names):
- &quot;show given parameters&quot;
- for name in names:
- yield '%s = %s' % (name, self.sh[name]) # no error checking
- def showall(self):
- &quot;show all parameters&quot;
- for name in self.sh:
- yield '%s = %s' % (name, self.sh[name])
- def delete(self, name=None):
- &quot;delete given parameter (or everything)&quot;
- if name is None:
- yield 'deleting everything'
- self.sh.clear()
- else:
- yield 'deleting %s' % name
- del self.sh[name] # no error checking
-
-main = ShelveInterface # useful for the tests
-
-if __name__ == '__main__':
- plac.Interpreter.call(ShelveInterface)
-
-</pre>
-<p><tt class="docutils literal">plac.Interpreter</tt> objects wrap context manager objects
-consistently. In other words, if you wrap an object with
-<tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt> methods, they are invoked in the right
-order (<tt class="docutils literal">__enter__</tt> before the interpreter loop starts and
-<tt class="docutils literal">__exit__</tt> after the interpreter loop ends, both in the regular and
-in the exceptional case). In our example, the methods <tt class="docutils literal">__enter__</tt>
-and <tt class="docutils literal">__exit__</tt> make sure the the shelve is opened and closed
-correctly even in the case of exceptions. Notice that I have not
-implemented any error checking in the <tt class="docutils literal">show</tt> and <tt class="docutils literal">delete</tt> methods
-on purpose, to verify that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> works correctly in the presence of
-exceptions.</p>
-<p>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">
-$ python ishelve2.py
-A minimal interface over a shelve object.
-Operating on /home/micheles/conf.shelve.
-Use help to see the available commands.
-i&gt; help
-
-special commands
-================
-last_tb
-
-custom commands
-===============
-delete set show showall
-
-i&gt; delete
-deleting everything
-i&gt; set a pippo
-setting a=pippo
-i&gt; set b lippo
-setting b=lippo
-i&gt; showall
-b = lippo
-a = pippo
-i&gt; show a b
-a = pippo
-b = lippo
-i&gt; del a
-deleting a
-i&gt; showall
-b = lippo
-i&gt; delete a
-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>CHANGED IN VERSION 0.9: if you have an old version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the
-<tt class="docutils literal">help</tt> command must be prefixed with a dot, i.e. you must write
-<tt class="docutils literal">.help</tt>. The old behavior was more consistent in my opinion, since
-it made it clear that the <tt class="docutils literal">help</tt> command was special and threated
-differently from the regular commands. However many users complained against
-the dot, so I changed it to make them happy. Starting from release 0.9
-the <tt class="docutils literal">help</tt> command is just an alias for <tt class="docutils literal"><span class="pre">--help</span></tt>, in the
-sense that there is a preprocessor step replacing <tt class="docutils literal">help</tt> with <tt class="docutils literal"><span class="pre">--help</span></tt>
-in the argument list.
-Notice that if you implement a custom <tt class="docutils literal">help</tt> command in the commander class
-the preprocessor will be automatically disabled: passing <tt class="docutils literal">help</tt>
-will call the custom help command, just as you would expect.</p>
-</div>
-<div class="section" id="plac-interpreter-call">
-<h2><a class="toc-backref" href="#id37">plac.Interpreter.call</a></h2>
-<p>At the core of <tt class="docutils literal">plac</tt> there is the <tt class="docutils literal">call</tt> function which invokes
-a callable with the list of arguments passed at the command-line
-(<tt class="docutils literal">sys.argv[1:]</tt>). Thanks to <tt class="docutils literal">plac.call</tt> you can launch your module
-by simply adding the lines:</p>
-<pre class="literal-block">
-if __name__ == '__main__':
- plac.call(main)
-</pre>
-<p>Everything works fine if <tt class="docutils literal">main</tt> is a simple callable performing some
-action; however, in many cases, one has a <tt class="docutils literal">main</tt> &quot;function&quot; which
-is a actually a factory returning a command container object. For
-instance, in my second shelve example the main function is the class
-<tt class="docutils literal">ShelveInterface</tt>, and the two lines needed to run the module are
-a bit ugly:</p>
-<pre class="literal-block">
-if __name__ == '__main__':
- plac.Interpreter(plac.call(ShelveInterface)).interact()
-</pre>
-<p>Moreover, now the program runs, but only in interactive mode, i.e.
-it is not possible to run it as a script. Instead, it would be nice
-to be able to specify the command to execute on the command-line
-and have the interpreter start, execute the command and finish
-properly (I mean by calling <tt class="docutils literal">__enter__</tt> and <tt class="docutils literal">__exit__</tt>)
-without needing user input. Then the script could be called from
-a batch shell script working in the background.
-In order to provide such functionality <tt class="docutils literal">plac.Interpreter</tt> provides
-a classmethod named <tt class="docutils literal">.call</tt> which takes the factory, instantiates
-it with the arguments read from the command line, wraps the resulting
-container object as an interpreter and runs it with the rest arguments
-found in the command line. Here is the code to turn the <tt class="docutils literal">ShelveInterface</tt>
-into a script</p>
-<pre class="literal-block">
-# ishelve3.py
-from ishelve2 import ShelveInterface as main
-
-if __name__ == '__main__':
- import plac; plac.Interpreter.call(main)
-
-</pre>
-<p>and here are a few examples of usage:</p>
-<pre class="literal-block">
-$ python ishelve3.py -h
-usage: ishelve3.py [-h] [-i] [-configfile CONFIGFILE] [args [args ...]]
-
-positional arguments:
- args
-
-optional arguments:
- -h, --help show this help message and exit
- -i, --interact start interactive interpreter
- -configfile CONFIGFILE
- path name of the shelve
-
-$ python ishelve3.py set a 1
-setting a=1
-$ python ishelve3.py show a
-a = 1
-</pre>
-<p>If you pass the <tt class="docutils literal"><span class="pre">-i</span></tt> flag in the command line, then the
-script will enter in interactive mode and ask the user
-for the commands to execute:</p>
-<pre class="literal-block">
-$ python ishelve3.py -i
-A minimal interface over a shelve object.
-Operating on /home/micheles/conf.shelve.
-Use help to see the available commands.
-
-i&gt;
-</pre>
-<p>In a sense, I have closed the circle: at the beginning of this
-document I discussed how to turn a script into an interactive
-application (the <tt class="docutils literal">shelve_interpreter.py</tt> example), whereas here I
-have show how to turn an interactive application into a script.</p>
-<p>The complete signature of <tt class="docutils literal">plac.Interpreter.call</tt> is the following:</p>
-<pre class="literal-block">
-call(factory, arglist=sys.argv[1:],
- commentchar='#', split=shlex.split,
- stdin=sys.stdin, prompt='i&gt; ', verbose=False)
-</pre>
-<p>The factory must have a fixed number of positional arguments (no
-default arguments, no varargs, no kwargs), otherwise a <tt class="docutils literal">TypeError</tt>
-is raised: the reason is that we want to be able to distinguish the
-command-line arguments needed to instantiate the factory from the rest
-arguments that must be sent to the corresponding interpreter object.
-It is also possible to specify a list of arguments different from
-<tt class="docutils literal">sys.argv[1:]</tt> (useful in tests), the character to be recognized as
-a comment, the splitting function, the input source and the prompt to
-use while in interactive mode, and a verbose flag.</p>
-</div>
-<div class="section" id="readline-support">
-<h2><a class="toc-backref" href="#id38">Readline support</a></h2>
-<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 sensitivity,
-the way to go is to pass a <tt class="docutils literal">plac.ReadlineInput</tt> object to the
-interpreter. Here is an example, assuming you want to build a
-database interface understanding SQL commands:</p>
-<pre class="literal-block">
-import os, plac
-from sqlalchemy.ext.sqlsoup import SqlSoup
-
-SQLKEYWORDS = set(['help', '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, argstring):
- sql = 'SELECT ' + argstring
- for row in self.soup.bind.execute(sql):
- yield str(row) # the formatting can be much improved
-
-rl_input = plac.ReadlineInput(
- COMPLETIONS, histfile=os.path.expanduser('~/.sql_interface.history'),
- case_sensitive=False)
-
-def split_on_first_space(line, commentchar):
- return line.strip().split(' ', 1) # ignoring comments
-
-if __name__ == '__main__':
- plac.Interpreter.call(SqlInterface, split=split_on_first_space,
- stdin=rl_input, prompt='sql&gt; ')
-
-</pre>
-<p>Here is an example of usage:</p>
-<pre class="literal-block">
-$ 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 <a class="reference external" href="http://ipython.scipy.org/moin/PyReadline/Intro">pyreadline</a>
-library (I do not use Windows, so this part is very little tested: I
-tried it only once and it worked, but your mileage may vary).
-For people worried about licenses, I will notice that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses the
-readline library only if available, it does not include it and it does
-not rely on it in any fundamental way, so that the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> licence does
-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
-
-set name value
-
-positional arguments:
- name
- value
-
-i&gt; help delete
-usage: delete [name]
-
-delete given parameter (or everything)
-
-positional arguments:
- name [None]
-
-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 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>
-</div>
-<div class="section" id="the-plac-runner">
-<h2><a class="toc-backref" href="#id39">The plac runner</a></h2>
-<p>The distribution of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> includes a runner script named <tt class="docutils literal">plac_runner.py</tt>,
-which will be installed in a suitable directory in your system by <a class="reference external" href="http://docs.python.org/distutils/">distutils</a>
-(say in <tt class="docutils literal">\usr\local\bin\plac_runner.py</tt> in a Unix-like operative system).
-The runner provides many facilities to run <tt class="docutils literal">.plac</tt> scripts and
-<tt class="docutils literal">.placet</tt> files, as well as Python modules containg a <tt class="docutils literal">main</tt>
-object, which can be a function, a command container object or
-even a command container class.</p>
-<p>For instance, suppose you want to execute a script containing commands
-defined in the <tt class="docutils literal">ishelve2</tt> module like the following one:</p>
-<pre class="literal-block">
-#!ishelve2.py:ShelveInterface -c ~/conf.shelve
-set a 1
-del a
-del a # intentional error
-
-</pre>
-<p>The first line of the <tt class="docutils literal">.plac</tt> script contains the name of the
-python module containing the plac interpreter and the arguments
-which must be passed to its main function in order to be able
-to instantiate an interpreter object. In this case I appended
-<tt class="docutils literal">:ShelveInterface</tt> to the name of the module to specify the
-object that must be imported: if not specified, by default the
-object named 'main' is imported.
-The other lines contains commands.
-You can run the script as follows:</p>
-<pre class="literal-block">
-$ plac_runner.py --batch ishelve2.plac
-setting a=1
-deleting a
-Traceback (most recent call last):
- ...
-_bsddb.DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
-</pre>
-<p>The last command intentionally contained an error, to show that the
-plac runner does not eat the traceback.</p>
-<p>The runner can also be used to run Python modules in interactive
-mode and non-interactive mode. If you put this alias in your bashrc</p>
-<blockquote>
-<tt class="docutils literal">alias <span class="pre">plac=&quot;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:ShelveInterface
-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
-setting a=1
-i&gt; set b 2
-setting b=2
-i&gt; show b
-b = 2
-</pre>
-<p>Now you can cut and paste the interactive session an turns into into
-a <tt class="docutils literal">.placet</tt> file like the following:</p>
-<pre class="literal-block">
-#!ishelve2.py:ShelveInterface -configfile=~/test.shelve
-i&gt; del
-deleting everything
-i&gt; set a 1
-setting a=1
-i&gt; set b 2
-setting b=2
-i&gt; show a
-a = 1
-
-</pre>
-<p>Notice that the first line specifies a test database
-<tt class="docutils literal">~/test.shelve</tt>, to avoid clobbering your default shelve. If you
-mispell the arguments in the first line plac will give you an
-<a class="reference external" href="http://argparse.googlecode.com">argparse</a> error message (just try).</p>
-<p>You can run placets following the shebang convention directly with
-the plac runner:</p>
-<pre class="literal-block">
-$ plac --test ishelve2.placet
-run 1 plac test(s)
-</pre>
-<p>If you want to see the output of the tests, pass the <tt class="docutils literal"><span class="pre">-v/--verbose</span></tt> flag.
-Notice that he runner ignore the extension, so you can actually use any
-extension your like, but <em>it relies on the first line of the file to invoke
-the corresponding plac tool with the given arguments</em>.</p>
-<p>The plac runner does not provide any test discovery facility,
-but you can use standard Unix tools to help. For instance, you can
-run all the <tt class="docutils literal">.placet</tt> files into a directory and its subdirectories
-as follows:</p>
-<pre class="literal-block">
-$ find . -name \*.placet | xargs plac_runner.py -t
-</pre>
-<p>The plac runner expects the main function of your script to
-return a plac tool, i.e. a function or an object with a <tt class="docutils literal">.commands</tt>
-attribute. It this is not the case the runner gracefully exits.</p>
-<p>It also works in non-interactive mode, if you call it as</p>
-<blockquote>
-<tt class="docutils literal">$ plac module.py args ...</tt></blockquote>
-<p>Here is an example:</p>
-<pre class="literal-block">
-$ plac ishelve.py a=1
-setting a=1
-$ plac ishelve.py .show
-a=1
-</pre>
-<p>Notice that in non-interactive mode the runner just invokes <tt class="docutils literal">plac.call</tt>
-on the <tt class="docutils literal">main</tt> object of the Python module.</p>
-</div>
-<div class="section" id="a-non-class-based-example">
-<h2><a class="toc-backref" href="#id40">A non class-based example</a></h2>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not force you to use classes to define command containers.
-Even a simple function can be a valid command container, it is
-enough to add to it a <tt class="docutils literal">.commands</tt> attribute and possibly
-<tt class="docutils literal">__enter__</tt> and/or <tt class="docutils literal">__exit__</tt> attributes.</p>
-<p>In particular, a Python module is a perfect container of commands. As an
-example, consider the following module implementing a fake Version
-Control System:</p>
-<pre class="literal-block">
-&quot;A Fake Version Control System&quot;
-
-import plac
-
-commands = 'checkout', 'commit', 'status'
-
-&#64;plac.annotations(url='url of the source code')
-def checkout(url):
- &quot;A fake checkout command&quot;
- return ('checkout ', url)
-
-&#64;plac.annotations(message=('commit message', 'option'))
-def commit(message):
- &quot;A fake commit command&quot;
- return ('commit ', message)
-
-&#64;plac.annotations(quiet=('summary information', 'flag', 'q'))
-def status(quiet):
- &quot;A fake status command&quot;
- return ('status ', quiet)
-
-def __missing__(name):
- return 'Command %r does not exist' % name
-
-def __exit__(etype, exc, tb):
- &quot;Will be called automatically at the end of the call/cmdloop&quot;
- if etype in (None, GeneratorExit): # success
- print('ok')
-
-main = __import__(__name__) # the module imports itself!
-
-</pre>
-<p>Notice that I have defined both an <tt class="docutils literal">__exit__</tt> hook and a <tt class="docutils literal">__missing__</tt>
-hook, invoked for non-existing commands.
-The real trick here is the line <tt class="docutils literal">main = __import__(__name__)</tt>, which
-define <tt class="docutils literal">main</tt> to be an alias for the current module.</p>
-<p>The <tt class="docutils literal">vcs</tt> module does not contain an <tt class="docutils literal">if __name__ == '__main__'</tt>
-block, but you can still run it through the plac runner
-(try <tt class="docutils literal">plac vcs.py <span class="pre">-h</span></tt>):</p>
-<pre class="literal-block">
-usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
-
-A Fake Version Control System
-
-optional arguments:
- -h, --help show this help message and exit
-
-subcommands:
- {status,commit,checkout}
- checkout A fake checkout command
- commit A fake commit command
- status A fake status command
-
-</pre>
-<p>You can get help for the subcommands by postponing <tt class="docutils literal"><span class="pre">-h</span></tt> after the
-name of the command:</p>
-<pre class="literal-block">
-$ plac vcs.py status -h
-usage: vcs.py status [-h] [-q]
-
-A fake status command
-
-optional arguments:
- -h, --help show this help message and exit
- -q, --quiet summary information
-</pre>
-<p>Notice how the docstring of the command is automatically shown in
-usage message, as well as the documentation for the sub flag <tt class="docutils literal"><span class="pre">-q</span></tt>.</p>
-<p>Here is an example of a non-interactive session:</p>
-<pre class="literal-block">
-$ plac vcs.py check url
-checkout
-url
-$ plac vcs.py st -q
-status
-True
-$ plac vcs.py co
-commit
-None
-</pre>
-<p>and here is an interactive session:</p>
-<pre class="literal-block">
-$ plac -i vcs.py
-usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
-i&gt; check url
-checkout
-url
-i&gt; st -q
-status
-True
-i&gt; co
-commit
-None
-i&gt; sto
-Command 'sto' does not exist
-i&gt; [CTRL-D]
-ok
-</pre>
-<p>Notice the invocation of the <tt class="docutils literal">__missing__</tt> hook for non-existing commands.
-Notice also that the <tt class="docutils literal">__exit__</tt> hook gets called only in interactive
-mode.</p>
-<p>If the commands are completely independent, a module is a good fit for
-a method container. In other situations, it is best to use a custom
-class.</p>
-</div>
-<div class="section" id="writing-your-own-plac-runner">
-<h2><a class="toc-backref" href="#id41">Writing your own plac runner</a></h2>
-<p>The runner included in the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> distribution is intentionally kept
-small (around 50 lines of code) so that you can study it and write
-your own runner if want to. If you need to go to such level
-of detail, you should know that the most important method of
-the <tt class="docutils literal">Interpreter</tt> class is the <tt class="docutils literal">.send</tt> method, which takes
-strings in input and returns a four-tuple with attributes
-<tt class="docutils literal">.str</tt>, <tt class="docutils literal">.etype</tt>, <tt class="docutils literal">.exc</tt> and <tt class="docutils literal">.tb</tt>:</p>
-<ul class="simple">
-<li><tt class="docutils literal">.str</tt> is the output of the command, if successful (a string);</li>
-<li><tt class="docutils literal">.etype</tt> is the class of the exception, if the command fail;</li>
-<li><tt class="docutils literal">.exc</tt> is the exception instance;</li>
-<li><tt class="docutils literal">.tb</tt> is the traceback.</li>
-</ul>
-<p>Moreover the <tt class="docutils literal">__str__</tt> representation of the output object is redefined
-to return the output string if the command was successful or the error
-message if the command failed (actually it returns the error message
-preceded by the name of the exception class).</p>
-<p>For instance, if you send a mispelled option to
-the interpreter a <tt class="docutils literal">SystemExit</tt> will be trapped:</p>
-<pre class="doctest-block">
-&gt;&gt;&gt; import plac
-&gt;&gt;&gt; from ishelve import ishelve
-&gt;&gt;&gt; with plac.Interpreter(ishelve) as i:
-... print(i.send('.cler'))
-...
-SystemExit: unrecognized arguments: .cler
-</pre>
-<p>It is important to invoke the <tt class="docutils literal">.send</tt> method inside the context manager,
-otherwise you will get a <tt class="docutils literal">RuntimeError</tt>.</p>
-<p>For instance, suppose you want to implement a graphical runner for a
-plac-based interpreter with two text widgets: one to enter the commands
-and one to display the results. Suppose you want to display the errors
-with tracebacks in red. You will need to code something like that
-(pseudocode follows):</p>
-<pre class="literal-block">
-input_widget = WidgetReadingInput()
-output_widget = WidgetDisplayingOutput()
-
-def send(interpreter, line):
- out = interpreter.send(line)
- if out.tb: # there was an error
- output_widget.display(out.tb, color='red')
- else:
- output_widget.display(out.str)
-
-main = plac.import_main(tool_path) # get the main object
-
-with plac.Interpreter(main) as i:
- def callback(event):
- if event.user_pressed_ENTER():
- send(i, input_widget.last_line)
- input_widget.addcallback(callback)
- gui_mainloop.start()
-</pre>
-<p>You can adapt the pseudocode to your GUI toolkit of choice and you can
-also change the file associations in such a way that clicking on a
-plac tool file the graphical user interface starts.</p>
-<p>An example of GUI program built on top of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is given later on, in the
-paragraph <em>Managing the output of concurrent commands</em> (using Tkinter
-for simplicity and portability).</p>
-<p>There is a final <em>caveat</em>: since the plac interpreter loop is
-implemented via extended generators, plac interpreters are single threaded: you
-will get an error if you <tt class="docutils literal">.send</tt> commands from separated threads.
-You can circumvent the problem by using a queue. If EXIT is a sentinel
-value to signal exiting from the interpreter look, you can write code
-like this:</p>
-<pre class="literal-block">
-with interpreter:
- for input_value in iter(input_queue.get, EXIT):
- output_queue.put(interpreter.send(input_value))
-</pre>
-<p>The same trick also work for processes; you could run the interpreter
-loop in a separate process and send commands to it via the Queue
-class provided by the <a class="reference external" href="http://docs.python.org/library/multiprocessing.html">multiprocessing</a> module.</p>
-</div>
-<div class="section" id="long-running-commands">
-<h2><a class="toc-backref" href="#id42">Long running commands</a></h2>
-<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.call(FakeImporter)
-
-</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 -i
-A fake importer with an import_file command
-i&gt; import_file file1
-... &lt;wait 3+ minutes&gt;
-Imported 100 lines
-Imported 200 lines
-Imported 300 lines
-...
-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">
-<h2><a class="toc-backref" href="#id43">Threaded commands</a></h2>
-<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>It is possible to store the output of a task into a file, to be read
-later (this is useful for tasks with a large output):</p>
-<pre class="literal-block">
-i&gt; .output 1 /tmp/out.txt
-saved output of 1 into /tmp/out.txt
-</pre>
-<p>You can even skip the number argument: then <tt class="docutils literal">.output</tt> will the return
-the output of the last launched command (the special commands like .output
-do not count).</p>
-<p>You can launch many tasks one after the other:</p>
-<pre class="literal-block">
-i&gt; import_file file2
-&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.call(FakeImporter)
-
-</pre>
-</div>
-<div class="section" id="running-commands-as-external-processes">
-<h2><a class="toc-backref" href="#id44">Running commands as external processes</a></h2>
-<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="managing-the-output-of-concurrent-commands">
-<h2><a class="toc-backref" href="#id45">Managing the output of concurrent commands</a></h2>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> acts as a command-line task launcher and can be used as the base
-to build a GUI-based task launcher and task monitor. To this aim the
-interpreter class provides a <tt class="docutils literal">.submit</tt> method which returns a task
-object and a <tt class="docutils literal">.tasks</tt> method returning the list of all the tasks
-submitted to the interpreter. The <tt class="docutils literal">submit</tt> method does not start the task
-and thus it is nonblocking.
-Each task has an <tt class="docutils literal">.outlist</tt> attribute which is a list
-storing the value yielded by the generator underlying the task (the
-<tt class="docutils literal">None</tt> values are skipped though): the <tt class="docutils literal">.outlist</tt> grows as the
-task runs and more values are yielded. Accessing the <tt class="docutils literal">.outlist</tt> is
-nonblocking and can be done freely.
-Finally there is a <tt class="docutils literal">.result</tt>
-property which waits for the task to finish and returns the last yielded
-value or raises an exception. The code below provides an example of
-how you could implement a GUI over the importer example:</p>
-<pre class="literal-block">
-from __future__ import with_statement
-from Tkinter import *
-from importer3 import FakeImporter
-
-def taskwidget(root, task, tick=500):
- &quot;A Label widget showing the output of a task every 500 ms&quot;
- sv = StringVar(root)
- lb = Label(root, textvariable=sv)
- def show_outlist():
- try:
- out = task.outlist[-1]
- except IndexError: # no output yet
- out = ''
- sv.set('%s %s' % (task, out))
- root.after(tick, show_outlist)
- root.after(0, show_outlist)
- return lb
-
-def monitor(tasks):
- root = Tk()
- for task in tasks:
- task.run()
- taskwidget(root, task).pack()
- root.mainloop()
-
-if __name__ == '__main__':
- import plac
- with plac.Interpreter(plac.call(FakeImporter)) as i:
- tasks = [i.submit('import_file f1'), i.submit('import_file f2')]
- monitor(tasks)
-
-</pre>
-</div>
-<div class="section" id="monitor-support">
-<h2><a class="toc-backref" href="#id46">Monitor support</a></h2>
-<p>Starting from release 0.8 <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides builtin support for monitoring the
-output of concurrent commands, at least for platforms where multiprocessing
-is fully supported. You can define your own monitor
-class, simply by inheriting from <tt class="docutils literal">plac.Monitor</tt> and by
-overriding the methods <tt class="docutils literal">add_listener(self, no)</tt>,
-<tt class="docutils literal">del_listener(self, taskno)</tt>, <tt class="docutils literal">notify_listener(self, taskno, msg)</tt>,
-<tt class="docutils literal">schedule(self, seconds, func, arg)</tt> and <tt class="docutils literal">run(self)</tt>.
-Then, you can a monitor object to any <tt class="docutils literal">plac.Interpreter</tt> object
-by simply calling the <tt class="docutils literal">add_monitor</tt> method.
-For convenience,
-<tt class="docutils literal">plac</tt> comes with a very simple <tt class="docutils literal">TkMonitor</tt> based on Tkinter
-(I chose Tkinter because it is easy to use and it is
-in the standard library, but you can use any GUI): you can just
-look at how the <tt class="docutils literal">TkMonitor</tt> is implemented in <tt class="docutils literal">plac_tk.py</tt>
-and adapt it. Here is an example of usage of the <tt class="docutils literal">TkMonitor</tt>:</p>
-<pre class="literal-block">
-from __future__ import with_statement
-import plac
-
-class Hello(object):
- mpcommands = ['hello']
- def hello(self):
- yield 'hello'
-
-if __name__ == '__main__':
- i = plac.Interpreter(Hello())
- i.add_monitor(plac.TkMonitor('tkmon'))
- with i:
- i.interact()
-
-
-</pre>
-<p>Try to give the <tt class="docutils literal">hello</tt> command from the interactive interpreter:
-each time a new text widget will be added displaying the output
-of the command. Notice that if <tt class="docutils literal">Tkinter</tt> is not installed correctly
-on your system the <tt class="docutils literal">TkMonitor</tt> class will not be available.</p>
-</div>
-<div class="section" id="parallel-computing-with-plac">
-<h2><a class="toc-backref" href="#id47">Parallel computing with plac</a></h2>
-<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is certainly not intended as a tool for parallel computing, but
-still you can use it to launch a set of commands and to collect the
-results, similarly to the MapReduce pattern popularized by
-Google. In order to give an example, I will consider the &quot;Hello
-World&quot; of parallel computing, i.e. the computation of pi with
-independent processes. There is a huge number of algorithms to
-compute pi; here I will describe a trivial one chosen for simplicity,
-not per efficienty. The trick is to consider the first quadrant of a
-circle with radius 1 and to extract a number of points <tt class="docutils literal">(x, y)</tt> with
-<tt class="docutils literal">x</tt> and <tt class="docutils literal">y</tt> random variables in the interval <tt class="docutils literal">[0,1]</tt>. The
-probability of extracting a number inside the quadrant (i.e. with
-<tt class="docutils literal">x^2 + y^2 &lt; 1</tt>) is proportional to the area of the quadrant
-(i.e. <tt class="docutils literal">pi/4</tt>). The value of <tt class="docutils literal">pi</tt> therefore can be extracted by
-multiplying by 4 the ratio between the number of points in the
-quadrant versus the total number of points <tt class="docutils literal">N</tt>, for <tt class="docutils literal">N</tt> large:</p>
-<pre class="literal-block">
-def calc_pi(N):
- inside = 0
- for j in xrange(N):
- x, y = random(), random()
- if x*x + y*y &lt; 1:
- inside += 1
- return (4.0 * inside) / N
-</pre>
-<p>The algorithm is trivially parallelizable: if you have n CPUs, you can
-compute pi n times with N/n iterations, sum the results and divide the total
-by n. I have a Macbook with two cores, therefore I would expect a speedup
-factor of 2 with respect to a sequential computation. Moreover, I would
-expect a threaded computation to be even slower than a sequential
-computation, due to the GIL and the scheduling overhead.</p>
-<p>Here is a script implementing the algorithm and working in three different
-modes (parallel mode, threaded mode and sequential mode) depending on a
-<tt class="docutils literal">mode</tt> option:</p>
-<pre class="literal-block">
-from __future__ import with_statement
-from random import random
-import multiprocessing
-import plac
-
-class PiCalculator(object):
- &quot;&quot;&quot;Compute pi in parallel with threads or processes&quot;&quot;&quot;
-
- &#64;plac.annotations(
- npoints=('number of integration points', 'positional', None, int),
- mode=('sequential|parallel|threaded', 'option', 'm', str, 'SPT'))
- def __init__(self, npoints, mode='S'):
- self.npoints = npoints
- if mode == 'P':
- self.mpcommands = ['calc_pi']
- elif mode == 'T':
- self.thcommands = ['calc_pi']
- elif mode == 'S':
- self.commands = ['calc_pi']
- self.n_cpu = multiprocessing.cpu_count()
-
- def submit_tasks(self):
- self.i = plac.Interpreter(self).__enter__()
- return [self.i.submit('calc_pi %d' % (self.npoints / self.n_cpu))
- for _ in range(self.n_cpu)]
-
- def close(self):
- self.i.close()
-
- &#64;plac.annotations(
- npoints=('npoints', 'positional', None, int))
- def calc_pi(self, npoints):
- counts = 0
- for j in xrange(npoints):
- n, r = divmod(j, 1000000)
- if r == 0:
- yield '%dM iterations' % n
- x, y = random(), random()
- if x*x + y*y &lt; 1:
- counts += 1
- yield (4.0 * counts)/npoints
-
- def run(self):
- tasks = self.i.tasks()
- for t in tasks:
- t.run()
- try:
- total = 0
- for task in tasks:
- total += task.result
- except: # the task was killed
- print tasks
- return
- return total / self.n_cpu
-
-if __name__ == '__main__':
- pc = plac.call(PiCalculator)
- pc.submit_tasks()
- try:
- import time; t0 = time.time()
- print '%f in %f seconds ' % (pc.run(), time.time() - t0)
- finally:
- pc.close()
-
-</pre>
-<p>Notice the <tt class="docutils literal">submit_tasks</tt> method, which instantiates and initializes a
-<tt class="docutils literal">plac.Interpreter</tt> object and submits a number of commands corresponding
-to the number of available CPUs. The <tt class="docutils literal">calc_pi</tt> command yield a log
-message every million of interactions, just to monitor the progress of
-the computation. The <tt class="docutils literal">run</tt> method starts all the submitted commands
-in parallel and sums the results. It returns the average value of <tt class="docutils literal">pi</tt>
-after the slowest CPU has finished its job (if the CPUs are equal and
-equally busy they should finish more or less at the same time).</p>
-<p>Here are the results on my old Macbook with Ubuntu 10.04 and Python 2.6,
-for 10 million of iterations:</p>
-<pre class="literal-block">
-$ python picalculator.py -mP 10000000 # two processes
-3.141904 in 5.744545 seconds
-$ python picalculator.py -mT 10000000 # two threads
-3.141272 in 13.875645 seconds
-$ python picalculator.py -mS 10000000 # sequential
-3.141586 in 11.353841 seconds
-</pre>
-<p>As you see using processes one gets a 2x speedup indeed, where the threaded
-mode is some 20% slower than the sequential mode.</p>
-<p>Since the pattern submit a bunch of tasks, starts them and collect the
-results is so common, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides an utility function
-<tt class="docutils literal">runp(genseq, <span class="pre">mode='p',</span> <span class="pre">monitors=(),</span> start=True)</tt> to start
-a bunch a generators and return a list of task objects. By default
-<tt class="docutils literal">runp</tt> use processes, but you can use threads by passing <tt class="docutils literal"><span class="pre">mode='t'</span></tt>.
-If you do not wont to start the tasks, you can say so (<tt class="docutils literal">start=False</tt>).
-With <tt class="docutils literal">runp</tt> the parallel pi calculation becomes a one-liner:</p>
-<pre class="literal-block">
-sum(task.result for task in plac.runp(calc_pi(N) for i in range(ncpus)))/ncpus
-</pre>
-<p>The file <tt class="docutils literal">test_runp</tt> in the <tt class="docutils literal">doc</tt> directory of the plac distribution
-shows another couple of examples of usage, including how to show the
-results of the running computation on a <tt class="docutils literal">TkMonitor</tt>.</p>
-</div>
-<div class="section" id="the-plac-server">
-<h2><a class="toc-backref" href="#id48">The plac server</a></h2>
-<p>A command-line oriented interface can be easily converted into a
-socket-based interface. Starting from release 0.7 plac features
-a builtin server which is able to accept commands from multiple
-clients and to execute them. The server works by instantiating
-a separate interpreter for each client, so that if a client interpreter
-dies for any reason the other interpreters keep working.
-To avoid external dependencies the server is based on the <tt class="docutils literal">asynchat</tt>
-module in the standard library, but it would not be difficult to
-replace the server with a different one (for instance, a Twisted server).
-Since <tt class="docutils literal">asynchat</tt>-based servers are asynchronous, any blocking command
-in the interpreter should be run in a separated process or thread.
-The default port for the <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> server is 2199, and the command to
-signal end-of-connection is EOF.
-For instance, here is how you could manage remote import on a database
-(say a SQLite db):</p>
-<pre class="literal-block">
-import plac
-from importer2 import FakeImporter
-
-def main(port=2199):
- main = FakeImporter('dsn')
- plac.Interpreter(main).start_server(port)
-
-if __name__ == '__main__':
- plac.call(main)
-
-</pre>
-<p>You can connect to the server with <tt class="docutils literal">telnet</tt> on port 2199, as follows:</p>
-<pre class="literal-block">
-$ telnet localhost 2199
-Trying ::1...
-Trying 127.0.0.1...
-Connected to localhost.
-Escape character is '^]'.
-i&gt; import_file f1
-i&gt; .list
-&lt;ThreadedTask 1 [import_file f1] RUNNING&gt;
-i&gt; .out
-Imported 100 lines
-Imported 200 lines
-i&gt; EOF
-Connection closed by foreign host.
-</pre>
-</div>
-<div class="section" id="summary">
-<h2><a class="toc-backref" href="#id49">Summary</a></h2>
-<p>Once <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> claimed to be the easiest command-line arguments parser
-in the world. Having read this document you may think that it is not
-so easy after all. But it is a false impression. Actually the
-rules are quite simple:</p>
-<ol class="arabic simple">
-<li>if you want to implement a command-line script, use <tt class="docutils literal">plac.call</tt>;</li>
-<li>if you want to implement a command interpreter, use <tt class="docutils literal">plac.Interpreter</tt>:<ul>
-<li>for an interactive interpreter, call the <tt class="docutils literal">.interact</tt> method;</li>
-<li>for an batch interpreter, call the <tt class="docutils literal">.execute</tt> method;</li>
-</ul>
-</li>
-<li>for testing call the <tt class="docutils literal">Interpreter.check</tt> method in the appropriate context
-or use the <tt class="docutils literal">Interpreter.doctest</tt> feature;</li>
-<li>if you need to go at a lower level, you may need to call the
-<tt class="docutils literal">Interpreter.send</tt> method 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>
-<li>the <tt class="docutils literal">.start_server</tt> method starts an asynchronous server on the
-given port number (default 2199)</li>
-</ol>
-<p>Moreover, remember that <tt class="docutils literal">plac_runner.py</tt> is your friend.</p>
-</div>
-<hr class="docutils" />
-<div class="section" id="appendix-custom-annotation-objects">
-<h2><a class="toc-backref" href="#id50">Appendix: custom annotation objects</a></h2>
-<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>
-<p>Advanced users can implement their own annotation objects.
-For instance, here is an example of how you could implement annotations for
-positional arguments:</p>
-<pre class="literal-block">
-# annotations.py
-class Positional(object):
- def __init__(self, help='', type=None, choices=None, metavar=None):
- self.help = help
- self.kind = 'positional'
- self.abbrev = None
- self.type = type
- self.choices = choices
- self.metavar = metavar
-
-</pre>
-<p>You can use such annotations objects as follows:</p>
-<pre class="literal-block">
-# example11.py
-import plac
-from annotations import Positional
-
-&#64;plac.annotations(
- i=Positional(&quot;This is an int&quot;, int),
- n=Positional(&quot;This is a float&quot;, float),
- rest=Positional(&quot;Other arguments&quot;))
-def main(i, n, *rest):
- print(i, n, rest)
-
-if __name__ == '__main__':
- import plac; plac.call(main)
-
-</pre>
-<p>Here is the usage message you get:</p>
-<pre class="literal-block">
-usage: example11.py [-h] i n [rest [rest ...]]
-
-positional arguments:
- i This is an int
- n This is a float
- rest Other arguments
-optional arguments:
- -h, --help show this help message and exit
+The documentation of plac has been <a href="http://plac.googlecode.com/hg/doc/plac.html">moved here</a>.
-</pre>
-<p>You can go on and define <tt class="docutils literal">Option</tt> and <tt class="docutils literal">Flag</tt> classes, if you like.
-Using custom annotation objects you could do advanced things like extracting the
-annotations from a configuration file or from a database, but I expect such
-use cases to be quite rare: the default mechanism should work
-pretty well for most users.</p>
-</div>
-</div>
-</div>
</body>
</html>
diff --git a/plac/doc/plac.pdf b/plac/doc/plac.pdf
deleted file mode 100644
index 527b867..0000000
--- a/plac/doc/plac.pdf
+++ /dev/null
@@ -1,13201 +0,0 @@
-%PDF-1.4
-%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
-% 'BasicFonts': class PDFDictionary
-1 0 obj
-% The standard fonts dictionary
-<< /F1 2 0 R
- /F2 3 0 R
- /F3 7 0 R
- /F4 96 0 R >>
-endobj
-% 'F1': class PDFType1Font
-2 0 obj
-% Font Helvetica
-<< /BaseFont /Helvetica
- /Encoding /WinAnsiEncoding
- /Name /F1
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'F2': class PDFType1Font
-3 0 obj
-% Font Helvetica-Bold
-<< /BaseFont /Helvetica-Bold
- /Encoding /WinAnsiEncoding
- /Name /F2
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Annot.NUMBER1': class PDFDictionary
-4 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (mailto:michele.simionato@gmail.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 153.7323
- 705.7736
- 526.5827
- 717.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER2': class PDFDictionary
-5 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 153.7323
- 675.7736
- 526.5827
- 687.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER3': class PDFDictionary
-6 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://plac.googlecode.com/hg/doc/plac.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 153.7323
- 648.7736
- 526.5827
- 660.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'F3': class PDFType1Font
-7 0 obj
-% Font Courier
-<< /BaseFont /Courier
- /Encoding /WinAnsiEncoding
- /Name /F3
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Page1': class PDFPage
-8 0 obj
-% Page dictionary
-<< /Annots [ 4 0 R
- 5 0 R
- 6 0 R ]
- /Contents 351 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER4': class LinkAnnotation
-9 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 8 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 62.69291
- 723.7736
- 286.0929
- 735.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER5': class LinkAnnotation
-10 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 8 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 527.0227
- 723.7736
- 532.5827
- 735.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER6': class LinkAnnotation
-11 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 97 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 82.69291
- 705.7736
- 223.8629
- 717.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER7': class LinkAnnotation
-12 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 97 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 527.0227
- 705.7736
- 532.5827
- 717.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER8': class LinkAnnotation
-13 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 97 0 R
- /XYZ
- 62.69291
- 411.0236
- 0 ]
- /Rect [ 82.69291
- 687.7736
- 223.2929
- 699.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER9': class LinkAnnotation
-14 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 97 0 R
- /XYZ
- 62.69291
- 411.0236
- 0 ]
- /Rect [ 527.0227
- 687.7736
- 532.5827
- 699.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER10': class LinkAnnotation
-15 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 112 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 82.69291
- 669.7736
- 216.6329
- 681.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER11': class LinkAnnotation
-16 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 112 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 527.0227
- 669.7736
- 532.5827
- 681.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER12': class LinkAnnotation
-17 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 122 0 R
- /XYZ
- 62.69291
- 707.8236
- 0 ]
- /Rect [ 82.69291
- 651.7736
- 257.7529
- 663.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER13': class LinkAnnotation
-18 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 122 0 R
- /XYZ
- 62.69291
- 707.8236
- 0 ]
- /Rect [ 527.0227
- 651.7736
- 532.5827
- 663.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER14': class LinkAnnotation
-19 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 126 0 R
- /XYZ
- 62.69291
- 715.8236
- 0 ]
- /Rect [ 82.69291
- 633.7736
- 157.7129
- 645.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER15': class LinkAnnotation
-20 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 126 0 R
- /XYZ
- 62.69291
- 715.8236
- 0 ]
- /Rect [ 527.0227
- 633.7736
- 532.5827
- 645.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER16': class LinkAnnotation
-21 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 126 0 R
- /XYZ
- 62.69291
- 216.5299
- 0 ]
- /Rect [ 82.69291
- 615.7736
- 194.4129
- 627.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER17': class LinkAnnotation
-22 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 126 0 R
- /XYZ
- 62.69291
- 216.5299
- 0 ]
- /Rect [ 527.0227
- 615.7736
- 532.5827
- 627.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER18': class LinkAnnotation
-23 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 134 0 R
- /XYZ
- 62.69291
- 457.4236
- 0 ]
- /Rect [ 82.69291
- 597.7736
- 144.3829
- 609.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER19': class LinkAnnotation
-24 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 134 0 R
- /XYZ
- 62.69291
- 457.4236
- 0 ]
- /Rect [ 521.4627
- 597.7736
- 532.5827
- 609.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER20': class LinkAnnotation
-25 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 139 0 R
- /XYZ
- 62.69291
- 478.6236
- 0 ]
- /Rect [ 82.69291
- 579.7736
- 166.6029
- 591.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER21': class LinkAnnotation
-26 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 139 0 R
- /XYZ
- 62.69291
- 478.6236
- 0 ]
- /Rect [ 521.4627
- 579.7736
- 532.5827
- 591.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER22': class LinkAnnotation
-27 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 142 0 R
- /XYZ
- 62.69291
- 420.6236
- 0 ]
- /Rect [ 82.69291
- 561.7736
- 171.6129
- 573.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER23': class LinkAnnotation
-28 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 142 0 R
- /XYZ
- 62.69291
- 420.6236
- 0 ]
- /Rect [ 521.4627
- 561.7736
- 532.5827
- 573.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER24': class LinkAnnotation
-29 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 144 0 R
- /XYZ
- 62.69291
- 409.4236
- 0 ]
- /Rect [ 82.69291
- 543.7736
- 228.8629
- 555.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER25': class LinkAnnotation
-30 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 144 0 R
- /XYZ
- 62.69291
- 409.4236
- 0 ]
- /Rect [ 521.4627
- 543.7736
- 532.5827
- 555.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER26': class LinkAnnotation
-31 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 159 0 R
- /XYZ
- 62.69291
- 357.4236
- 0 ]
- /Rect [ 82.69291
- 525.7736
- 156.0529
- 537.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER27': class LinkAnnotation
-32 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 159 0 R
- /XYZ
- 62.69291
- 357.4236
- 0 ]
- /Rect [ 521.4627
- 525.7736
- 532.5827
- 537.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER28': class LinkAnnotation
-33 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 214 0 R
- /XYZ
- 62.69291
- 705.0236
- 0 ]
- /Rect [ 82.69291
- 507.7736
- 204.4129
- 519.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER29': class LinkAnnotation
-34 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 214 0 R
- /XYZ
- 62.69291
- 705.0236
- 0 ]
- /Rect [ 521.4627
- 507.7736
- 532.5827
- 519.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER30': class LinkAnnotation
-35 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 214 0 R
- /XYZ
- 62.69291
- 411.0236
- 0 ]
- /Rect [ 82.69291
- 489.7736
- 128.2729
- 501.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER31': class LinkAnnotation
-36 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 214 0 R
- /XYZ
- 62.69291
- 411.0236
- 0 ]
- /Rect [ 521.4627
- 489.7736
- 532.5827
- 501.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER32': class LinkAnnotation
-37 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 214 0 R
- /XYZ
- 62.69291
- 207.0236
- 0 ]
- /Rect [ 82.69291
- 471.7736
- 228.3129
- 483.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER33': class LinkAnnotation
-38 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 214 0 R
- /XYZ
- 62.69291
- 207.0236
- 0 ]
- /Rect [ 521.4627
- 471.7736
- 532.5827
- 483.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER34': class LinkAnnotation
-39 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 585.0236
- 0 ]
- /Rect [ 62.69291
- 453.7736
- 182.7329
- 465.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER35': class LinkAnnotation
-40 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 585.0236
- 0 ]
- /Rect [ 521.4627
- 453.7736
- 532.5827
- 465.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER36': class LinkAnnotation
-41 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 552.0236
- 0 ]
- /Rect [ 82.69291
- 435.7736
- 134.9429
- 447.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER37': class LinkAnnotation
-42 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 552.0236
- 0 ]
- /Rect [ 521.4627
- 435.7736
- 532.5827
- 447.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER38': class LinkAnnotation
-43 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 330.0236
- 0 ]
- /Rect [ 82.69291
- 417.7736
- 252.7429
- 429.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER39': class LinkAnnotation
-44 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 330.0236
- 0 ]
- /Rect [ 521.4627
- 417.7736
- 532.5827
- 429.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER40': class LinkAnnotation
-45 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 234 0 R
- /XYZ
- 62.69291
- 533.8236
- 0 ]
- /Rect [ 82.69291
- 399.7736
- 195.5229
- 411.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER41': class LinkAnnotation
-46 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 234 0 R
- /XYZ
- 62.69291
- 533.8236
- 0 ]
- /Rect [ 521.4627
- 399.7736
- 532.5827
- 411.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER42': class LinkAnnotation
-47 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 237 0 R
- /XYZ
- 62.69291
- 497.8236
- 0 ]
- /Rect [ 82.69291
- 381.7736
- 149.9429
- 393.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER43': class LinkAnnotation
-48 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 237 0 R
- /XYZ
- 62.69291
- 497.8236
- 0 ]
- /Rect [ 521.4627
- 381.7736
- 532.5827
- 393.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER44': class LinkAnnotation
-49 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 247 0 R
- /XYZ
- 62.69291
- 171.0642
- 0 ]
- /Rect [ 82.69291
- 363.7736
- 161.0529
- 375.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER45': class LinkAnnotation
-50 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 247 0 R
- /XYZ
- 62.69291
- 171.0642
- 0 ]
- /Rect [ 521.4627
- 363.7736
- 532.5827
- 375.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER46': class LinkAnnotation
-51 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 253 0 R
- /XYZ
- 62.69291
- 256.6236
- 0 ]
- /Rect [ 82.69291
- 345.7736
- 210.5129
- 357.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER47': class LinkAnnotation
-52 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 253 0 R
- /XYZ
- 62.69291
- 256.6236
- 0 ]
- /Rect [ 521.4627
- 345.7736
- 532.5827
- 357.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER48': class LinkAnnotation
-53 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 260 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 82.69291
- 327.7736
- 167.7229
- 339.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER49': class LinkAnnotation
-54 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 260 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 521.4627
- 327.7736
- 532.5827
- 339.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER50': class LinkAnnotation
-55 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 262 0 R
- /XYZ
- 62.69291
- 468.6236
- 0 ]
- /Rect [ 82.69291
- 309.7736
- 158.2829
- 321.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER51': class LinkAnnotation
-56 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 262 0 R
- /XYZ
- 62.69291
- 468.6236
- 0 ]
- /Rect [ 521.4627
- 309.7736
- 532.5827
- 321.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER52': class LinkAnnotation
-57 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 279 0 R
- /XYZ
- 62.69291
- 566.6236
- 0 ]
- /Rect [ 82.69291
- 291.7736
- 152.7229
- 303.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER53': class LinkAnnotation
-58 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 279 0 R
- /XYZ
- 62.69291
- 566.6236
- 0 ]
- /Rect [ 521.4627
- 291.7736
- 532.5827
- 303.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER54': class LinkAnnotation
-59 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 283 0 R
- /XYZ
- 62.69291
- 729.0236
- 0 ]
- /Rect [ 82.69291
- 273.7736
- 205.5229
- 285.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER55': class LinkAnnotation
-60 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 283 0 R
- /XYZ
- 62.69291
- 729.0236
- 0 ]
- /Rect [ 521.4627
- 273.7736
- 532.5827
- 285.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER56': class LinkAnnotation
-61 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 286 0 R
- /XYZ
- 62.69291
- 729.0236
- 0 ]
- /Rect [ 82.69291
- 255.7736
- 209.9529
- 267.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER57': class LinkAnnotation
-62 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 286 0 R
- /XYZ
- 62.69291
- 729.0236
- 0 ]
- /Rect [ 521.4627
- 255.7736
- 532.5827
- 267.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER58': class LinkAnnotation
-63 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 290 0 R
- /XYZ
- 62.69291
- 555.8236
- 0 ]
- /Rect [ 82.69291
- 237.7736
- 192.7429
- 249.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER59': class LinkAnnotation
-64 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 290 0 R
- /XYZ
- 62.69291
- 555.8236
- 0 ]
- /Rect [ 521.4627
- 237.7736
- 532.5827
- 249.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER60': class LinkAnnotation
-65 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 293 0 R
- /XYZ
- 62.69291
- 659.8236
- 0 ]
- /Rect [ 82.69291
- 219.7736
- 177.1729
- 231.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER61': class LinkAnnotation
-66 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 293 0 R
- /XYZ
- 62.69291
- 659.8236
- 0 ]
- /Rect [ 521.4627
- 219.7736
- 532.5827
- 231.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER62': class LinkAnnotation
-67 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 298 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 82.69291
- 201.7736
- 271.6529
- 213.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER63': class LinkAnnotation
-68 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 298 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 521.4627
- 201.7736
- 532.5827
- 213.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER64': class LinkAnnotation
-69 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 298 0 R
- /XYZ
- 62.69291
- 297.8236
- 0 ]
- /Rect [ 82.69291
- 183.7736
- 286.6829
- 195.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER65': class LinkAnnotation
-70 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 298 0 R
- /XYZ
- 62.69291
- 297.8236
- 0 ]
- /Rect [ 521.4627
- 183.7736
- 532.5827
- 195.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER66': class LinkAnnotation
-71 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 300 0 R
- /XYZ
- 62.69291
- 427.8236
- 0 ]
- /Rect [ 82.69291
- 165.7736
- 152.1629
- 177.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER67': class LinkAnnotation
-72 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 300 0 R
- /XYZ
- 62.69291
- 427.8236
- 0 ]
- /Rect [ 521.4627
- 165.7736
- 532.5827
- 177.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER68': class LinkAnnotation
-73 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 302 0 R
- /XYZ
- 62.69291
- 717.0236
- 0 ]
- /Rect [ 82.69291
- 147.7736
- 206.6229
- 159.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER69': class LinkAnnotation
-74 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 302 0 R
- /XYZ
- 62.69291
- 717.0236
- 0 ]
- /Rect [ 521.4627
- 147.7736
- 532.5827
- 159.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER70': class LinkAnnotation
-75 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 306 0 R
- /XYZ
- 62.69291
- 553.2159
- 0 ]
- /Rect [ 82.69291
- 129.7736
- 151.6029
- 141.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER71': class LinkAnnotation
-76 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 306 0 R
- /XYZ
- 62.69291
- 553.2159
- 0 ]
- /Rect [ 521.4627
- 129.7736
- 532.5827
- 141.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER72': class LinkAnnotation
-77 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 309 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 82.69291
- 111.7736
- 125.4729
- 123.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER73': class LinkAnnotation
-78 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 309 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Rect [ 521.4627
- 111.7736
- 532.5827
- 123.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER74': class LinkAnnotation
-79 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 309 0 R
- /XYZ
- 62.69291
- 454.6772
- 0 ]
- /Rect [ 82.69291
- 93.77362
- 246.1129
- 105.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER75': class LinkAnnotation
-80 0 obj
-<< /Border [ 0
- 0
- 0 ]
- /Contents ()
- /Dest [ 309 0 R
- /XYZ
- 62.69291
- 454.6772
- 0 ]
- /Rect [ 521.4627
- 93.77362
- 532.5827
- 105.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page2': class PDFPage
-81 0 obj
-% Page dictionary
-<< /Annots [ 9 0 R
- 10 0 R
- 11 0 R
- 12 0 R
- 13 0 R
- 14 0 R
- 15 0 R
- 16 0 R
- 17 0 R
- 18 0 R
- 19 0 R
- 20 0 R
- 21 0 R
- 22 0 R
- 23 0 R
- 24 0 R
- 25 0 R
- 26 0 R
- 27 0 R
- 28 0 R
- 29 0 R
- 30 0 R
- 31 0 R
- 32 0 R
- 33 0 R
- 34 0 R
- 35 0 R
- 36 0 R
- 37 0 R
- 38 0 R
- 39 0 R
- 40 0 R
- 41 0 R
- 42 0 R
- 43 0 R
- 44 0 R
- 45 0 R
- 46 0 R
- 47 0 R
- 48 0 R
- 49 0 R
- 50 0 R
- 51 0 R
- 52 0 R
- 53 0 R
- 54 0 R
- 55 0 R
- 56 0 R
- 57 0 R
- 58 0 R
- 59 0 R
- 60 0 R
- 61 0 R
- 62 0 R
- 63 0 R
- 64 0 R
- 65 0 R
- 66 0 R
- 67 0 R
- 68 0 R
- 69 0 R
- 70 0 R
- 71 0 R
- 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 ]
- /Contents 352 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER76': class PDFDictionary
-82 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/getopt.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 214.8914
- 717.7736
- 246.5585
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER77': class PDFDictionary
-83 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 346.507
- 717.7736
- 389.2842
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER78': class PDFDictionary
-84 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 493.1227
- 717.7736
- 531.4956
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER79': class PDFDictionary
-85 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 346.384
- 705.7736
- 388.8477
- 717.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER80': class PDFDictionary
-86 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 311.5097
- 663.7736
- 371.5115
- 675.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER81': class PDFDictionary
-87 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 615.7736
- 84.90623
- 627.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER82': class PDFDictionary
-88 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 453.4229
- 603.7736
- 492.8829
- 615.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER83': class PDFDictionary
-89 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 116.9711
- 585.7736
- 139.5794
- 597.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER84': class PDFDictionary
-90 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 277.9887
- 585.7736
- 321.7169
- 597.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER85': class PDFDictionary
-91 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 504.0394
- 573.7736
- 525.3627
- 585.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER86': class PDFDictionary
-92 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 351.0408
- 561.7736
- 390.5008
- 573.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER87': class PDFDictionary
-93 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 247.8817
- 537.7736
- 266.2217
- 549.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER88': class PDFDictionary
-94 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 507.7736
- 85.3538
- 519.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER89': class PDFDictionary
-95 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 124.2211
- 435.7736
- 146.9252
- 447.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'F4': class PDFType1Font
-96 0 obj
-% Font Helvetica-Oblique
-<< /BaseFont /Helvetica-Oblique
- /Encoding /WinAnsiEncoding
- /Name /F4
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Page3': class PDFPage
-97 0 obj
-% Page dictionary
-<< /Annots [ 82 0 R
- 83 0 R
- 84 0 R
- 85 0 R
- 86 0 R
- 87 0 R
- 88 0 R
- 89 0 R
- 90 0 R
- 91 0 R
- 92 0 R
- 93 0 R
- 94 0 R
- 95 0 R ]
- /Contents 353 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER90': class PDFDictionary
-98 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/getopt.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 325.341
- 717.7736
- 356.6198
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER91': class PDFDictionary
-99 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 376.7786
- 717.7736
- 419.1674
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER92': class PDFDictionary
-100 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 365.694
- 705.7736
- 408.8281
- 717.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER93': class PDFDictionary
-101 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 83.82606
- 478.5736
- 106.0692
- 490.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER94': class PDFDictionary
-102 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 243.8829
- 466.5736
- 265.0029
- 478.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER95': class PDFDictionary
-103 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 83.6329
- 329.3736
- 105.6829
- 341.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER96': class PDFDictionary
-104 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 421.9727
- 329.3736
- 465.1427
- 341.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER97': class PDFDictionary
-105 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 107.8707
- 134.9736
- 129.1584
- 146.9736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER98': class PDFDictionary
-106 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 117.7229
- 122.9736
- 138.8429
- 134.9736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page4': class PDFPage
-107 0 obj
-% Page dictionary
-<< /Annots [ 98 0 R
- 99 0 R
- 100 0 R
- 101 0 R
- 102 0 R
- 103 0 R
- 104 0 R
- 105 0 R
- 106 0 R ]
- /Contents 354 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER99': class PDFDictionary
-108 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 321.4303
- 460.5736
- 363.754
- 472.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER100': class PDFDictionary
-109 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 126.0429
- 448.5736
- 147.1629
- 460.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER101': class PDFDictionary
-110 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 162.6654
- 210.1736
- 184.5985
- 222.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER102': class PDFDictionary
-111 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 155.8754
- 198.1736
- 177.1129
- 210.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page5': class PDFPage
-112 0 obj
-% Page dictionary
-<< /Annots [ 108 0 R
- 109 0 R
- 110 0 R
- 111 0 R ]
- /Contents 355 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER103': class PDFDictionary
-113 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 446.1627
- 499.3736
- 464.5027
- 511.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER104': class PDFDictionary
-114 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 469.3736
- 84.53568
- 481.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER105': class PDFDictionary
-115 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 331.3133
- 469.3736
- 383.5633
- 481.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page6': class PDFPage
-116 0 obj
-% Page dictionary
-<< /Annots [ 113 0 R
- 114 0 R
- 115 0 R ]
- /Contents 356 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER106': class PDFDictionary
-117 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 720.5736
- 83.81291
- 732.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER107': class PDFDictionary
-118 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 240.1228
- 624.5736
- 297.7099
- 636.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER108': class PDFDictionary
-119 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 612.5736
- 114.9429
- 624.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER109': class PDFDictionary
-120 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 496.4721
- 612.5736
- 518.6827
- 624.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER110': class PDFDictionary
-121 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 494.1558
- 451.3736
- 515.9027
- 463.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page7': class PDFPage
-122 0 obj
-% Page dictionary
-<< /Annots [ 117 0 R
- 118 0 R
- 119 0 R
- 120 0 R
- 121 0 R ]
- /Contents 357 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page8': class PDFPage
-123 0 obj
-% Page dictionary
-<< /Contents 358 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER111': class PDFDictionary
-124 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 680.5736
- 84.62846
- 692.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER112': class PDFDictionary
-125 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 110.2829
- 157.2799
- 132.8629
- 169.2799 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page9': class PDFPage
-126 0 obj
-% Page dictionary
-<< /Annots [ 124 0 R
- 125 0 R ]
- /Contents 359 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER113': class PDFDictionary
-127 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 392.5829
- 470.1736
- 413.7029
- 482.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER114': class PDFDictionary
-128 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 157.3904
- 422.1736
- 179.9938
- 434.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER115': class PDFDictionary
-129 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 184.0634
- 410.1736
- 223.5234
- 422.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER116': class PDFDictionary
-130 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 398.1736
- 102.1529
- 410.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER117': class PDFDictionary
-131 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 192.7997
- 398.1736
- 237.9391
- 410.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER118': class PDFDictionary
-132 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 324.4372
- 398.1736
- 342.7772
- 410.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER119': class PDFDictionary
-133 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 360.0429
- 170.1736
- 402.2829
- 182.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page10': class PDFPage
-134 0 obj
-% Page dictionary
-<< /Annots [ 127 0 R
- 128 0 R
- 129 0 R
- 130 0 R
- 131 0 R
- 132 0 R
- 133 0 R ]
- /Contents 360 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page11': class PDFPage
-135 0 obj
-% Page dictionary
-<< /Contents 361 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER120': class PDFDictionary
-136 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 338.1568
- 443.3736
- 360.5113
- 455.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER121': class PDFDictionary
-137 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.sqlalchemy.org/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 110.6843
- 431.3736
- 169.0343
- 443.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER122': class PDFDictionary
-138 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 168.3029
- 419.3736
- 208.8829
- 431.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page12': class PDFPage
-139 0 obj
-% Page dictionary
-<< /Annots [ 136 0 R
- 137 0 R
- 138 0 R ]
- /Contents 362 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER123': class PDFDictionary
-140 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 185.0709
- 385.3736
- 208.0228
- 397.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER124': class PDFDictionary
-141 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 220.5998
- 373.3736
- 243.819
- 385.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page13': class PDFPage
-142 0 obj
-% Page dictionary
-<< /Annots [ 140 0 R
- 141 0 R ]
- /Contents 363 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER125': class PDFDictionary
-143 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 374.4929
- 350.1736
- 395.6129
- 362.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page14': class PDFPage
-144 0 obj
-% Page dictionary
-<< /Annots [ 143 0 R ]
- /Contents 364 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER126': class PDFDictionary
-145 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 304.0655
- 312.0481
- 348.3808
- 324.0481 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER127': class PDFDictionary
-146 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 293.7749
- 138.0481
- 316.2402
- 150.0481 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page15': class PDFPage
-147 0 obj
-% Page dictionary
-<< /Annots [ 145 0 R
- 146 0 R ]
- /Contents 365 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER128': class PDFDictionary
-148 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 322.1736
- 84.8789
- 334.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER129': class PDFDictionary
-149 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 466.5307
- 322.1736
- 509.8367
- 334.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER130': class PDFDictionary
-150 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 124.3929
- 298.1736
- 163.8529
- 310.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER131': class PDFDictionary
-151 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 217.1736
- 127.9329
- 229.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER132': class PDFDictionary
-152 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 199.1736
- 107.9337
- 211.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER133': class PDFDictionary
-153 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 308.5389
- 199.1736
- 351.8997
- 211.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER134': class PDFDictionary
-154 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 380.6856
- 175.1736
- 423.7999
- 187.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER135': class PDFDictionary
-155 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 494.4684
- 175.1736
- 516.4627
- 187.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER136': class PDFDictionary
-156 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 145.1736
- 108.3529
- 157.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER137': class PDFDictionary
-157 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 277.2428
- 145.1736
- 321.0228
- 157.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER138': class PDFDictionary
-158 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 404.5839
- 133.1736
- 426.0657
- 145.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page16': class PDFPage
-159 0 obj
-% Page dictionary
-<< /Annots [ 148 0 R
- 149 0 R
- 150 0 R
- 151 0 R
- 152 0 R
- 153 0 R
- 154 0 R
- 155 0 R
- 156 0 R
- 157 0 R
- 158 0 R ]
- /Contents 366 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER139': class PDFDictionary
-160 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 750.7736
- 108.61
- 762.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER140': class PDFDictionary
-161 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 459.2622
- 738.7736
- 481.289
- 750.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER141': class PDFDictionary
-162 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 708.7736
- 108.9242
- 720.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER142': class PDFDictionary
-163 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/doc/in-writing) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 340.9248
- 708.7736
- 470.1087
- 720.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER143': class PDFDictionary
-164 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 678.7736
- 107.9247
- 690.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER144': class PDFDictionary
-165 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 666.7736
- 104.0329
- 678.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER145': class PDFDictionary
-166 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/doc/in-writing) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 489.2227
- 666.7736
- 532.176
- 678.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER146': class PDFDictionary
-167 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/doc/in-writing) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 654.7736
- 159.6229
- 666.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER147': class PDFDictionary
-168 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 639.7736
- 83.81291
- 651.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER148': class PDFDictionary
-169 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 219.4229
- 639.7736
- 261.6629
- 651.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER149': class PDFDictionary
-170 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
- 609.7736
- 534.3667
- 621.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER150': class PDFDictionary
-171 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 325.7268
- 442.5736
- 347.4138
- 454.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER151': class PDFDictionary
-172 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 327.2261
- 275.3736
- 410.5152
- 287.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER152': class PDFDictionary
-173 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 275.5829
- 96.17362
- 317.8229
- 108.1736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page17': class PDFPage
-174 0 obj
-% Page dictionary
-<< /Annots [ 160 0 R
- 161 0 R
- 162 0 R
- 163 0 R
- 164 0 R
- 165 0 R
- 166 0 R
- 167 0 R
- 168 0 R
- 169 0 R
- 170 0 R
- 171 0 R
- 172 0 R
- 173 0 R ]
- /Contents 367 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER153': class PDFDictionary
-175 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 307.9178
- 753.7736
- 351.5999
- 765.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER154': class PDFDictionary
-176 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 329.8034
- 729.7736
- 352.1804
- 741.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER155': class PDFDictionary
-177 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 109.0098
- 669.7736
- 131.9967
- 681.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER156': class PDFDictionary
-178 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 397.2929
- 645.7736
- 415.6329
- 657.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER157': class PDFDictionary
-179 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/opterator) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 624.7736
- 128.4929
- 636.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER158': class PDFDictionary
-180 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/CLIArgs) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 606.7736
- 124.5929
- 618.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER159': class PDFDictionary
-181 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/commandline) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.69291
- 588.7736
- 147.9329
- 600.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER160': class PDFDictionary
-182 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 464.3898
- 573.7736
- 503.8498
- 585.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER161': class PDFDictionary
-183 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 305.0429
- 561.7736
- 323.3829
- 573.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER162': class PDFDictionary
-184 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/Clap/0.7) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 455.0104
- 543.7736
- 479.9015
- 555.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER163': class PDFDictionary
-185 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 303.707
- 531.7736
- 322.047
- 543.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER164': class PDFDictionary
-186 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/Clap/0.7) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 328.8186
- 531.7736
- 353.3701
- 543.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER165': class PDFDictionary
-187 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 519.7736
- 81.03291
- 531.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER166': class PDFDictionary
-188 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 501.7736
- 84.4354
- 513.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER167': class PDFDictionary
-189 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 275.6978
- 501.7736
- 297.9903
- 513.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER168': class PDFDictionary
-190 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://packages.python.org/cmd2/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 203.5285
- 489.7736
- 231.1357
- 501.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER169': class PDFDictionary
-191 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://packages.python.org/cmd2/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 164.4129
- 477.7736
- 191.6429
- 489.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER170': class PDFDictionary
-192 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 302.7929
- 477.7736
- 321.1329
- 489.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER171': class PDFDictionary
-193 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (https://github.com/pulp/marrow.script) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 458.7927
- 459.7736
- 522.0227
- 471.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER172': class PDFDictionary
-194 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 276.5607
- 447.7736
- 297.9876
- 459.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER173': class PDFDictionary
-195 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 427.9754
- 447.7736
- 470.5222
- 459.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER174': class PDFDictionary
-196 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://packages.python.org/argh) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 497.8158
- 447.7736
- 522.0227
- 459.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER175': class PDFDictionary
-197 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 357.7489
- 435.7736
- 397.2089
- 447.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER176': class PDFDictionary
-198 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 462.971
- 435.7736
- 484.6916
- 447.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER177': class PDFDictionary
-199 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 156.6051
- 375.7736
- 177.8606
- 387.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER178': class PDFDictionary
-200 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 186.6535
- 351.7736
- 226.1135
- 363.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER179': class PDFDictionary
-201 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 493.1227
- 351.7736
- 532.4646
- 363.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER180': class PDFDictionary
-202 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 72.7804
- 339.7736
- 96.20788
- 351.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER181': class PDFDictionary
-203 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 149.2229
- 309.7736
- 171.2704
- 321.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER182': class PDFDictionary
-204 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 128.0309
- 267.7736
- 149.4369
- 279.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER183': class PDFDictionary
-205 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 502.8367
- 267.7736
- 524.2427
- 279.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER184': class PDFDictionary
-206 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 187.4797
- 243.7736
- 209.0991
- 255.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER185': class PDFDictionary
-207 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (file:///home/micheles/Dropbox/md/gcodedev/plac/doc/in-writing) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 301.6965
- 243.7736
- 426.0446
- 255.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER186': class PDFDictionary
-208 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 83.6829
- 171.7736
- 105.7829
- 183.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER187': class PDFDictionary
-209 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 446.5627
- 171.7736
- 502.5727
- 183.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER188': class PDFDictionary
-210 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 275.6828
- 159.7736
- 297.3688
- 171.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER189': class PDFDictionary
-211 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 77.19665
- 147.7736
- 139.4904
- 159.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER190': class PDFDictionary
-212 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 96.54131
- 135.7736
- 139.0255
- 147.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER191': class PDFDictionary
-213 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 203.5016
- 102.7736
- 245.8453
- 114.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page18': class PDFPage
-214 0 obj
-% Page dictionary
-<< /Annots [ 175 0 R
- 176 0 R
- 177 0 R
- 178 0 R
- 179 0 R
- 180 0 R
- 181 0 R
- 182 0 R
- 183 0 R
- 184 0 R
- 185 0 R
- 186 0 R
- 187 0 R
- 188 0 R
- 189 0 R
- 190 0 R
- 191 0 R
- 192 0 R
- 193 0 R
- 194 0 R
- 195 0 R
- 196 0 R
- 197 0 R
- 198 0 R
- 199 0 R
- 200 0 R
- 201 0 R
- 202 0 R
- 203 0 R
- 204 0 R
- 205 0 R
- 206 0 R
- 207 0 R
- 208 0 R
- 209 0 R
- 210 0 R
- 211 0 R
- 212 0 R
- 213 0 R ]
- /Contents 368 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER192': class PDFDictionary
-215 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 711.7736
- 138.7898
- 723.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER193': class PDFDictionary
-216 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 114.6649
- 699.7736
- 154.1249
- 711.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER194': class PDFDictionary
-217 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 191.6329
- 687.7736
- 233.8729
- 699.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER195': class PDFDictionary
-218 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/Clap/0.7) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 263.3429
- 657.7736
- 286.6829
- 669.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER196': class PDFDictionary
-219 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 298.1928
- 609.7736
- 316.5328
- 621.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER197': class PDFDictionary
-220 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 185.4471
- 516.7736
- 207.1062
- 528.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER198': class PDFDictionary
-221 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 177.6784
- 504.7736
- 199.6123
- 516.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER199': class PDFDictionary
-222 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 450.7736
- 83.81291
- 462.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER200': class PDFDictionary
-223 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 278.4678
- 432.7736
- 300.0328
- 444.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER201': class PDFDictionary
-224 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 117.3573
- 420.7736
- 138.5845
- 432.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER202': class PDFDictionary
-225 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://twill.idyll.org/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 82.74466
- 378.7736
- 104.4564
- 390.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER203': class PDFDictionary
-226 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 127.2882
- 378.7736
- 145.6282
- 390.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER204': class PDFDictionary
-227 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 410.1674
- 378.7736
- 433.5592
- 390.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER205': class PDFDictionary
-228 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 265.9578
- 354.7736
- 284.2978
- 366.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER206': class PDFDictionary
-229 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 487.7492
- 354.7736
- 506.0892
- 366.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER207': class PDFDictionary
-230 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://plac.googlecode.com/hg/doc/plac.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 198.7736
- 157.1829
- 210.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page19': class PDFPage
-231 0 obj
-% Page dictionary
-<< /Annots [ 215 0 R
- 216 0 R
- 217 0 R
- 218 0 R
- 219 0 R
- 220 0 R
- 221 0 R
- 222 0 R
- 223 0 R
- 224 0 R
- 225 0 R
- 226 0 R
- 227 0 R
- 228 0 R
- 229 0 R
- 230 0 R ]
- /Contents 369 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page20': class PDFPage
-232 0 obj
-% Page dictionary
-<< /Contents 370 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER208': class PDFDictionary
-233 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 383.9329
- 576.5736
- 405.0529
- 588.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page21': class PDFPage
-234 0 obj
-% Page dictionary
-<< /Annots [ 233 0 R ]
- /Contents 371 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER209': class PDFDictionary
-235 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 370.6785
- 199.3736
- 392.4956
- 211.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER210': class PDFDictionary
-236 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 455.8742
- 199.3736
- 477.6913
- 211.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page22': class PDFPage
-237 0 obj
-% Page dictionary
-<< /Annots [ 235 0 R
- 236 0 R ]
- /Contents 372 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER211': class PDFDictionary
-238 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 185.9351
- 624.9893
- 207.4695
- 636.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER212': class PDFDictionary
-239 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/shlex.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 369.8905
- 624.9893
- 396.425
- 636.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER213': class PDFDictionary
-240 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 408.8916
- 612.9893
- 427.2316
- 624.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER214': class PDFDictionary
-241 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/shlex.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 600.9893
- 86.03291
- 612.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER215': class PDFDictionary
-242 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 92.4689
- 600.9893
- 114.4649
- 612.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER216': class PDFDictionary
-243 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/shlex.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 149.3204
- 588.9893
- 176.9472
- 600.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER217': class PDFDictionary
-244 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 129.6923
- 492.9893
- 151.4655
- 504.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER218': class PDFDictionary
-245 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 173.8529
- 480.9893
- 194.9729
- 492.9893 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER219': class PDFDictionary
-246 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 460.388
- 123.8142
- 482.0127
- 135.8142 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page23': class PDFPage
-247 0 obj
-% Page dictionary
-<< /Annots [ 238 0 R
- 239 0 R
- 240 0 R
- 241 0 R
- 242 0 R
- 243 0 R
- 244 0 R
- 245 0 R
- 246 0 R ]
- /Contents 373 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER220': class PDFDictionary
-248 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 95.32996
- 717.7736
- 116.857
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER221': class PDFDictionary
-249 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://plac.googlecode.com/hg/doc/plac.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 316.3528
- 221.3736
- 409.2453
- 233.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER222': class PDFDictionary
-250 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 419.1694
- 209.3736
- 440.4061
- 221.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER223': class PDFDictionary
-251 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 179.3736
- 84.70395
- 191.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER224': class PDFDictionary
-252 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 293.2359
- 101.3736
- 316.4697
- 113.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page24': class PDFPage
-253 0 obj
-% Page dictionary
-<< /Annots [ 248 0 R
- 249 0 R
- 250 0 R
- 251 0 R
- 252 0 R ]
- /Contents 374 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER225': class PDFDictionary
-254 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 431.7904
- 118.5736
- 453.7245
- 130.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page25': class PDFPage
-255 0 obj
-% Page dictionary
-<< /Annots [ 254 0 R ]
- /Contents 375 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER226': class PDFDictionary
-256 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 255.5885
- 753.7736
- 278.2757
- 765.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER227': class PDFDictionary
-257 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 513.6927
- 729.7736
- 532.1846
- 741.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER228': class PDFDictionary
-258 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 323.3
- 226.8179
- 344.4661
- 238.8179 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page26': class PDFPage
-259 0 obj
-% Page dictionary
-<< /Annots [ 256 0 R
- 257 0 R
- 258 0 R ]
- /Contents 376 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page27': class PDFPage
-260 0 obj
-% Page dictionary
-<< /Contents 377 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER229': class PDFDictionary
-261 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 179.0529
- 433.3736
- 201.1953
- 445.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page28': class PDFPage
-262 0 obj
-% Page dictionary
-<< /Annots [ 261 0 R ]
- /Contents 378 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER230': class PDFDictionary
-263 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://freshmeat.net/projects/rlwrap/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 377.8504
- 481.3736
- 409.861
- 493.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER231': class PDFDictionary
-264 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 242.4466
- 469.3736
- 263.7922
- 481.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER232': class PDFDictionary
-265 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://ipython.scipy.org/moin/PyReadline/Intro) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 456.2271
- 469.3736
- 505.3627
- 481.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER233': class PDFDictionary
-266 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 363.1739
- 445.3736
- 386.5004
- 457.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER234': class PDFDictionary
-267 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 479.7508
- 433.3736
- 501.4627
- 445.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER235': class PDFDictionary
-268 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 366.9454
- 403.3736
- 388.8033
- 415.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER236': class PDFDictionary
-269 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/cmd.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 172.4855
- 391.3736
- 191.3755
- 403.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page29': class PDFPage
-270 0 obj
-% Page dictionary
-<< /Annots [ 263 0 R
- 264 0 R
- 265 0 R
- 266 0 R
- 267 0 R
- 268 0 R
- 269 0 R ]
- /Contents 379 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER237': class PDFDictionary
-271 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 390.8027
- 708.5736
- 435.0027
- 720.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER238': class PDFDictionary
-272 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 213.451
- 696.5736
- 237.5255
- 708.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER239': class PDFDictionary
-273 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 114.9429
- 672.5736
- 157.1829
- 684.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER240': class PDFDictionary
-274 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 385.0429
- 672.5736
- 403.3829
- 684.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER241': class PDFDictionary
-275 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 350.6129
- 642.5736
- 371.7329
- 654.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER242': class PDFDictionary
-276 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 256.6729
- 624.5736
- 277.7929
- 636.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER243': class PDFDictionary
-277 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 149.5469
- 531.3736
- 172.1982
- 543.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER244': class PDFDictionary
-278 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/distutils/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 224.3178
- 519.3736
- 260.8853
- 531.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page30': class PDFPage
-279 0 obj
-% Page dictionary
-<< /Annots [ 271 0 R
- 272 0 R
- 273 0 R
- 274 0 R
- 275 0 R
- 276 0 R
- 277 0 R
- 278 0 R ]
- /Contents 380 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER245': class PDFDictionary
-280 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 381.1529
- 415.3736
- 423.3929
- 427.3736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page31': class PDFPage
-281 0 obj
-% Page dictionary
-<< /Annots [ 280 0 R ]
- /Contents 381 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER246': class PDFDictionary
-282 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 693.7736
- 84.72012
- 705.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page32': class PDFPage
-283 0 obj
-% Page dictionary
-<< /Annots [ 282 0 R ]
- /Contents 382 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page33': class PDFPage
-284 0 obj
-% Page dictionary
-<< /Contents 383 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER247': class PDFDictionary
-285 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 182.479
- 693.7736
- 203.7662
- 705.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page34': class PDFPage
-286 0 obj
-% Page dictionary
-<< /Annots [ 285 0 R ]
- /Contents 384 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER248': class PDFDictionary
-287 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 255.1228
- 723.7736
- 276.5028
- 735.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER249': class PDFDictionary
-288 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/multiprocessing.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 295.0229
- 568.5736
- 367.2629
- 580.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER250': class PDFDictionary
-289 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 179.1295
- 520.5736
- 201.6839
- 532.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page35': class PDFPage
-290 0 obj
-% Page dictionary
-<< /Annots [ 287 0 R
- 288 0 R
- 289 0 R ]
- /Contents 385 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER251': class PDFDictionary
-291 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 414.8874
- 684.5736
- 436.9487
- 696.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER252': class PDFDictionary
-292 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 122.7054
- 540.5736
- 145.2085
- 552.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page36': class PDFPage
-293 0 obj
-% Page dictionary
-<< /Annots [ 291 0 R
- 292 0 R ]
- /Contents 386 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page37': class PDFPage
-294 0 obj
-% Page dictionary
-<< /Contents 387 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER253': class PDFDictionary
-295 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 183.3657
- 717.7736
- 207.8364
- 729.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER254': class PDFDictionary
-296 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/multiprocessing.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 254.9929
- 693.7736
- 327.2329
- 705.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER255': class PDFDictionary
-297 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 262.5736
- 85.70846
- 274.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page38': class PDFPage
-298 0 obj
-% Page dictionary
-<< /Annots [ 295 0 R
- 296 0 R
- 297 0 R ]
- /Contents 388 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER256': class PDFDictionary
-299 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 176.9371
- 392.5736
- 198.5507
- 404.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page39': class PDFPage
-300 0 obj
-% Page dictionary
-<< /Annots [ 299 0 R ]
- /Contents 389 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER257': class PDFDictionary
-301 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 681.7736
- 84.98766
- 693.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page40': class PDFPage
-302 0 obj
-% Page dictionary
-<< /Annots [ 301 0 R ]
- /Contents 390 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page41': class PDFPage
-303 0 obj
-% Page dictionary
-<< /Contents 391 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER258': class PDFDictionary
-304 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 473.5049
- 678.5736
- 494.7927
- 690.5736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER259': class PDFDictionary
-305 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 172.4311
- 433.9659
- 194.7087
- 445.9659 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page42': class PDFPage
-306 0 obj
-% Page dictionary
-<< /Annots [ 304 0 R
- 305 0 R ]
- /Contents 392 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER260': class PDFDictionary
-307 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 91.57623
- 729.7736
- 114.8995
- 741.7736 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER261': class PDFDictionary
-308 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 106.6216
- 419.4272
- 128.3202
- 431.4272 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page43': class PDFPage
-309 0 obj
-% Page dictionary
-<< /Annots [ 307 0 R
- 308 0 R ]
- /Contents 393 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page44': class PDFPage
-310 0 obj
-% Page dictionary
-<< /Contents 394 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 350 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'R311': class PDFCatalog
-311 0 obj
-% Document Root
-<< /Outlines 313 0 R
- /PageLabels 395 0 R
- /PageMode /UseNone
- /Pages 350 0 R
- /Type /Catalog >>
-endobj
-% 'R312': class PDFInfo
-312 0 obj
-<< /Author ()
- /CreationDate (D:20110605055423-01'00')
- /Creator (\(unspecified\))
- /Keywords ()
- /Producer (ReportLab PDF Library - www.reportlab.com)
- /Subject (\(unspecified\))
- /Title () >>
-endobj
-% 'R313': class PDFOutlines
-313 0 obj
-<< /Count 38
- /First 314 0 R
- /Last 329 0 R
- /Type /Outlines >>
-endobj
-% 'Outline.0': class OutlineEntryObject
-314 0 obj
-<< /Count 14
- /Dest [ 8 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /First 315 0 R
- /Last 328 0 R
- /Next 329 0 R
- /Parent 313 0 R
- /Title (Plac: Parsing the Command Line the Easy Way) >>
-endobj
-% 'Outline.37.0': class OutlineEntryObject
-315 0 obj
-<< /Dest [ 97 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Next 316 0 R
- /Parent 314 0 R
- /Title (The importance of scaling down) >>
-endobj
-% 'Outline.37.1': class OutlineEntryObject
-316 0 obj
-<< /Dest [ 97 0 R
- /XYZ
- 62.69291
- 411.0236
- 0 ]
- /Next 317 0 R
- /Parent 314 0 R
- /Prev 315 0 R
- /Title (Scripts with required arguments) >>
-endobj
-% 'Outline.37.2': class OutlineEntryObject
-317 0 obj
-<< /Dest [ 112 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Next 318 0 R
- /Parent 314 0 R
- /Prev 316 0 R
- /Title (Scripts with default arguments) >>
-endobj
-% 'Outline.37.3': class OutlineEntryObject
-318 0 obj
-<< /Dest [ 122 0 R
- /XYZ
- 62.69291
- 707.8236
- 0 ]
- /Next 319 0 R
- /Parent 314 0 R
- /Prev 317 0 R
- /Title (Scripts with options \(and smart options\)) >>
-endobj
-% 'Outline.37.4': class OutlineEntryObject
-319 0 obj
-<< /Dest [ 126 0 R
- /XYZ
- 62.69291
- 715.8236
- 0 ]
- /Next 320 0 R
- /Parent 314 0 R
- /Prev 318 0 R
- /Title (Scripts with flags) >>
-endobj
-% 'Outline.37.5': class OutlineEntryObject
-320 0 obj
-<< /Dest [ 126 0 R
- /XYZ
- 62.69291
- 216.5299
- 0 ]
- /Next 321 0 R
- /Parent 314 0 R
- /Prev 319 0 R
- /Title (plac for Python 2.X users) >>
-endobj
-% 'Outline.37.6': class OutlineEntryObject
-321 0 obj
-<< /Dest [ 134 0 R
- /XYZ
- 62.69291
- 457.4236
- 0 ]
- /Next 322 0 R
- /Parent 314 0 R
- /Prev 320 0 R
- /Title (More features) >>
-endobj
-% 'Outline.37.7': class OutlineEntryObject
-322 0 obj
-<< /Dest [ 139 0 R
- /XYZ
- 62.69291
- 478.6236
- 0 ]
- /Next 323 0 R
- /Parent 314 0 R
- /Prev 321 0 R
- /Title (A realistic example) >>
-endobj
-% 'Outline.37.8': class OutlineEntryObject
-323 0 obj
-<< /Dest [ 142 0 R
- /XYZ
- 62.69291
- 420.6236
- 0 ]
- /Next 324 0 R
- /Parent 314 0 R
- /Prev 322 0 R
- /Title (Keyword arguments) >>
-endobj
-% 'Outline.37.9': class OutlineEntryObject
-324 0 obj
-<< /Dest [ 144 0 R
- /XYZ
- 62.69291
- 409.4236
- 0 ]
- /Next 325 0 R
- /Parent 314 0 R
- /Prev 323 0 R
- /Title (Final example: a shelve interface) >>
-endobj
-% 'Outline.37.10': class OutlineEntryObject
-325 0 obj
-<< /Dest [ 159 0 R
- /XYZ
- 62.69291
- 357.4236
- 0 ]
- /Next 326 0 R
- /Parent 314 0 R
- /Prev 324 0 R
- /Title (plac vs argparse) >>
-endobj
-% 'Outline.37.11': class OutlineEntryObject
-326 0 obj
-<< /Dest [ 214 0 R
- /XYZ
- 62.69291
- 705.0236
- 0 ]
- /Next 327 0 R
- /Parent 314 0 R
- /Prev 325 0 R
- /Title (plac vs the rest of the world) >>
-endobj
-% 'Outline.37.12': class OutlineEntryObject
-327 0 obj
-<< /Dest [ 214 0 R
- /XYZ
- 62.69291
- 411.0236
- 0 ]
- /Next 328 0 R
- /Parent 314 0 R
- /Prev 326 0 R
- /Title (The future) >>
-endobj
-% 'Outline.37.13': class OutlineEntryObject
-328 0 obj
-<< /Dest [ 214 0 R
- /XYZ
- 62.69291
- 207.0236
- 0 ]
- /Parent 314 0 R
- /Prev 327 0 R
- /Title (Trivia: the story behind the name) >>
-endobj
-% 'Outline.1': class OutlineEntryObject
-329 0 obj
-<< /Count 20
- /Dest [ 231 0 R
- /XYZ
- 62.69291
- 585.0236
- 0 ]
- /First 330 0 R
- /Last 349 0 R
- /Parent 313 0 R
- /Prev 314 0 R
- /Title (Advanced usages of plac) >>
-endobj
-% 'Outline.38.0': class OutlineEntryObject
-330 0 obj
-<< /Dest [ 231 0 R
- /XYZ
- 62.69291
- 552.0236
- 0 ]
- /Next 331 0 R
- /Parent 329 0 R
- /Title (Introduction) >>
-endobj
-% 'Outline.38.1': class OutlineEntryObject
-331 0 obj
-<< /Dest [ 231 0 R
- /XYZ
- 62.69291
- 330.0236
- 0 ]
- /Next 332 0 R
- /Parent 329 0 R
- /Prev 330 0 R
- /Title (From scripts to interactive applications) >>
-endobj
-% 'Outline.38.2': class OutlineEntryObject
-332 0 obj
-<< /Dest [ 234 0 R
- /XYZ
- 62.69291
- 533.8236
- 0 ]
- /Next 333 0 R
- /Parent 329 0 R
- /Prev 331 0 R
- /Title (Testing a plac application) >>
-endobj
-% 'Outline.38.3': class OutlineEntryObject
-333 0 obj
-<< /Dest [ 237 0 R
- /XYZ
- 62.69291
- 497.8236
- 0 ]
- /Next 334 0 R
- /Parent 329 0 R
- /Prev 332 0 R
- /Title (Plac easy tests) >>
-endobj
-% 'Outline.38.4': class OutlineEntryObject
-334 0 obj
-<< /Dest [ 247 0 R
- /XYZ
- 62.69291
- 171.0642
- 0 ]
- /Next 335 0 R
- /Parent 329 0 R
- /Prev 333 0 R
- /Title (Plac batch scripts) >>
-endobj
-% 'Outline.38.5': class OutlineEntryObject
-335 0 obj
-<< /Dest [ 253 0 R
- /XYZ
- 62.69291
- 256.6236
- 0 ]
- /Next 336 0 R
- /Parent 329 0 R
- /Prev 334 0 R
- /Title (Implementing subcommands) >>
-endobj
-% 'Outline.38.6': class OutlineEntryObject
-336 0 obj
-<< /Dest [ 260 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Next 337 0 R
- /Parent 329 0 R
- /Prev 335 0 R
- /Title (plac.Interpreter.call) >>
-endobj
-% 'Outline.38.7': class OutlineEntryObject
-337 0 obj
-<< /Dest [ 262 0 R
- /XYZ
- 62.69291
- 468.6236
- 0 ]
- /Next 338 0 R
- /Parent 329 0 R
- /Prev 336 0 R
- /Title (Readline support) >>
-endobj
-% 'Outline.38.8': class OutlineEntryObject
-338 0 obj
-<< /Dest [ 279 0 R
- /XYZ
- 62.69291
- 566.6236
- 0 ]
- /Next 339 0 R
- /Parent 329 0 R
- /Prev 337 0 R
- /Title (The plac runner) >>
-endobj
-% 'Outline.38.9': class OutlineEntryObject
-339 0 obj
-<< /Dest [ 283 0 R
- /XYZ
- 62.69291
- 729.0236
- 0 ]
- /Next 340 0 R
- /Parent 329 0 R
- /Prev 338 0 R
- /Title (A non class-based example) >>
-endobj
-% 'Outline.38.10': class OutlineEntryObject
-340 0 obj
-<< /Dest [ 286 0 R
- /XYZ
- 62.69291
- 729.0236
- 0 ]
- /Next 341 0 R
- /Parent 329 0 R
- /Prev 339 0 R
- /Title (Writing your own plac runner) >>
-endobj
-% 'Outline.38.11': class OutlineEntryObject
-341 0 obj
-<< /Dest [ 290 0 R
- /XYZ
- 62.69291
- 555.8236
- 0 ]
- /Next 342 0 R
- /Parent 329 0 R
- /Prev 340 0 R
- /Title (Long running commands) >>
-endobj
-% 'Outline.38.12': class OutlineEntryObject
-342 0 obj
-<< /Dest [ 293 0 R
- /XYZ
- 62.69291
- 659.8236
- 0 ]
- /Next 343 0 R
- /Parent 329 0 R
- /Prev 341 0 R
- /Title (Threaded commands) >>
-endobj
-% 'Outline.38.13': class OutlineEntryObject
-343 0 obj
-<< /Dest [ 298 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Next 344 0 R
- /Parent 329 0 R
- /Prev 342 0 R
- /Title (Running commands as external processes) >>
-endobj
-% 'Outline.38.14': class OutlineEntryObject
-344 0 obj
-<< /Dest [ 298 0 R
- /XYZ
- 62.69291
- 297.8236
- 0 ]
- /Next 345 0 R
- /Parent 329 0 R
- /Prev 343 0 R
- /Title (Managing the output of concurrent commands) >>
-endobj
-% 'Outline.38.15': class OutlineEntryObject
-345 0 obj
-<< /Dest [ 300 0 R
- /XYZ
- 62.69291
- 427.8236
- 0 ]
- /Next 346 0 R
- /Parent 329 0 R
- /Prev 344 0 R
- /Title (Monitor support) >>
-endobj
-% 'Outline.38.16': class OutlineEntryObject
-346 0 obj
-<< /Dest [ 302 0 R
- /XYZ
- 62.69291
- 717.0236
- 0 ]
- /Next 347 0 R
- /Parent 329 0 R
- /Prev 345 0 R
- /Title (Parallel computing with plac) >>
-endobj
-% 'Outline.38.17': class OutlineEntryObject
-347 0 obj
-<< /Dest [ 306 0 R
- /XYZ
- 62.69291
- 553.2159
- 0 ]
- /Next 348 0 R
- /Parent 329 0 R
- /Prev 346 0 R
- /Title (The plac server) >>
-endobj
-% 'Outline.38.18': class OutlineEntryObject
-348 0 obj
-<< /Dest [ 309 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Next 349 0 R
- /Parent 329 0 R
- /Prev 347 0 R
- /Title (Summary) >>
-endobj
-% 'Outline.38.19': class OutlineEntryObject
-349 0 obj
-<< /Dest [ 309 0 R
- /XYZ
- 62.69291
- 454.6772
- 0 ]
- /Parent 329 0 R
- /Prev 348 0 R
- /Title (Appendix: custom annotation objects) >>
-endobj
-% 'R350': class PDFPages
-350 0 obj
-% page tree
-<< /Count 44
- /Kids [ 8 0 R
- 81 0 R
- 97 0 R
- 107 0 R
- 112 0 R
- 116 0 R
- 122 0 R
- 123 0 R
- 126 0 R
- 134 0 R
- 135 0 R
- 139 0 R
- 142 0 R
- 144 0 R
- 147 0 R
- 159 0 R
- 174 0 R
- 214 0 R
- 231 0 R
- 232 0 R
- 234 0 R
- 237 0 R
- 247 0 R
- 253 0 R
- 255 0 R
- 259 0 R
- 260 0 R
- 262 0 R
- 270 0 R
- 279 0 R
- 281 0 R
- 283 0 R
- 284 0 R
- 286 0 R
- 290 0 R
- 293 0 R
- 294 0 R
- 298 0 R
- 300 0 R
- 302 0 R
- 303 0 R
- 306 0 R
- 309 0 R
- 310 0 R ]
- /Type /Pages >>
-endobj
-% 'R351': class PDFStream
-351 0 obj
-% page stream
-<< /Length 2933 >>
-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 3.5 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Plac: Parsing the Command Line the Easy Way) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 732.0236 cm
-Q
-q
-1 0 0 1 62.69291 717.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 36.93937 0 Td (Author:) Tj T* -36.93937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Michele Simionato) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 702.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 39.69937 0 Td (E-mail:) Tj T* -39.69937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (michele.simionato@gmail.com) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 687.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 48.03937 0 Td (Date:) Tj T* -48.03937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (June 2011) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 660.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F2 10 Tf 12 TL 25.25937 0 Td (Download) Tj T* 21.11 0 Td (page:) Tj T* -46.36937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 15 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (http://pypi.python.org/pypi/plac) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 645.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 9.68937 0 Td (Project page:) Tj T* -9.68937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (http://plac.googlecode.com/hg/doc/plac.html) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 630.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 26.91937 0 Td (Requires:) Tj T* -26.91937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Python 2.3+) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 615.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 16.91937 0 Td (Installation:) Tj T* -16.91937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL (easy_install -U plac) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 600.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 32.46937 0 Td (License:) Tj T* -32.46937 0 Td ET
-Q
-Q
-q
-1 0 0 1 91.03937 3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (BSD license) 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 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (1) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R352': class PDFStream
-352 0 obj
-% page stream
-<< /Length 9629 >>
-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 3.5 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Contents) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 90.02362 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 0 633 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Plac: Parsing the Command Line the Easy Way) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 633 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 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 615 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (The importance of scaling down) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 615 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 66.44 0 Td (3) Tj T* -66.44 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 597 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Scripts with required arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 597 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 66.44 0 Td (3) Tj T* -66.44 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 579 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Scripts with default arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 579 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 66.44 0 Td (5) Tj T* -66.44 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 561 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Scripts with options \(and smart options\)) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 561 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 66.44 0 Td (7) Tj T* -66.44 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 543 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Scripts with flags) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 543 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 525 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac for Python 2.X users) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 525 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 66.44 0 Td (9) Tj T* -66.44 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 507 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (More features) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 507 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (10) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 489 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (A realistic example) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 489 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (12) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 471 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Keyword arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 471 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (13) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 453 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Final example: a shelve interface) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 453 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (14) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 435 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac vs argparse) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 435 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (16) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 417 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac vs the rest of the world) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 417 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (18) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 399 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (The future) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 399 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (18) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 381 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Trivia: the story behind the name) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 381 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (18) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 363 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Advanced usages of plac) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 363 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F2 10 Tf 12 TL 60.88 0 Td (19) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 345 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Introduction) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 345 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (19) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 327 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (From scripts to interactive applications) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 327 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (19) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 309 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Testing a plac application) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 309 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (21) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 291 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Plac easy tests) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 291 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (22) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 273 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Plac batch scripts) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 273 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (23) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 255 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Implementing subcommands) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 255 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (24) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 237 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac.Interpreter.call) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 237 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (27) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 219 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Readline support) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 219 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (28) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 201 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (The plac runner) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 201 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (30) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 183 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (A non class-based example) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 183 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (32) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 165 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Writing your own plac runner) 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 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (34) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 147 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Long running commands) 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 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (35) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 129 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Threaded commands) 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 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (36) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 111 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Running commands as external processes) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 111 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (38) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 93 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Managing the output of concurrent commands) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 93 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (38) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 75 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Monitor support) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 75 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (39) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 57 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Parallel computing with plac) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 57 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (40) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 39 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (The plac server) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 39 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (42) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 21 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Summary) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 21 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (43) Tj T* -60.88 0 Td ET
-Q
-Q
-q
-1 0 0 1 0 3 cm
-q
-BT 1 0 0 1 20 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (Appendix: custom annotation objects) Tj T* ET
-Q
-Q
-q
-1 0 0 1 397.8898 3 cm
-q
-0 0 .501961 rg
-0 0 .501961 RG
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 60.88 0 Td (43) Tj T* -60.88 0 Td 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 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (2) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R353': class PDFStream
-353 0 obj
-% page stream
-<< /Length 5956 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 747.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (The importance of scaling down) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 681.0236 cm
-q
-BT 1 0 0 1 0 50 Tm 1.50936 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is no want of command line arguments parsers in the Python world. The standard library alone) Tj T* 0 Tw 1.087126 Tw (contains three different modules: ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (\(from the stone age\), ) Tj 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (\(from Python 2.3\) and ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw .223735 Tw 0 0 0 rg (\(from Python 2.7\). All of them are quite powerful and especially ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (is an industrial strength solution;) Tj T* 0 Tw 1.40311 Tw (unfortunately, all of them feature a non-zero learning curve and a certain verbosity. They do not scale) Tj T* 0 Tw (down well, at least in my opinion.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 603.0236 cm
-q
-BT 1 0 0 1 0 62 Tm 2.20186 Tw 12 TL /F1 10 Tf 0 0 0 rg (It should not be necessary to stress the importance ) Tj 0 0 .501961 rg (scaling down) Tj 0 0 0 rg (; nevertheless, a lot of people are) Tj T* 0 Tw .968555 Tw (obsessed with features and concerned with the possibility of scaling up, forgetting the equally important) Tj T* 0 Tw .048221 Tw (issue of scaling down. This is an old meme in the computing world: programs should address the common) Tj T* 0 Tw .36311 Tw (cases simply and simple things should be kept simple, while at the same keeping difficult things possible.) Tj T* 0 Tw 1.09332 Tw 0 0 .501961 rg (plac ) Tj 0 0 0 rg (adhere as much as possible to this philosophy and it is designed to handle well the simple cases,) Tj T* 0 Tw (while retaining the ability to handle complex cases by relying on the underlying power of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 525.0236 cm
-q
-BT 1 0 0 1 0 62 Tm 1.488221 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is just a simple wrapper over ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (which hides most of its complexity by using a) Tj T* 0 Tw .203318 Tw (declarative interface: the argument parser is inferred rather than written down by imperatively. Still, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is) Tj T* 0 Tw .125984 Tw (surprisingly scalable upwards, even without using the underlying ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. I have been using Python for 8) Tj T* 0 Tw 1.618876 Tw (years and in my experience it is extremely unlikely that you will ever need to go beyond the features) Tj T* 0 Tw 1.776457 Tw (provided by the declarative interface of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (: they should be more than enough for 99.9% of the use) Tj T* 0 Tw (cases.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 423.0236 cm
-q
-BT 1 0 0 1 0 86 Tm 1.540888 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is targetting especially unsophisticated users, programmers, sys-admins, scientists and in general) Tj T* 0 Tw .81284 Tw (people writing throw-away scripts for themselves, choosing the command line interface because it is the) Tj T* 0 Tw .471751 Tw (quick and simple. Such users are not interested in features, they are interested in a small learning curve:) Tj T* 0 Tw .984988 Tw (they just want to be able to write a simple command line tool from a simple specification, not to build a) Tj T* 0 Tw 1.127318 Tw (command-line parser by hand. Unfortunately, the modules in the standard library forces them to go the) Tj T* 0 Tw .014104 Tw (hard way. They are designed to implement power user tools and they have a non-trivial learning curve. On) Tj T* 0 Tw 1.584104 Tw (the contrary, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is designed to be simple to use and extremely concise, as the examples below will) Tj T* 0 Tw (show.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 393.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Scripts with required arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 327.0236 cm
-q
-BT 1 0 0 1 0 50 Tm .352209 Tw 12 TL /F1 10 Tf 0 0 0 rg (Let me start with the simplest possible thing: a script that takes a single argument and does something to) Tj T* 0 Tw 1.053984 Tw (it. It cannot get simpler than that, unless you consider a script without command-line arguments, where) Tj T* 0 Tw .735488 Tw (there is nothing to parse. Still, it is a use case ) Tj /F4 10 Tf (extremely common) Tj /F1 10 Tf (: I need to write scripts like that nearly) Tj T* 0 Tw .486655 Tw (every day, I wrote hundreds of them in the last few years and I have never been happy. Here is a typical) Tj T* 0 Tw (example of code I have been writing by hand for years:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 125.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 192 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 170 Tm /F3 10 Tf 12 TL (# example1.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( n = len\(sys.argv[1:]\)) Tj T* ( if n == 0:) Tj T* ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif n == 1:) Tj T* ( main\(sys.argv[1]\)) Tj T* ( else:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(sys.argv[2:]\)\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 93.82362 cm
-q
-BT 1 0 0 1 0 14 Tm .880651 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see the whole ) Tj /F3 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block \(nine lines\) is essentially boilerplate that ) Tj T* 0 Tw 1.972927 Tw (should not exist. Actually I think the language should recognize the main function and pass to it the) Tj T* 0 Tw ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (3) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R354': class PDFStream
-354 0 obj
-% page stream
-<< /Length 4163 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 693.0236 cm
-q
-BT 1 0 0 1 0 62 Tm 3.309147 Tw 12 TL /F1 10 Tf 0 0 0 rg (command-line arguments automatically; unfortunaly this is unlikely to happen. I have been writing) Tj T* 0 Tw 1.767356 Tw (boilerplate like this in hundreds of scripts for years, and every time I ) Tj /F4 10 Tf (hate ) Tj /F1 10 Tf (it. The purpose of using a) Tj T* 0 Tw 1.47229 Tw (scripting language is convenience and trivial things should be trivial. Unfortunately the standard library) Tj T* 0 Tw .69881 Tw (does not help for this incredibly common use case. Using ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (and ) Tj 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (does not help, since they) Tj T* 0 Tw .894104 Tw (are intended to manage options and not positional arguments; the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module helps a bit and it is) Tj T* 0 Tw (able to reduce the boilerplate from nine lines to six lines:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 527.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 134 Tm /F3 10 Tf 12 TL (# example2.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import argparse) Tj T* ( p = argparse.ArgumentParser\(\)) Tj T* ( p.add_argument\('dsn'\)) Tj T* ( arg = p.parse_args\(\)) Tj T* ( main\(arg.dsn\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 495.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F1 10 Tf 12 TL .817488 Tw (However, it just feels too complex to instantiate a class and to define a parser by hand for such a trivial) Tj T* 0 Tw (task.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 465.8236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.123145 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (module is designed to manage well such use cases, and it is able to reduce the original nine) Tj T* 0 Tw (lines of boiler plate to two lines. With the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (module all you need to write is) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 348.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 86 Tm /F3 10 Tf 12 TL (# example3.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) Tj T* ( # ...) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 316.6236 cm
-q
-BT 1 0 0 1 0 14 Tm .929986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (module provides for free \(actually the work is done by the underlying ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module\) a nice) Tj T* 0 Tw (usage message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 283.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 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL ($ python example3.py -h) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 154.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 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (usage: example3.py [-h] dsn) Tj T* T* (Do something with the database) Tj T* T* (positional arguments:) Tj T* ( dsn) 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 122.2236 cm
-q
-BT 1 0 0 1 0 14 Tm .167765 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (manages the case of missing arguments and of too many arguments. This is only the tip of) Tj T* 0 Tw (the iceberg: ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to do much more than that.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (4) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R355': class PDFStream
-355 0 obj
-% page stream
-<< /Length 4211 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 747.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Scripts with default arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 717.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F1 10 Tf 12 TL 2.609984 Tw (The need to have suitable defaults for command-line scripts is quite common. For instance I have) Tj T* 0 Tw (encountered this use case at work hundreds of times:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 515.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 192 re B*
-Q
-q
-BT 1 0 0 1 0 170 Tm 12 TL /F3 10 Tf 0 0 0 rg (# example4.py) Tj T* (from datetime import datetime) Tj T* T* (def main\(dsn, table='product', today=datetime.today\(\)\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn, table, today\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( args = sys.argv[1:]) Tj T* ( if not args:) Tj T* ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif len\(args\) ) Tj (>) Tj ( 2:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(argv[2:]\)\)) Tj T* ( main\(*args\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 447.8236 cm
-q
-BT 1 0 0 1 0 50 Tm .038488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here I want to perform a query on a database table, by extracting the most recent data: it makes sense for) Tj T* 0 Tw .299988 Tw /F3 10 Tf (today ) Tj /F1 10 Tf (to be a default argument. If there is a most used table \(in this example a table called ) Tj /F3 10 Tf ('product') Tj /F1 10 Tf (\)) Tj T* 0 Tw 3.313984 Tw (it also makes sense to make it a default argument. Performing the parsing of the command-line) Tj T* 0 Tw .083735 Tw (arguments by hand takes 8 ugly lines of boilerplate \(using ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (would require about the same number) Tj T* 0 Tw (of lines\). With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the entire ) Tj /F3 10 Tf (__main__ ) Tj /F1 10 Tf (block reduces to the usual two lines:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 402.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 382.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (In other words, six lines of boilerplate have been removed, and we get the usage message for free:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 229.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 144 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 122 Tm /F3 10 Tf 12 TL (usage: example5.py [-h] dsn [table] [today]) Tj T* T* (Do something on the database) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( table [product]) Tj T* ( today [YYYY-MM-DD]) 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 185.4236 cm
-q
-BT 1 0 0 1 0 26 Tm .81311 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that by default ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (prints the string representation of the default values \(with square brackets\) in) Tj T* 0 Tw .117485 Tw (the usage message. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (manages transparently even the case when you want to pass a variable number) Tj T* 0 Tw (of arguments. Here is an example, a script running on a database a series of SQL scripts:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 92.22362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 62 Tm /F3 10 Tf 12 TL (# example7.py) Tj T* (from datetime import datetime) Tj T* T* (def main\(dsn, *scripts\):) Tj T* ( "Run the given scripts on the database") Tj T* ( for script in scripts:) 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 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (5) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R356': class PDFStream
-356 0 obj
-% page stream
-<< /Length 3873 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 691.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 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 50 Tm /F3 10 Tf 12 TL ( print\('executing %s' % script\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 671.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 530.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 110 Tm /F3 10 Tf 12 TL (usage: example7.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (Run the given scripts on the database) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( scripts) Tj T* T* (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 486.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .952485 Tw 12 TL /F1 10 Tf 0 0 0 rg (The examples here should have made clear that ) Tj /F4 10 Tf (plac is able to figure out the command-line arguments) Tj T* 0 Tw .899988 Tw (parser to use from the signature of the main function) Tj /F1 10 Tf (. This is the whole idea behind ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (: if the intent is) Tj T* 0 Tw (clear, let's the machine take care of the details.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 444.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .722765 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is inspired to an old Python Cookbook recipe of mine \() Tj 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (\), in the sense that it delivers the) Tj T* 0 Tw .847209 Tw (programmer from the burden of writing the parser, but is less of a hack: instead of extracting the parser) Tj T* 0 Tw (from the docstring of the module, it extracts it from the signature of the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 414.6236 cm
-q
-BT 1 0 0 1 0 14 Tm .319987 Tw 12 TL /F1 10 Tf 0 0 0 rg (The idea comes from the ) Tj /F4 10 Tf (function annotations ) Tj /F1 10 Tf (concept, a new feature of Python 3. An example is worth a) Tj T* 0 Tw (thousand words, so here it is:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 261.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 144 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 122 Tm /F3 10 Tf 12 TL (# example7_.py) Tj T* (from datetime import datetime) Tj T* T* (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ( "Run the given scripts on the database") Tj T* ( for script in scripts:) Tj T* ( print\('executing %s' % script\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 229.4236 cm
-q
-BT 1 0 0 1 0 14 Tm .17528 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here the arguments of the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function have been annotated with strings which are intented to be used) Tj T* 0 Tw (in the help message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 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 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (usage: example7_.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (Run the given scripts on the database) Tj T* T* (positional arguments:) Tj T* ( dsn Database dsn) Tj T* ( scripts SQL scripts) Tj T* T* (optional arguments:) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (6) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R357': class PDFStream
-357 0 obj
-% page stream
-<< /Length 5179 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 739.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 719.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize much more complex annotations, as I will show in the next paragraphs.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 689.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Scripts with options \(and smart options\)) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 599.8236 cm
-q
-BT 1 0 0 1 0 74 Tm .016457 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is surprising how few command-line scripts with options I have written over the years \(probably less than) Tj T* 0 Tw 1.02311 Tw (a hundred\), compared to the number of scripts with positional arguments I wrote \(certainly more than a) Tj T* 0 Tw .177045 Tw (thousand of them\). Still, this use case cannot be neglected. The standard library modules \(all of them\) are) Tj T* 0 Tw 2.30686 Tw (quite verbose when it comes to specifying the options and frankly I have never used them directly.) Tj T* 0 Tw 2.557126 Tw (Instead, I have always relied on the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe, which provides a convenient wrapper over) Tj T* 0 Tw 1.09061 Tw 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (. Alternatively, in the simplest cases, I have just performed the parsing by hand. In ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the) Tj T* 0 Tw (parser is inferred by the function annotations. Here is an example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 482.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 86 Tm /F3 10 Tf 12 TL (# example8.py) Tj T* (def main\(command: \("SQL query", 'option', 'c'\), dsn\):) Tj T* ( if command:) Tj T* ( print\('executing %s on %s' % \(command, dsn\)\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 426.6236 cm
-q
-BT 1 0 0 1 0 38 Tm .929213 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here the argument ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (has been annotated with the tuple ) Tj /F3 10 Tf (\("SQL query", 'option', 'c'\)) Tj /F1 10 Tf (:) Tj T* 0 Tw .62683 Tw (the first string is the help string which will appear in the usage message, the second string tells ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (that) Tj T* 0 Tw .931894 Tw /F3 10 Tf (command ) Tj /F1 10 Tf (is an option and the third string that there is also a short form of the option ) Tj /F3 10 Tf (-c) Tj /F1 10 Tf (, the long form) Tj T* 0 Tw (being ) Tj /F3 10 Tf (--command) Tj /F1 10 Tf (. The usage message is the following:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 297.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 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (usage: example8.py [-h] [-c COMMAND] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -c COMMAND, --command COMMAND) Tj T* ( SQL query) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 277.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here are two examples of usage:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 196.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 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 50 Tm /F3 10 Tf 12 TL ($ python3 example8.py -c"select * from table" dsn) Tj T* (executing select * from table on dsn) Tj T* T* ($ python3 example8.py --command="select * from table" dsn) Tj T* (executing select * from table on dsn) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 140.2236 cm
-q
-BT 1 0 0 1 0 38 Tm .268935 Tw 12 TL /F1 10 Tf 0 0 0 rg (The third argument in the function annotation can be omitted: in such case it will be assumed to be ) Tj /F3 10 Tf (None) Tj /F1 10 Tf (.) Tj T* 0 Tw 2.839213 Tw (The consequence is that the usual dichotomy between long and short options \(GNU-style options\)) Tj T* 0 Tw .396235 Tw (disappears: we get ) Tj /F4 10 Tf (smart options) Tj /F1 10 Tf (, which have the single character prefix of short options and behave like) Tj T* 0 Tw (both long and short options, since they can be abbreviated. Here is an example featuring smart options:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 95.02362 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 14 Tm /F3 10 Tf 12 TL (# example6.py) Tj T* (def main\(dsn, command: \("SQL query", 'option'\)\):) 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 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (7) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R358': class PDFStream
-358 0 obj
-% page stream
-<< /Length 4058 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 703.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL ( print\('executing %r on %s' % \(command, dsn\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 586.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 86 Tm /F3 10 Tf 12 TL (usage: example6.py [-h] [-command COMMAND] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -command COMMAND SQL query) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 566.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (The following are all valid invocations ot the script:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 473.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
-0 0 0 rg
-BT 1 0 0 1 0 62 Tm /F3 10 Tf 12 TL ($ python3 example6.py -c "select" dsn) Tj T* (executing 'select' on dsn) Tj T* ($ python3 example6.py -com "select" dsn) Tj T* (executing 'select' on dsn) Tj T* ($ python3 example6.py -command="select" dsn) Tj T* (executing 'select' on dsn) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 453.4236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that the form ) Tj /F3 10 Tf (-command=SQL ) Tj /F1 10 Tf (is recognized only for the full option, not for its abbreviations:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 396.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 48 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 26 Tm /F3 10 Tf 12 TL ($ python3 example6.py -com="select" dsn) Tj T* (usage: example6.py [-h] [-command COMMAND] dsn) Tj T* (example6.py: error: unrecognized arguments: -com=select) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 364.2236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.724987 Tw 12 TL /F1 10 Tf 0 0 0 rg (If the option is not passed, the variable ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (will get the value ) Tj /F3 10 Tf (None) Tj /F1 10 Tf (. However, it is possible to) Tj T* 0 Tw (specify a non-trivial default. Here is an example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 271.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 84 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 62 Tm /F3 10 Tf 12 TL (# example8_.py) Tj T* (def main\(dsn, command: \("SQL query", 'option'\)='select * from table'\):) Tj T* ( print\('executing %r on %s' % \(command, dsn\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 251.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Notice that the default value appears in the help message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 121.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 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (usage: example8_.py [-h] [-command select * from table] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -command select * from table) Tj T* ( SQL query) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 101.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (When you run the script and you do not pass the ) Tj /F3 10 Tf (-command ) Tj /F1 10 Tf (option, the default query will be executed:) 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 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (8) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R359': class PDFStream
-359 0 obj
-% page stream
-<< /Length 4742 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 727.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL ($ python3 example8_.py dsn) Tj T* (executing 'select * from table' on dsn) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 697.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Scripts with flags) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 667.8236 cm
-q
-BT 1 0 0 1 0 14 Tm .815542 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is able to recognize flags, i.e. boolean options which are ) Tj /F3 10 Tf (True ) Tj /F1 10 Tf (if they are passed to the command) Tj T* 0 Tw (line and ) Tj /F3 10 Tf (False ) Tj /F1 10 Tf (if they are absent. Here is an example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 542.9299 cm
-q
-q
-.96447 0 0 .96447 0 0 cm
-q
-1 0 0 1 6.6 6.843137 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 486 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (# example9.py) Tj T* T* (def main\(verbose: \('prints more info', 'flag', 'v'\), dsn: 'connection string'\):) Tj T* ( if verbose:) Tj T* ( print\('connecting to %s' % dsn\)) Tj T* ( # ...) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 425.7299 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 86 Tm /F3 10 Tf 12 TL (usage: example9.py [-h] [-v] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn connection string) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -v, --verbose prints more info) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 380.5299 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 14 Tm /F3 10 Tf 12 TL ($ python3 example9.py -v dsn) Tj T* (connecting to dsn) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 336.5299 cm
-q
-BT 1 0 0 1 0 26 Tm .31408 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that it is an error trying to specify a default for flags: the default value for a flag is always ) Tj /F3 10 Tf (False) Tj /F1 10 Tf (. If) Tj T* 0 Tw 2.652485 Tw (you feel the need to implement non-boolean flags, you should use an option with two choices, as) Tj T* 0 Tw (explained in the "more features" section.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 270.5299 cm
-q
-BT 1 0 0 1 0 50 Tm 5.832651 Tw 12 TL /F1 10 Tf 0 0 0 rg (For consistency with the way the usage message is printed, I suggest you to follow the) Tj T* 0 Tw 1.895433 Tw (Flag-Option-Required-Default \(FORD\) convention: in the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function write first the flag arguments,) Tj T* 0 Tw .881235 Tw (then the option arguments, then the required arguments and finally the default arguments. This is just a) Tj T* 0 Tw .110574 Tw (convention and you are not forced to use it, except for the default arguments \(including the varargs\) which) Tj T* 0 Tw (must stay at the end as it is required by the Python syntax.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 228.5299 cm
-q
-BT 1 0 0 1 0 26 Tm .897045 Tw 12 TL /F1 10 Tf 0 0 0 rg (I also suggests to specify a one-character abbreviation for flags: in this way you can use the GNU-style) Tj T* 0 Tw 2.034431 Tw (composition of flags \(i.e. ) Tj /F3 10 Tf (-zxvf ) Tj /F1 10 Tf (is an abbreviation of ) Tj /F3 10 Tf (-z -x -v -f) Tj /F1 10 Tf (\). I usually do not provide the) Tj T* 0 Tw (one-character abbreviation for options, since it does not make sense to compose them.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 198.5299 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (plac for Python 2.X users) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 132.5299 cm
-q
-BT 1 0 0 1 0 50 Tm .211807 Tw 12 TL /F1 10 Tf 0 0 0 rg (I do not use Python 3. At work we are just starting to think about migrating to Python 2.6. It will take years) Tj T* 0 Tw .304724 Tw (before we think to migrate to Python 3. I am pretty much sure most Pythonistas are in the same situation.) Tj T* 0 Tw 1.459984 Tw (Therefore ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides a way to work with function annotations even in Python 2.X \(including Python) Tj T* 0 Tw 2.692339 Tw (2.3\). There is no magic involved; you just need to add the annotations by hand. For instance the) Tj T* 0 Tw (annotated function declaration) Tj T* ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 238.1649 0 Td (9) Tj T* -238.1649 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R360': class PDFStream
-360 0 obj
-% page stream
-<< /Length 6254 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 727.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 707.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (is equivalent to the following code:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 626.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 50 Tm /F3 10 Tf 12 TL (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* (main.__annotations__ = dict\() Tj T* ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 582.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .536098 Tw 12 TL /F1 10 Tf 0 0 0 rg (One should be careful to match the keys of the annotation dictionary with the names of the arguments in) Tj T* 0 Tw 3.347485 Tw (the annotated function; for lazy people with Python 2.4 available the simplest way is to use the) Tj T* 0 Tw /F3 10 Tf (plac.annotations ) Tj /F1 10 Tf (decorator that performs the check for you:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 501.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 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 50 Tm /F3 10 Tf 12 TL (@plac.annotations\() Tj T* ( dsn="Database dsn",) Tj T* ( scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 469.4236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.846077 Tw 12 TL /F1 10 Tf 0 0 0 rg (In the rest of this article I will assume that you are using Python 2.X with X >) Tj (= 4 and I will use the) Tj T* 0 Tw /F3 10 Tf (plac.annotations ) Tj /F1 10 Tf (decorator. Notice however that the core features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (run even on Python 2.3.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 439.4236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (More features) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 385.4236 cm
-q
-BT 1 0 0 1 0 38 Tm 1.483488 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the goals of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is to have a learning curve of ) Tj /F4 10 Tf (minutes ) Tj /F1 10 Tf (for its core features, compared to the) Tj T* 0 Tw 1.152093 Tw (learning curve of ) Tj /F4 10 Tf (hours ) Tj /F1 10 Tf (of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. In order to reach this goal, I have ) Tj /F4 10 Tf (not ) Tj /F1 10 Tf (sacrificed all the features of) Tj T* 0 Tw 2.89936 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually a lot of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (power persists in ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Until now, I have only showed simple) Tj T* 0 Tw (annotations, but in general an annotation is a 6-tuple of the form) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 379.4236 cm
-Q
-q
-1 0 0 1 62.69291 367.4236 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL (\(help, kind, abbrev, type, choices, metavar\)) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 367.4236 cm
-Q
-q
-1 0 0 1 62.69291 325.4236 cm
-q
-BT 1 0 0 1 0 26 Tm 1.068735 Tw 12 TL /F1 10 Tf 0 0 0 rg (where ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (is the help message, ) Tj /F3 10 Tf (kind ) Tj /F1 10 Tf (is a string in the set { ) Tj /F3 10 Tf ("flag") Tj /F1 10 Tf (, ) Tj /F3 10 Tf ("option") Tj /F1 10 Tf (, ) Tj /F3 10 Tf ("positional") Tj /F1 10 Tf (},) Tj T* 0 Tw 1.579431 Tw /F3 10 Tf (abbrev ) Tj /F1 10 Tf (is a one-character string or ) Tj /F3 10 Tf (None) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (type ) Tj /F1 10 Tf (is a callable taking a string in input, ) Tj /F3 10 Tf (choices ) Tj /F1 10 Tf (is a) Tj T* 0 Tw (discrete sequence of values and ) Tj /F3 10 Tf (metavar ) Tj /F1 10 Tf (is a string.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 295.4236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.05061 Tw 12 TL /F3 10 Tf 0 0 0 rg (type ) Tj /F1 10 Tf (is used to automagically convert the command line arguments from the string type to any Python) Tj T* 0 Tw (type; by default there is no conversion and ) Tj /F3 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 265.4236 cm
-q
-BT 1 0 0 1 0 14 Tm 2.904692 Tw 12 TL /F3 10 Tf 0 0 0 rg (choices ) Tj /F1 10 Tf (is used to restrict the number of the valid options; by default there is no restriction i.e.) Tj T* 0 Tw /F3 10 Tf (choices=None) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 187.4236 cm
-q
-BT 1 0 0 1 0 62 Tm 1.171163 Tw 12 TL /F3 10 Tf 0 0 0 rg (metavar ) Tj /F1 10 Tf (has two meanings. For a positional argument it is used to change the argument name in the) Tj T* 0 Tw .352209 Tw (usage message \(and only there\). By default the metavar is ) Tj /F3 10 Tf (None ) Tj /F1 10 Tf (and the name in the usage message is) Tj T* 0 Tw .752339 Tw (the same as the argument name. For an option the ) Tj /F3 10 Tf (metavar ) Tj /F1 10 Tf (is used differently in the usage message,) Tj T* 0 Tw .802927 Tw (which has now the form ) Tj /F3 10 Tf ([--option-name METAVAR]) Tj /F1 10 Tf (. If the ) Tj /F3 10 Tf (metavar ) Tj /F1 10 Tf (is ) Tj /F3 10 Tf (None) Tj /F1 10 Tf (, then it is equal to the) Tj T* 0 Tw .50683 Tw (uppercased name of the argument, unless the argument has a default and in such a case is equal to the) Tj T* 0 Tw (stringified form of the default.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 169.4236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (Here is an example showing many of the features \(copied from the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation\):) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 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 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL (# example10.py) Tj T* (import plac) Tj T* T* (@plac.annotations\() 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (10) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R361': class PDFStream
-361 0 obj
-% page stream
-<< /Length 3696 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 564.6467 cm
-q
-q
-.976496 0 0 .976496 0 0 cm
-q
-1 0 0 1 6.6 6.758862 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 480 204 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 182 Tm /F3 10 Tf 12 TL (operator=\("The name of an operator", 'positional', None, str, ['add', 'mul']\),) Tj T* (numbers=\("A number", 'positional', None, float, None, "n"\)\)) Tj T* (def main\(operator, *numbers\):) Tj T* ( "A script to add and multiply numbers") Tj T* ( if operator == 'mul':) Tj T* ( op = float.__mul__) Tj T* ( result = 1.0) Tj T* ( else: # operator == 'add') Tj T* ( op = float.__add__) Tj T* ( result = 0.0) Tj T* ( for n in numbers:) Tj T* ( result = op\(result, n\)) Tj T* ( return result) Tj T* T* (if __name__ == '__main__':) Tj T* ( print\(plac.call\(main\)\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 544.6467 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is the usage:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 403.4467 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 110 Tm /F3 10 Tf 12 TL (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* T* (A script to add and multiply numbers) Tj T* T* (positional arguments:) Tj T* ( {add,mul} The name of an operator) Tj T* ( n A number) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 371.4467 cm
-q
-BT 1 0 0 1 0 14 Tm .15186 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the docstring of the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function has been automatically added to the usage message. Here) Tj T* 0 Tw (are a couple of examples of use:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 278.108 cm
-q
-q
-.87797 0 0 .87797 0 0 cm
-q
-1 0 0 1 6.6 7.517338 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 534 96 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 74 Tm /F3 10 Tf 12 TL ($ python example10.py add 1 2 3 4) Tj T* (10.0) Tj T* ($ python example10.py mul 1 2 3 4) Tj T* (24.0) Tj T* ($ python example10.py ad 1 2 3 4 # a mispelling error) Tj T* (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* (example10.py: error: argument operator: invalid choice: 'ad' \(choose from 'add', 'mul'\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 258.108 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F3 10 Tf 0 0 0 rg (plac.call ) Tj /F1 10 Tf (can also be used in doctests like this:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 200.908 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
-BT 1 0 0 1 0 26 Tm 12 TL /F3 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac, example10) Tj T* (>) Tj (>) Tj (>) Tj ( plac.call\(example10.main, ['add', '1', '2']\)) Tj T* (3.0) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 180.908 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F3 10 Tf 0 0 0 rg (plac.call ) Tj /F1 10 Tf (works for generators too:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 99.70797 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
-Q
-q
-BT 1 0 0 1 0 50 Tm 12 TL /F3 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( def main\(n\):) Tj T* (... for i in range\(int\(n\)\):) Tj T* (... yield i) Tj T* (>) Tj (>) Tj (>) Tj ( plac.call\(main, ['3']\)) Tj T* ([0, 1, 2]) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (11) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R362': class PDFStream
-362 0 obj
-% page stream
-<< /Length 4537 >>
-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 26 Tm .158409 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (tries to convert the output of the main function into a list, if possible. If the output is) Tj T* 0 Tw .725703 Tw (not iterable or it is a string, it is left unchanged, but if it is iterable it is converted. In particular, generator) Tj T* 0 Tw (objects are exhausted by ) Tj /F3 10 Tf (plac.call) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 699.0236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.450751 Tw 12 TL /F1 10 Tf 0 0 0 rg (This behavior avoids mistakes like forgetting of applying ) Tj /F3 10 Tf (list\(result\) ) Tj /F1 10 Tf (to the result of ) Tj /F3 10 Tf (plac.call) Tj /F1 10 Tf (;) Tj T* 0 Tw (moreover it makes errors visible early, and avoids mistakes in code like the following:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 629.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL (try:) Tj T* ( result = plac.call\(main, args\)) Tj T* (except:) Tj T* ( # do something) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 597.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 567.8236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (eager ) Tj /F1 10 Tf (flag to ) Tj /F3 10 Tf (False) Tj /F1 10 Tf (, as in the following) Tj T* 0 Tw (example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 522.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 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 490.6236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.35528 Tw 12 TL /F1 10 Tf 0 0 0 rg (If ) Tj /F3 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 460.6236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (A realistic example) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 418.6236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 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 97.42362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 312 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 290 Tm /F3 10 Tf 12 TL (# dbcli.py) Tj T* (import plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (@plac.annotations\() Tj T* ( db=\("Connection string", 'positional', None, SqlSoup\),) Tj T* ( header=\("Header", 'flag', 'H'\),) Tj T* ( sqlcmd=\("SQL command", 'option', 'c', str, None, "SQL"\),) Tj T* ( delimiter=\("Column separator", 'option', 'd'\),) Tj T* ( scripts="SQL scripts",) Tj T* ( \)) Tj T* (def main\(db, header, sqlcmd, delimiter="|", *scripts\):) Tj T* ( "A script to run queries and SQL scripts on a database") Tj T* ( yield 'Working on %s' % db.bind.url) Tj T* T* ( if sqlcmd:) Tj T* ( result = db.bind.execute\(sqlcmd\)) Tj T* ( if header: # print the header) Tj T* ( yield delimiter.join\(result.keys\(\)\)) Tj T* ( for row in result: # print the rows) Tj T* ( yield delimiter.join\(map\(str, row\)\)) Tj T* T* ( for script in scripts:) Tj T* ( db.bind.execute\(file\(script\).read\(\)\)) Tj T* ( yield 'executed %s' % script) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (12) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R363': class PDFStream
-363 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 703.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL 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 647.8236 cm
-q
-BT 1 0 0 1 0 38 Tm .049987 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can see the ) Tj /F4 10 Tf (yield-is-print ) Tj /F1 10 Tf (pattern here: instead of using ) Tj /F3 10 Tf (print ) Tj /F1 10 Tf (in the main function, I use ) Tj /F3 10 Tf (yield) Tj /F1 10 Tf (, and) Tj T* 0 Tw 3.55061 Tw (I perform the print in the ) Tj /F3 10 Tf (__main__ ) Tj /F1 10 Tf (block. The advantage of the pattern is that tests invoking) Tj T* 0 Tw .52936 Tw /F3 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 /F3 10 Tf (sys.stdout ) Tj /F1 10 Tf (to a ) Tj /F3 10 Tf (StringIO ) Tj /F1 10 Tf (object.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 629.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 452.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 168 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 146 Tm /F3 10 Tf 12 TL (usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]) Tj T* T* (A script to run queries and SQL scripts on a database) Tj T* T* (positional arguments:) Tj T* ( db Connection string) Tj T* ( scripts SQL scripts) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -H, --header Header) Tj T* ( -c SQL, --sqlcmd SQL SQL command) Tj T* ( -d |, --delimiter | Column separator) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 432.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 402.6236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Keyword arguments) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 360.6236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 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 123.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 228 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 206 Tm /F3 10 Tf 12 TL (# example12.py) Tj T* (import plac) Tj T* T* (@plac.annotations\() Tj T* ( opt=\('some option', 'option'\),) Tj T* ( args='default arguments',) Tj T* ( kw='keyword arguments'\)) Tj T* (def main\(opt, *args, **kw\):) Tj T* ( if opt:) Tj T* ( yield 'opt=%s' % opt) Tj T* ( if args:) Tj T* ( yield 'args=%s' % str\(args\)) Tj T* ( if kw:) Tj T* ( yield 'kw=%s' % kw) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 103.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is the generated usage message:) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (13) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R364': class PDFStream
-364 0 obj
-% page stream
-<< /Length 4073 >>
-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
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]) Tj T* T* (positional arguments:) Tj T* ( args default arguments) Tj T* ( kw keyword arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -opt OPT some option) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 623.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 554.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
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 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
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 522.6236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (opt ) Tj /F1 10 Tf (is taken:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 465.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 26 Tm /F3 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 421.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 26 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 391.4236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Final example: a shelve interface) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 349.4236 cm
-q
-BT 1 0 0 1 0 26 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 105.8928 cm
-q
-q
-.976496 0 0 .976496 0 0 cm
-q
-1 0 0 1 6.6 6.758862 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 480 240 re B*
-Q
-q
-BT 1 0 0 1 0 218 Tm 12 TL /F3 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. Use .help to see the available commands.") Tj T* ( sh = shelve.open\(filename\)) Tj T* ( try:) Tj T* ( if not any\([help, showall, clear, delete, params, setters]\):) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (14) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R365': class PDFStream
-365 0 obj
-% page stream
-<< /Length 6068 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 352.2981 cm
-q
-q
-.952737 0 0 .952737 0 0 cm
-q
-1 0 0 1 6.6 6.927412 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 492 432 re B*
-Q
-q
-BT 1 0 0 1 0 410 Tm 12 TL /F3 10 Tf 0 0 0 rg ( yield 'no arguments passed, use .help to see the available commands') Tj T* ( elif help: # custom help) Tj T* ( yield 'Commands: .help, .showall, .clear, .delete') Tj T* ( yield ') Tj (<) Tj (param) Tj (>) Tj ( ...') Tj T* ( yield ') Tj (<) Tj (param=value) Tj (>) Tj ( ...') Tj T* ( elif showall:) Tj T* ( for param, name in sh.items\(\):) Tj T* ( yield '%s=%s' % \(param, name\)) Tj T* ( elif clear:) Tj T* ( sh.clear\(\)) Tj T* ( yield 'cleared the shelve') Tj T* ( elif delete:) Tj T* ( try:) Tj T* ( del sh[delete]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % delete) Tj T* ( else:) Tj T* ( yield 'deleted %s' % delete) Tj T* ( for param in params:) Tj T* ( try:) Tj T* ( yield sh[param]) Tj T* ( except KeyError:) Tj T* ( yield '%s: not found' % param ) Tj T* ( for param, value in setters.items\(\):) Tj T* ( sh[param] = value) Tj T* ( yield 'setting %s=%s' % \(param, value\)) Tj T* ( finally:) Tj T* ( sh.close\(\)) Tj T* T* (main.add_help = False # there is a custom help, remove the default one) Tj T* (main.prefix_chars = '.' # use dot-prefixed commands) Tj T* T* (if __name__ == '__main__':) Tj T* ( for output in plac.call\(main\):) Tj T* ( print\(output\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 332.2981 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (A few notes are in order:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 326.2981 cm
-Q
-q
-1 0 0 1 62.69291 326.2981 cm
-Q
-q
-1 0 0 1 62.69291 302.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (1.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 14 Tm 2.075318 Tw 12 TL /F1 10 Tf 0 0 0 rg (I have disabled the ordinary help provided by ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (and I have implemented a custom help) Tj T* 0 Tw (command.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 296.2981 cm
-Q
-q
-1 0 0 1 62.69291 284.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (I have changed the prefix character used to recognize the options to a dot.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 278.2981 cm
-Q
-q
-1 0 0 1 62.69291 254.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (3.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 14 Tm .864985 Tw 12 TL /F1 10 Tf 0 0 0 rg (Keyword arguments recognition \(in the ) Tj /F3 10 Tf (**setters) Tj /F1 10 Tf (\) is used to make it possible to store a value in) Tj T* 0 Tw (the shelve with the syntax ) Tj /F3 10 Tf (param_name=param_value) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 248.2981 cm
-Q
-q
-1 0 0 1 62.69291 224.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 14 Tm .649318 Tw 12 TL /F3 10 Tf 0 0 0 rg (*params ) Tj /F1 10 Tf (are used to retrieve parameters from the shelve and some error checking is performed in) Tj T* 0 Tw (the case of missing parameters) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 218.2981 cm
-Q
-q
-1 0 0 1 62.69291 206.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (A command to clear the shelve is implemented as a flag \() Tj /F3 10 Tf (.clear) Tj /F1 10 Tf (\).) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 200.2981 cm
-Q
-q
-1 0 0 1 62.69291 188.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (6.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (A command to delete a given parameter is implemented as an option \() Tj /F3 10 Tf (.delete) Tj /F1 10 Tf (\).) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 182.2981 cm
-Q
-q
-1 0 0 1 62.69291 170.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (7.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (There is an option with default \() Tj /F3 10 Tf (.filename=conf.shelve) Tj /F1 10 Tf (\) to store the filename of the shelve.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 164.2981 cm
-Q
-q
-1 0 0 1 62.69291 128.2981 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 21 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (8.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 26 Tm 1.001984 Tw 12 TL /F1 10 Tf 0 0 0 rg (All things considered, the code looks like a poor man object oriented interface implemented with a) Tj T* 0 Tw 1.345251 Tw (chain of elifs instead of methods. Of course, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can do better than that, but let me start from a) Tj T* 0 Tw (low-level approach first.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 128.2981 cm
-Q
-q
-1 0 0 1 62.69291 110.2981 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F3 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (15) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R366': class PDFStream
-366 0 obj
-% page stream
-<< /Length 5938 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 727.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL ($ python ishelve.py) Tj T* (no arguments passed, use .help to see the available commands) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 707.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (If you run ) Tj /F3 10 Tf (ishelve.py ) Tj /F1 10 Tf (with the option ) Tj /F3 10 Tf (.h ) Tj /F1 10 Tf (\(or any abbreviation of ) Tj /F3 10 Tf (.help) Tj /F1 10 Tf (\) you get:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 638.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 38 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ python ishelve.py .h) Tj T* (Commands: .help, .showall, .clear, .delete) Tj T* (<) Tj (param) Tj (>) Tj ( ...) Tj T* (<) Tj (param=value) Tj (>) Tj ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 618.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 369.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 240 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 218 Tm /F3 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 339.4236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (plac vs argparse) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 297.4236 cm
-q
-BT 1 0 0 1 0 26 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 291.4236 cm
-Q
-q
-1 0 0 1 62.69291 291.4236 cm
-Q
-q
-1 0 0 1 62.69291 219.4236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 57 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 62 Tm 2.69784 Tw 12 TL /F1 10 Tf 0 0 0 rg (plac does not support the destination concept: the destination coincides with the name of the) Tj T* 0 Tw .359983 Tw (argument, always. This restriction has some drawbacks. For instance, suppose you want to define a) Tj T* 0 Tw 2.758651 Tw (long option called ) Tj /F3 10 Tf (--yield) Tj /F1 10 Tf (. In this case the destination would be ) Tj /F3 10 Tf (yield) Tj /F1 10 Tf (, which is a Python) Tj T* 0 Tw 1.181235 Tw (keyword, and since you cannot introduce an argument with that name in a function definition, it is) Tj T* 0 Tw 2.12528 Tw (impossible to implement it. Your choices are to change the name of the long option, or to use) Tj T* 0 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (with a suitable destination.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 213.4236 cm
-Q
-q
-1 0 0 1 62.69291 165.4236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 33 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 38 Tm 1.120751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support "required options". As the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation puts it: ) Tj /F4 10 Tf (Required options) Tj T* 0 Tw 1.075318 Tw (are generally considered bad form - normal users expect options to be optional. You should avoid) Tj T* 0 Tw .874269 Tw (the use of required options whenever possible. ) Tj /F1 10 Tf (Notice that since ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (supports them, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can) Tj T* 0 Tw (manage them too, but not directly.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 159.4236 cm
-Q
-q
-1 0 0 1 62.69291 99.42362 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 45 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 50 Tm 1.539982 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (supports only regular boolean flags. ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (has the ability to define generalized two-value) Tj T* 0 Tw .361751 Tw (flags with values different from ) Tj /F3 10 Tf (True ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (False) Tj /F1 10 Tf (. An earlier version of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (had this feature too, but) Tj T* 0 Tw .814985 Tw (since you can use options with two choices instead, and in any case the conversion from ) Tj /F3 10 Tf ({True,) Tj T* 0 Tw .901984 Tw (False} ) Tj /F1 10 Tf (to any couple of values can be trivially implemented with a ternary operator \() Tj /F3 10 Tf (value1 if) Tj T* 0 Tw (flag else value2) Tj /F1 10 Tf (\), I have removed it \(KISS rules!\).) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 93.42362 cm
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (16) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R367': class PDFStream
-367 0 obj
-% page stream
-<< /Length 7614 >>
-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
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 21 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 26 Tm 1.797126 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support ) Tj /F3 10 Tf (nargs ) Tj /F1 10 Tf (options directly \(it uses them internally, though, to implement flag) Tj T* 0 Tw .90683 Tw (recognition\). The reason it that all the use cases of interest to me are covered by ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and did not) Tj T* 0 Tw (feel the need to increase the learning curve by adding direct support for ) Tj /F3 10 Tf (nargs) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 723.0236 cm
-Q
-q
-1 0 0 1 62.69291 699.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 14 Tm 2.111318 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does support subparsers, but you must read the ) Tj 0 0 .501961 rg (advanced usage document ) Tj 0 0 0 rg (to see how it) Tj T* 0 Tw (works.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 693.0236 cm
-Q
-q
-1 0 0 1 62.69291 657.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 21 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 26 Tm 1.111751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not support actions directly. This also looks like a feature too advanced for the goals of) Tj T* 0 Tw .406651 Tw 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Notice however that the ability to define your own annotation objects \(again, see the ) Tj 0 0 .501961 rg (advanced) Tj T* 0 Tw (usage document) Tj 0 0 0 rg (\) may mitigate the need for custom actions.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 657.0236 cm
-Q
-q
-1 0 0 1 62.69291 639.0236 cm
-q
-BT 1 0 0 1 0 2 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 597.0236 cm
-q
-BT 1 0 0 1 0 26 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 1.435976 Tw /F3 10 Tf ('==SUPPRESS==' ) Tj /F1 10 Tf (as help string \(or ) Tj /F3 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use ) Tj 0 0 .501961 rg (argparse.FileType) Tj T* 0 Tw 0 0 0 rg (directly.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 543.0236 cm
-q
-BT 1 0 0 1 0 38 Tm 1.639213 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is also possible to pass options to the underlying ) Tj /F3 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F3 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F3 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 473.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL (def main\(...\):) Tj T* ( pass) Tj T* T* (main.add_help = False) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 429.8236 cm
-q
-BT 1 0 0 1 0 26 Tm .239318 Tw 12 TL /F1 10 Tf 0 0 0 rg (disables the recognition of the help flag ) Tj /F3 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 399.8236 cm
-q
-BT 1 0 0 1 0 14 Tm 2.391235 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, by setting the ) Tj /F3 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 /F3 10 Tf (main ) Tj /F1 10 Tf (function is used as description\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 369.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 336.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 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL (main.prefix_chars='/-') Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 292.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .924198 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first prefix char \() Tj /F3 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 /F3 10 Tf (-) Tj /F1 10 Tf (\) is kept to keep the ) Tj /F3 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 /F3 10 Tf (ishelve ) Tj /F1 10 Tf (example.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 262.6236 cm
-q
-BT 1 0 0 1 0 14 Tm 7.709147 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is possible to access directly the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, by invoking the) Tj T* 0 Tw /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 169.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 62 Tm 12 TL /F3 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import plac) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print\(plac.parser_from\(main\)\) #doctest: +ELLIPSIS) Tj T* (ArgumentParser\(prog=...\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 125.4236 cm
-q
-BT 1 0 0 1 0 26 Tm 2.646905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (uses ) Tj /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (and adds the parser to the main function as an) Tj T* 0 Tw .982126 Tw (attribute. When ) Tj /F3 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 95.42362 cm
-q
-BT 1 0 0 1 0 14 Tm .982765 Tw 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F3 10 Tf (plac.parser_from ) Tj /F1 10 Tf (in the unit tests of the module, but regular users should not need to use it,) Tj T* 0 Tw (unless they want to access ) Tj /F4 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (17) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R368': class PDFStream
-368 0 obj
-% page stream
-<< /Length 8496 >>
-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 38 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 /F3 10 Tf (main ) Tj /F1 10 Tf (function for you. For simplicity, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not perform any magic except the) Tj T* 0 Tw (addition of the ) Tj /F3 10 Tf (.p ) Tj /F1 10 Tf (attribute.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 687.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (plac vs the rest of the world) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 645.0236 cm
-q
-BT 1 0 0 1 0 26 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 639.0236 cm
-Q
-q
-1 0 0 1 62.69291 639.0236 cm
-Q
-q
-1 0 0 1 62.69291 627.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (opterator ) Tj 0 0 0 rg (by Dusty Phillips) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 621.0236 cm
-Q
-q
-1 0 0 1 62.69291 609.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (CLIArgs ) Tj 0 0 0 rg (by Pavel Panchekha) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 603.0236 cm
-Q
-q
-1 0 0 1 62.69291 591.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 .501961 rg (commandline ) Tj 0 0 0 rg (by David Laban) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 591.0236 cm
-Q
-q
-1 0 0 1 62.69291 561.0236 cm
-q
-BT 1 0 0 1 0 14 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 519.0236 cm
-q
-BT 1 0 0 1 0 26 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 477.0236 cm
-q
-BT 1 0 0 1 0 26 Tm .622488 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (can also be used as a replacement of the ) Tj 0 0 .501961 rg (cmd ) Tj 0 0 0 rg (module in the standard library and as such it shares) Tj T* 0 Tw .377126 Tw (many features with the module ) Tj 0 0 .501961 rg (cmd2 ) Tj 0 0 0 rg (by Catherine Devlin. However, this is completely coincidental, since) Tj T* 0 Tw (I became aware of the ) Tj 0 0 .501961 rg (cmd2 ) Tj 0 0 0 rg (module only after writing ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 423.0236 cm
-q
-BT 1 0 0 1 0 38 Tm .449982 Tw 12 TL /F1 10 Tf 0 0 0 rg (Command-line argument parsers keep coming out; between the newcomers I will notice ) Tj 0 0 .501961 rg (marrow.script ) Tj 0 0 0 rg (by) Tj T* 0 Tw .30683 Tw (Alice Bevan-McGregor, which is quite similar to ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (in spirit, but does not rely on ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (at all. ) Tj 0 0 .501961 rg (Argh ) Tj 0 0 0 rg (by) Tj T* 0 Tw .600542 Tw (Andrey Mikhaylenko is also worth mentioning: it is also based on ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (, it came after ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (and I must) Tj T* 0 Tw (give credit to the author for the choice of the name, much funnier than plac!) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 393.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (The future) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 327.0236 cm
-q
-BT 1 0 0 1 0 50 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 285.0236 cm
-q
-BT 1 0 0 1 0 26 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 219.0236 cm
-q
-BT 1 0 0 1 0 50 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 /F3 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 /F3 10 Tf (plac_ext.py) Tj /F1 10 Tf (\), since they require Python 2.5 to work, whereas ) Tj /F3 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 189.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Trivia: the story behind the name) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 123.0236 cm
-q
-BT 1 0 0 1 0 50 Tm .979984 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (project started very humbly: I just wanted to make easy_installable my old ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw .565988 Tw (and to publish it on PyPI. The original name of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was optionparser and the idea behind it was to build) Tj T* 0 Tw .603735 Tw (an ) Tj 0 0 .501961 rg (OptionParser ) Tj 0 0 0 rg (object from the docstring of the module. However, before doing that, I decided to check) Tj T* 0 Tw .244198 Tw (out the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module, since I knew it was going into Python 2.7 and Python 2.7 was coming out. Soon) Tj T* 0 Tw (enough I realized two things:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 117.0236 cm
-Q
-q
-1 0 0 1 62.69291 117.0236 cm
-Q
-q
-1 0 0 1 62.69291 93.02362 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (1.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 14 Tm .103735 Tw 12 TL /F1 10 Tf 0 0 0 rg (the single greatest idea of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (was unifying the positional arguments and the options in a single) Tj T* 0 Tw (namespace object;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 88.86614 cm
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (18) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R369': class PDFStream
-369 0 obj
-% page stream
-<< /Length 6685 >>
-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
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F1 10 Tf 12 TL 1.66748 Tw (parsing the docstring was so old-fashioned, considering the existence of functions annotations in) Tj T* 0 Tw (Python 3.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 741.0236 cm
-Q
-q
-1 0 0 1 62.69291 687.0236 cm
-q
-BT 1 0 0 1 0 38 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 /F3 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 /F3 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 657.0236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.093876 Tw 12 TL /F1 10 Tf 0 0 0 rg (I made a research on PyPI and the name ) Tj /F4 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 627.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 597.0236 cm
-q
-BT 1 0 0 1 0 14 Tm .225542 Tw 12 TL /F1 10 Tf 0 0 0 rg (That concludes the section about the basic usage of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. You are now ready to read about the advanced) Tj T* 0 Tw (usage.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 564.0236 cm
-q
-BT 1 0 0 1 0 3.5 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Advanced usages of plac) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 534.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Introduction) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 492.0236 cm
-q
-BT 1 0 0 1 0 26 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 /F4 10 Tf (huge ) Tj /F1 10 Tf (domain of applications.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 450.0236 cm
-q
-BT 1 0 0 1 0 26 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 408.0236 cm
-q
-BT 1 0 0 1 0 26 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 342.0236 cm
-q
-BT 1 0 0 1 0 50 Tm .694104 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can easily replace the ) Tj /F3 10 Tf (cmd ) Tj /F1 10 Tf (module of the standard library and you could easily write an application) Tj T* 0 Tw 2.271751 Tw (like ) Tj 0 0 .501961 rg (twill ) Tj 0 0 0 rg (with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. Or you could use it to script your building procedure. ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (also supports parallel) Tj T* 0 Tw .907765 Tw (execution of multiple commands and can be used as task manager and monitor. It is also quite easy to) Tj T* 0 Tw 1.483488 Tw (build a GUI or a Web application on top of ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (. When speaking of things you can do with ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (, your) Tj T* 0 Tw (imagination is the only limit!) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 312.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (From scripts to interactive applications) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 258.0236 cm
-q
-BT 1 0 0 1 0 38 Tm 1.300751 Tw 12 TL /F1 10 Tf 0 0 0 rg (Command-line scripts have many advantages, but they are no substitute for interactive applications. In) Tj T* 0 Tw .088171 Tw (particular, if you have a script with a large startup time which must be run multiple times, it is best to turn it) Tj T* 0 Tw 4.582126 Tw (into an interactive application, so that the startup is performed only once. ) Tj /F3 10 Tf (plac ) Tj /F1 10 Tf (provides an) Tj T* 0 Tw /F3 10 Tf (Interpreter ) Tj /F1 10 Tf (class just for this purpose.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 228.0236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.293984 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F3 10 Tf (Interpreter ) Tj /F1 10 Tf (class wraps the main function of a script and provides an ) Tj /F3 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 198.0236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 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 92.82362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 96 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 74 Tm /F3 10 Tf 12 TL (# shelve_interpreter.py) Tj T* (import plac, ishelve) Tj T* T* (@plac.annotations\() Tj T* ( interactive=\('start interactive interface', 'flag'\),) Tj T* ( subcommands='the commands of the underlying ishelve interpreter'\)) Tj T* (def main\(interactive, *subcommands\):) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (19) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R370': class PDFStream
-370 0 obj
-% page stream
-<< /Length 3712 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 607.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 134 Tm /F3 10 Tf 12 TL ( """) Tj T* ( This script works both interactively and non-interactively.) Tj T* ( Use .help to see the internal commands.) Tj T* ( """) Tj T* ( if interactive:) Tj T* ( plac.Interpreter\(ishelve.main\).interact\(\)) Tj T* ( else:) Tj T* ( for out in plac.call\(ishelve.main, subcommands\):) Tj T* ( print\(out\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 563.8236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 10 Tf (-h/--help ) Tj /F1 10 Tf (flag whereas) Tj T* 0 Tw (the internal interface only recognizes the ) Tj /F3 10 Tf (.help ) Tj /F1 10 Tf (command:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 530.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 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL ($ python shelve_interpreter.py -h) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 353.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 168 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 146 Tm /F3 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* ( -interactive start interactive interface) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 333.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 288.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 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL ($ python shelve_interpreter.py .clear # non-interactive use) Tj T* (cleared the shelve) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 268.2236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is an usage session:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 91.02362 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 168 re B*
-Q
-q
-BT 1 0 0 1 0 146 Tm 12 TL /F3 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* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (20) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R371': class PDFStream
-371 0 obj
-% page stream
-<< /Length 4861 >>
-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
-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 98 Tm 12 TL /F3 10 Tf 0 0 0 rg (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 575.8236 cm
-q
-BT 1 0 0 1 0 50 Tm .256412 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F3 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 /F3 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 /F3 10 Tf (plac ) Tj /F1 10 Tf (also understands command abbreviations: in this example) Tj T* 0 Tw /F3 10 Tf (del ) Tj /F1 10 Tf (is an abbreviation for ) Tj /F3 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 /F3 10 Tf (NameError) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 545.8236 cm
-q
-BT 1 0 0 1 0 14 Tm .847045 Tw 12 TL /F1 10 Tf 0 0 0 rg (Finally I must notice that the ) Tj /F3 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 515.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Testing a plac application) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 485.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 467.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (In principle, one could write automatic tests for the ) Tj /F3 10 Tf (ishelve ) Tj /F1 10 Tf (application by using ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (directly:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 302.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 156 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 134 Tm /F3 10 Tf 12 TL (# test_ishelve.py) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( assert plac.call\(ishelve.main, ['.clear']\) == ['cleared the shelve']) Tj T* ( assert plac.call\(ishelve.main, ['a=1']\) == ['setting a=1']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['1']) Tj T* ( assert plac.call\(ishelve.main, ['.delete=a']\) == ['deleted a']) Tj T* ( assert plac.call\(ishelve.main, ['a']\) == ['a: not found']) Tj T* T* (if __name__ == '__main__':) Tj T* ( test\(\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 258.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .390651 Tw 12 TL /F1 10 Tf 0 0 0 rg (However, using ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (is not especially nice. The big issue is that ) Tj /F3 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 /F3 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 216.6236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 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 /F3 10 Tf (plac ) Tj /F1 10 Tf (offers a better testing support through the ) Tj /F3 10 Tf (check ) Tj /F1 10 Tf (method of ) Tj /F3 10 Tf (Interpreter ) Tj /F1 10 Tf (objects:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 99.42362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 86 Tm /F3 10 Tf 12 TL (# test_ishelve_more.py) Tj T* (from __future__ import with_statement) Tj T* (import plac, ishelve) Tj T* T* (def test\(\):) Tj T* ( with plac.Interpreter\(ishelve.main\) as i:) Tj T* ( i.check\('.clear', 'cleared the shelve'\)) Tj T* ( i.check\('a=1', 'setting a=1'\)) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (21) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R372': class PDFStream
-372 0 obj
-% page stream
-<< /Length 5767 >>
-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
-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 26 Tm /F3 10 Tf 12 TL ( i.check\('a', '1'\)) Tj T* ( i.check\('.delete=a', 'deleted a'\)) Tj T* ( i.check\('a', 'a: not found'\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 659.8236 cm
-q
-BT 1 0 0 1 0 38 Tm 6.299974 Tw 12 TL /F1 10 Tf 0 0 0 rg (The method ) Tj /F3 10 Tf (.check\(given_input, expected_output\) ) Tj /F1 10 Tf (works on strings and raises an) Tj T* 0 Tw .971318 Tw /F3 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 /F3 10 Tf (AssertionError ) Tj /F1 10 Tf (is catched by tools like ) Tj /F3 10 Tf (py.test ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (nosetests ) Tj /F1 10 Tf (and) Tj T* 0 Tw (actually ) Tj /F3 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 605.8236 cm
-q
-BT 1 0 0 1 0 38 Tm .239984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Interpreters offer a minor syntactic advantage with respect to calling ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (directly, but they offer a) Tj T* 0 Tw .96748 Tw /F4 10 Tf (major ) Tj /F1 10 Tf (semantic advantage when things go wrong \(read exceptions\): an ) Tj /F3 10 Tf (Interpreter ) Tj /F1 10 Tf (object internally) Tj T* 0 Tw 1.181318 Tw (invokes something like ) Tj /F3 10 Tf (plac.call) Tj /F1 10 Tf (, but it wraps all exceptions, so that ) Tj /F3 10 Tf (i.check ) Tj /F1 10 Tf (is guaranteed not to) Tj T* 0 Tw (raise any exception except ) Tj /F3 10 Tf (AssertionError) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 587.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (Even the ) Tj /F3 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 581.8236 cm
-Q
-q
-1 0 0 1 62.69291 569.8236 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL (i.check\('-cler', 'SystemExit: unrecognized arguments: -cler'\)) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 569.8236 cm
-Q
-q
-1 0 0 1 62.69291 551.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 509.8236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F3 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 /F3 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 479.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Plac easy tests) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 437.8236 cm
-q
-BT 1 0 0 1 0 26 Tm 1.517126 Tw 12 TL /F1 10 Tf 0 0 0 rg (Writing your tests in terms of ) Tj /F3 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 /F3 10 Tf (plac.call) Tj /F1 10 Tf (, but they are still too low-level for my taste. The ) Tj /F3 10 Tf (Interpreter ) Tj /F1 10 Tf (class provides) Tj T* 0 Tw (support for doctest-style tests, a.k.a. ) Tj /F4 10 Tf (plac easy tests) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 395.8236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 10 Tf (ishelve.placet ) Tj /F1 10 Tf (\(the ) Tj /F3 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 218.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 168 re B*
-Q
-q
-BT 1 0 0 1 0 146 Tm 12 TL /F3 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 162.6236 cm
-q
-BT 1 0 0 1 0 38 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 /F3 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 132.6236 cm
-q
-BT 1 0 0 1 0 14 Tm 2.419984 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can test ) Tj /F3 10 Tf (ishelve.placet ) Tj /F1 10 Tf (file by calling the ) Tj /F3 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (22) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R373': class PDFStream
-373 0 obj
-% page stream
-<< /Length 6371 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 728.2393 cm
-q
-q
-.988825 0 0 .988825 0 0 cm
-q
-1 0 0 1 6.6 6.674587 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 474 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL ($ python -c"import plac, ishelve) Tj T* (plac.Interpreter\(ishelve.main\).doctest\(open\('ishelve.placet'\), verbose=True\)") Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 684.2393 cm
-q
-BT 1 0 0 1 0 26 Tm 4.007109 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj /F3 10 Tf (Interpreter.doctests ) Tj /F1 10 Tf (invokes something like ) Tj /F3 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 642.2393 cm
-q
-BT 1 0 0 1 0 26 Tm .175868 Tw 12 TL /F1 10 Tf 0 0 0 rg (You should realize tha the easy tests supported by ) Tj /F3 10 Tf (plac ) Tj /F1 10 Tf (are ) Tj /F4 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 /F3 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 564.2393 cm
-q
-BT 1 0 0 1 0 62 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 /F3 10 Tf (#) Tj /F1 10 Tf (\), escape sequences and) Tj T* 0 Tw 1.50686 Tw (more. Look at the ) Tj 0 0 .501961 rg (shlex ) Tj 0 0 0 rg (documentation if you need to customize how the language is interpreted. For) Tj T* 0 Tw 2.794985 Tw (more flexibility, it is even possible to pass to the interpreter a custom split function with signature) Tj T* 0 Tw /F3 10 Tf (split\(line, commentchar\)) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 510.2393 cm
-q
-BT 1 0 0 1 0 38 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 /F3 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 480.2393 cm
-q
-BT 1 0 0 1 0 14 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 450.2393 cm
-q
-BT 1 0 0 1 0 14 Tm 1.447318 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is straighforward to integrate your ) Tj /F3 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 /F3 10 Tf (nose ) Tj /F1 10 Tf (or ) Tj /F3 10 Tf (py.test ) Tj /F1 10 Tf (as follow:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 263.0642 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 180 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 158 Tm /F3 10 Tf 12 TL (import os, shlex, plac) Tj T* T* (def test_doct\(\):) Tj T* ( """) Tj T* ( Find all the doctests in the current directory and run them with the) Tj T* ( corresponding plac interpreter \(the shebang rules!\)) Tj T* ( """) Tj T* ( placets = [f for f in os.listdir\('.'\) if f.endswith\('.placet'\)]) Tj T* ( for placet in placets:) Tj T* ( lines = list\(open\(placet\)\)) Tj T* ( assert lines[0].startswith\('#!'\), 'Missing or incorrect shebang line!') Tj T* ( firstline = lines[0][2:] # strip the shebang) Tj T* ( main = plac.import_main\(*shlex.split\(firstline\)\)) Tj T* ( yield plac.Interpreter\(main\).doctest, lines[1:]) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 183.0642 cm
-q
-BT 1 0 0 1 0 62 Tm 1.44811 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here you should notice that usage of ) Tj /F3 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 /F3 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 /F3 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 /F3 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 /F3 10 Tf (ImportError ) Tj /F1 10 Tf (is raised.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 153.0642 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Plac batch scripts) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 111.0642 cm
-q
-BT 1 0 0 1 0 26 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 /F3 10 Tf (.execute ) Tj /F1 10 Tf (method to perform just that.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (23) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R374': class PDFStream
-374 0 obj
-% page stream
-<< /Length 4903 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 705.0236 cm
-q
-BT 1 0 0 1 0 50 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 /F3 10 Tf (Interpreter.execute ) Tj /F1 10 Tf (makes sure that any error raised by ) Tj /F3 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 /F4 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 675.0236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (.dl ) Tj /F1 10 Tf (instead of ) Tj /F3 10 Tf (.del) Tj /F1 10 Tf (\):) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 569.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 96 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 74 Tm /F3 10 Tf 12 TL (#!ishelve.py) Tj T* (.clear ) Tj T* (a=1 b=2) Tj T* (.show) Tj T* (.del a) Tj T* (.dl b) Tj T* (.show) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 537.8236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (.dl: not found ) Tj /F1 10 Tf (at the ) Tj /F3 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 312.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 216 re B*
-Q
-q
-BT 1 0 0 1 0 194 Tm 12 TL /F3 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* (i) Tj (>) Tj ( .show) Tj T* (b=2) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 268.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .159988 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F3 10 Tf (verbose ) Tj /F1 10 Tf (flag is there to show the lines which are being interpreted \(prefixed by ) Tj /F3 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 /F3 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 238.6236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Implementing subcommands) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 196.6236 cm
-q
-BT 1 0 0 1 0 26 Tm 1.182485 Tw 12 TL /F1 10 Tf 0 0 0 rg (When I discussed the ) Tj /F3 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 154.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .89104 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is actually able to infer a set of subparsers from a generic container of commands. This is useful if) Tj T* 0 Tw 3.125814 Tw (you want to implement ) Tj /F4 10 Tf (subcommands ) Tj /F1 10 Tf (\(a familiar example of a command-line application featuring) Tj T* 0 Tw (subcommands is subversion\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 100.6236 cm
-q
-BT 1 0 0 1 0 38 Tm .015868 Tw 12 TL /F1 10 Tf 0 0 0 rg (Technically a container of commands is any object with a ) Tj /F3 10 Tf (.commands ) Tj /F1 10 Tf (attribute listing a set of functions or ) 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 /F3 10 Tf (__enter__/__exit__) Tj /F1 10 Tf (\) and dispatch hooks \() Tj /F3 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 ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (24) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R375': class PDFStream
-375 0 obj
-% page stream
-<< /Length 3734 >>
-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 2 Tm /F1 10 Tf 12 TL (commands.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 735.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (The shelve interface can be rewritten in an object-oriented way as follows:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 197.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 528 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 506 Tm /F3 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\):) Tj T* ( self.configfile = configfile or '~/conf.shelve') Tj T* ( self.fname = os.path.expanduser\(self.configfile\)) Tj T* ( self.__doc__ += '\\nOperating on %s.\\nUse 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* ( if name is None:) Tj T* ( yield 'deleting everything') Tj T* ( self.sh.clear\(\)) Tj T* ( else:) Tj T* ( yield 'deleting %s' % name) Tj T* ( del self.sh[name] # no error checking) Tj T* T* (main = ShelveInterface # useful for the tests) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.Interpreter.call\(ShelveInterface\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 105.8236 cm
-q
-BT 1 0 0 1 0 74 Tm .885366 Tw 12 TL /F3 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 /F3 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (__exit__ ) Tj /F1 10 Tf (methods, they are invoked in the right order \() Tj /F3 10 Tf (__enter__) Tj T* 0 Tw .23528 Tw /F1 10 Tf (before the interpreter loop starts and ) Tj /F3 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 /F3 10 Tf (__enter__ ) Tj /F1 10 Tf (and ) Tj /F3 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 /F3 10 Tf (show ) Tj /F1 10 Tf (and ) Tj /F3 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (25) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R376': class PDFStream
-376 0 obj
-% page stream
-<< /Length 4389 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 693.0236 cm
-q
-BT 1 0 0 1 0 62 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 2.099213 Tw (provided commands: ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (.last_tb) Tj /F1 10 Tf (. The ) Tj /F3 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 /F3 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 675.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 276.0679 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 504 re B*
-Q
-q
-BT 1 0 0 1 0 482 Tm 12 TL /F3 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* (Use help to see the available commands.) Tj T* (i) Tj (>) Tj ( help) Tj T* T* (special commands) Tj T* (================) Tj T* (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* (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 244.0679 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (verbose ) Tj /F1 10 Tf (flag to the) Tj T* 0 Tw /F3 10 Tf (Interpreter.interact ) Tj /F1 10 Tf (method.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 142.0679 cm
-q
-BT 1 0 0 1 0 86 Tm .046098 Tw 12 TL /F1 10 Tf 0 0 0 rg (CHANGED IN VERSION 0.9: if you have an old version of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (command must be prefixed with) Tj T* 0 Tw 1.096303 Tw (a dot, i.e. you must write ) Tj /F3 10 Tf (.help) Tj /F1 10 Tf (. The old behavior was more consistent in my opinion, since it made it) Tj T* 0 Tw .416412 Tw (clear that the ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (command was special and threated differently from the regular commands. However) Tj T* 0 Tw .011654 Tw (many users complained against the dot, so I changed it to make them happy. Starting from release 0.9 the) Tj T* 0 Tw .077209 Tw /F3 10 Tf (help ) Tj /F1 10 Tf (command is just an alias for ) Tj /F3 10 Tf (--help) Tj /F1 10 Tf (, in the sense that there is a preprocessor step replacing ) Tj /F3 10 Tf (help) Tj T* 0 Tw 2.914985 Tw /F1 10 Tf (with ) Tj /F3 10 Tf (--help ) Tj /F1 10 Tf (in the argument list. Notice that if you implement a custom ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (command in the) Tj T* 0 Tw .299269 Tw (commander class the preprocessor will be automatically disabled: passing ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (will call the custom help) Tj T* 0 Tw (command, just as you would expect.) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (26) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R377': class PDFStream
-377 0 obj
-% page stream
-<< /Length 4852 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 747.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (plac.Interpreter.call) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 705.0236 cm
-q
-BT 1 0 0 1 0 26 Tm .10104 Tw 12 TL /F1 10 Tf 0 0 0 rg (At the core of ) Tj /F3 10 Tf (plac ) Tj /F1 10 Tf (there is the ) Tj /F3 10 Tf (call ) Tj /F1 10 Tf (function which invokes a callable with the list of arguments passed) Tj T* 0 Tw 1.238443 Tw (at the command-line \() Tj /F3 10 Tf (sys.argv[1:]) Tj /F1 10 Tf (\). Thanks to ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (you can launch your module by simply) Tj T* 0 Tw (adding the lines:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 659.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 14 Tm /F3 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 603.8236 cm
-q
-BT 1 0 0 1 0 38 Tm .50436 Tw 12 TL /F1 10 Tf 0 0 0 rg (Everything works fine if ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (is a simple callable performing some action; however, in many cases, one) Tj T* 0 Tw .087633 Tw (has a ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf ("function" which is a actually a factory returning a command container object. For instance, in) Tj T* 0 Tw .573318 Tw (my second shelve example the main function is the class ) Tj /F3 10 Tf (ShelveInterface) Tj /F1 10 Tf (, and the two lines needed) Tj T* 0 Tw (to run the module are a bit ugly:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 558.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( plac.Interpreter\(plac.call\(ShelveInterface\)\).interact\(\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 454.6236 cm
-q
-BT 1 0 0 1 0 86 Tm .873988 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover, now the program runs, but only in interactive mode, i.e. it is not possible to run it as a script.) Tj T* 0 Tw .097882 Tw (Instead, it would be nice to be able to specify the command to execute on the command-line and have the) Tj T* 0 Tw 4.08229 Tw (interpreter start, execute the command and finish properly \(I mean by calling ) Tj /F3 10 Tf (__enter__ ) Tj /F1 10 Tf (and) Tj T* 0 Tw .100574 Tw /F3 10 Tf (__exit__) Tj /F1 10 Tf (\) without needing user input. Then the script could be called from a batch shell script working in) Tj T* 0 Tw 2.26816 Tw (the background. In order to provide such functionality ) Tj /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (provides a classmethod) Tj T* 0 Tw 1.173318 Tw (named ) Tj /F3 10 Tf (.call ) Tj /F1 10 Tf (which takes the factory, instantiates it with the arguments read from the command line,) Tj T* 0 Tw 1.517045 Tw (wraps the resulting container object as an interpreter and runs it with the rest arguments found in the) Tj T* 0 Tw (command line. Here is the code to turn the ) Tj /F3 10 Tf (ShelveInterface ) Tj /F1 10 Tf (into a script) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 373.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 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 50 Tm /F3 10 Tf 12 TL (# ishelve3.py) Tj T* (from ishelve2 import ShelveInterface as main) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.Interpreter.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 353.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (and here are a few examples of usage:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 140.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 204 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 182 Tm /F3 10 Tf 12 TL ($ python ishelve3.py -h) Tj T* (usage: ishelve3.py [-h] [-i] [-configfile CONFIGFILE] [args [args ...]]) Tj T* T* (positional arguments:) Tj T* ( args) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -i, --interact start interactive interpreter) Tj T* ( -configfile CONFIGFILE) Tj T* ( path name of the shelve) Tj T* T* ($ python ishelve3.py set a 1) Tj T* (setting a=1) Tj T* ($ python ishelve3.py show a) Tj T* (a = 1) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 108.2236 cm
-q
-BT 1 0 0 1 0 14 Tm .079989 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you pass the ) Tj /F3 10 Tf (-i ) Tj /F1 10 Tf (flag in the command line, then the script will enter in interactive mode and ask the user) Tj T* 0 Tw (for the commands to execute:) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (27) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R378': class PDFStream
-378 0 obj
-% page stream
-<< /Length 4660 >>
-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
-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 62 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ python ishelve3.py -i) Tj T* (A minimal interface over a shelve object.) Tj T* (Operating on /home/micheles/conf.shelve.) Tj T* (Use help to see the available commands.) Tj T* T* (i) Tj (>) 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 26 Tm .221417 Tw 12 TL /F1 10 Tf 0 0 0 rg (In a sense, I have closed the circle: at the beginning of this document I discussed how to turn a script into) Tj T* 0 Tw .784147 Tw (an interactive application \(the ) Tj /F3 10 Tf (shelve_interpreter.py ) Tj /F1 10 Tf (example\), whereas here I have show how to) Tj T* 0 Tw (turn an interactive application into a script.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 617.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (The complete signature of ) Tj /F3 10 Tf (plac.Interpreter.call ) Tj /F1 10 Tf (is the following:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 560.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 48 re B*
-Q
-q
-BT 1 0 0 1 0 26 Tm 12 TL /F3 10 Tf 0 0 0 rg (call\(factory, arglist=sys.argv[1:],) Tj T* ( commentchar='#', split=shlex.split,) Tj T* ( stdin=sys.stdin, prompt='i) Tj (>) Tj ( ', verbose=False\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 480.6236 cm
-q
-BT 1 0 0 1 0 62 Tm 1.756651 Tw 12 TL /F1 10 Tf 0 0 0 rg (The factory must have a fixed number of positional arguments \(no default arguments, no varargs, no) Tj T* 0 Tw 1.87881 Tw (kwargs\), otherwise a ) Tj /F3 10 Tf (TypeError ) Tj /F1 10 Tf (is raised: the reason is that we want to be able to distinguish the) Tj T* 0 Tw .829984 Tw (command-line arguments needed to instantiate the factory from the rest arguments that must be sent to) Tj T* 0 Tw 2.609984 Tw (the corresponding interpreter object. It is also possible to specify a list of arguments different from) Tj T* 0 Tw .513318 Tw /F3 10 Tf (sys.argv[1:] ) Tj /F1 10 Tf (\(useful in tests\), the character to be recognized as a comment, the splitting function, the) Tj T* 0 Tw (input source and the prompt to use while in interactive mode, and a verbose flag.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 450.6236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Readline support) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 372.6236 cm
-q
-BT 1 0 0 1 0 62 Tm 1.022485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.6 ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (offers full readline support. That means that if your Python was compiled) Tj T* 0 Tw 2.120697 Tw (with readline support you get autocompletion and persistent command history for free. By default all) Tj T* 0 Tw .144104 Tw (commands are autocomplete in a case sensitive way. If you want to add new words to the autocompletion) Tj T* 0 Tw .116488 Tw (set, or you want to change the location of the ) Tj /F3 10 Tf (.history ) Tj /F1 10 Tf (file, or to change the case sensitivity, the way to) Tj T* 0 Tw .18436 Tw (go is to pass a ) Tj /F3 10 Tf (plac.ReadlineInput ) Tj /F1 10 Tf (object to the interpreter. Here is an example, assuming you want) Tj T* 0 Tw (to build a database interface understanding SQL commands:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 99.42362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 264 re B*
-Q
-q
-BT 1 0 0 1 0 242 Tm 12 TL /F3 10 Tf 0 0 0 rg (import os, plac) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (SQLKEYWORDS = set\(['help', 'select', 'from', ) Tj T* ( 'inner', 'join', 'outer', 'left', 'right']) Tj T* ( \) # and many others) Tj T* (DBTABLES = set\(['table1', 'table2']\) # you can read them from the db schema) Tj T* T* (COMPLETIONS = SQLKEYWORDS | DBTABLES) Tj T* T* (class SqlInterface\(object\):) Tj T* ( commands = ['SELECT']) Tj T* ( def __init__\(self, dsn\):) Tj T* ( self.soup = SqlSoup\(dsn\)) Tj T* ( def SELECT\(self, argstring\):) Tj T* ( sql = 'SELECT ' + argstring) Tj T* ( for row in self.soup.bind.execute\(sql\):) Tj T* ( yield str\(row\) # the formatting can be much improved) Tj T* T* (rl_input = plac.ReadlineInput\() Tj T* ( COMPLETIONS, histfile=os.path.expanduser\('~/.sql_interface.history'\), ) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (28) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R379': class PDFStream
-379 0 obj
-% page stream
-<< /Length 4519 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 655.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-BT 1 0 0 1 0 86 Tm 12 TL /F3 10 Tf 0 0 0 rg ( case_sensitive=False\)) Tj T* T* (def split_on_first_space\(line, commentchar\):) Tj T* ( return line.strip\(\).split\(' ', 1\) # ignoring comments) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( plac.Interpreter.call\(SqlInterface, split=split_on_first_space,) Tj T* ( stdin=rl_input, prompt='sql) Tj (>) Tj ( '\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 635.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is an example of usage:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 578.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
-Q
-q
-BT 1 0 0 1 0 26 Tm 12 TL /F3 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 498.6236 cm
-q
-BT 1 0 0 1 0 62 Tm 1.951318 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can check that entering just ) Tj /F3 10 Tf (sel ) Tj /F1 10 Tf (and pressing TAB the readline library completes the ) Tj /F3 10 Tf (SELECT) Tj T* 0 Tw .797356 Tw /F1 10 Tf (keyword for you and makes it upper case; idem for ) Tj /F3 10 Tf (FROM) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (INNER) Tj /F1 10 Tf (, ) Tj /F3 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 /F3 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 420.6236 cm
-q
-BT 1 0 0 1 0 62 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 ) Tj 0 0 .501961 rg (pyreadline ) Tj 0 0 0 rg (library) Tj T* 0 Tw .389989 Tw (\(I do not use Windows, so this part is very little tested: I tried it only once and it worked, but your mileage) Tj T* 0 Tw 2.206457 Tw (may vary\). For people worried about licenses, I will notice that ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses the readline library only if) Tj T* 0 Tw .591894 Tw (available, it does not include it and it does not rely on it in any fundamental way, so that the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (licence) Tj T* 0 Tw (does not need to be the GPL \(actually it is a BSD do-whatever-you-want-with-it licence\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 378.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .187882 Tw 12 TL /F1 10 Tf 0 0 0 rg (The interactive mode of ) Tj /F3 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 3.130651 Tw (is actually better than ) Tj 0 0 .501961 rg (cmd) Tj 0 0 0 rg (: for instance, the ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (command is more powerful, since it provides) Tj T* 0 Tw (information about the arguments accepted by the given command:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 93.42362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 276 re B*
-Q
-q
-BT 1 0 0 1 0 254 Tm 12 TL /F3 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 [None]) Tj T* T* (i) Tj (>) Tj ( help show) Tj T* (usage: show [names [names ...]]) Tj T* T* (show given parameters) Tj T* T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (29) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R380': class PDFStream
-380 0 obj
-% page stream
-<< /Length 5976 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 727.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-BT 1 0 0 1 0 14 Tm 12 TL /F3 10 Tf 0 0 0 rg (positional arguments:) Tj T* ( names) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 671.8236 cm
-q
-BT 1 0 0 1 0 38 Tm 1.959985 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you can imagine, the help message is provided by the underlying ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (subparser \(there is a) Tj T* 0 Tw 2.954524 Tw (subparser for each command\). ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (commands accept options, flags, varargs, keyword arguments,) Tj T* 0 Tw .719318 Tw (arguments with defaults, arguments with a fixed number of choices, type conversion and all the features) Tj T* 0 Tw (provided of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (which should be reimplemented from scratch using ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 641.8236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.78248 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover at the moment ) Tj /F3 10 Tf (plac ) Tj /F1 10 Tf (also understands command abbreviations. However, this feature may) Tj T* 0 Tw (disappear in future releases. It was meaningful in the past, when ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (did not support readline.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 623.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that if an abbreviation is ambiguous, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (warns you:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 578.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-BT 1 0 0 1 0 14 Tm 12 TL /F3 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 548.6236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (The plac runner) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 482.6236 cm
-q
-BT 1 0 0 1 0 50 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 /F3 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 /F3 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 /F3 10 Tf (.plac ) Tj /F1 10 Tf (scripts and ) Tj /F3 10 Tf (.placet ) Tj /F1 10 Tf (files, as well) Tj T* 0 Tw 1.47311 Tw (as Python modules containg a ) Tj /F3 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 452.6236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 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 383.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL (#!ishelve2.py:ShelveInterface -c ~/conf.shelve) Tj T* (set a 1) Tj T* (del a) Tj T* (del a # intentional error) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 315.4236 cm
-q
-BT 1 0 0 1 0 50 Tm .575868 Tw 12 TL /F1 10 Tf 0 0 0 rg (The first line of the ) Tj /F3 10 Tf (.plac ) Tj /F1 10 Tf (script contains the name of the python module containing the plac interpreter) Tj T* 0 Tw 2.327209 Tw (and the arguments which must be passed to its main function in order to be able to instantiate an) Tj T* 0 Tw .202485 Tw (interpreter object. In this case I appended ) Tj /F3 10 Tf (:ShelveInterface ) Tj /F1 10 Tf (to the name of the module to specify the) Tj T* 0 Tw 1.030574 Tw (object that must be imported: if not specified, by default the object named 'main' is imported. The other) Tj T* 0 Tw (lines contains commands. You can run the script as follows:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 226.2505 cm
-q
-q
-.952737 0 0 .952737 0 0 cm
-q
-1 0 0 1 6.6 6.927412 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 492 84 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 62 Tm /F3 10 Tf 12 TL ($ plac_runner.py --batch ishelve2.plac) Tj T* (setting a=1) Tj T* (deleting a) Tj T* (Traceback \(most recent call last\):) Tj T* ( ...) Tj T* (_bsddb.DBNotFoundError: \(-30988, 'DB_NOTFOUND: No matching key/data pair found'\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 194.2505 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 164.2505 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 158.2505 cm
-Q
-q
-1 0 0 1 62.69291 146.2505 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL (alias plac="plac_runner.py") Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 146.2505 cm
-Q
-q
-1 0 0 1 62.69291 116.2505 cm
-q
-BT 1 0 0 1 0 14 Tm 2.955318 Tw 12 TL /F1 10 Tf 0 0 0 rg (\(or you define a suitable ) Tj /F3 10 Tf (plac.bat ) Tj /F1 10 Tf (script in Windows\) you can run the ) Tj /F3 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (30) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R381': class PDFStream
-381 0 obj
-% page stream
-<< /Length 4819 >>
-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
-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 168 re B*
-Q
-q
-BT 1 0 0 1 0 146 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ plac -i ishelve2.py:ShelveInterface) 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 575.8236 cm
-q
-BT 1 0 0 1 0 2 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 /F3 10 Tf (.placet ) Tj /F1 10 Tf (file like the following:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 446.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 98 Tm 12 TL /F3 10 Tf 0 0 0 rg (#!ishelve2.py:ShelveInterface -configfile=~/test.shelve) Tj T* (i) Tj (>) Tj ( del) Tj T* (deleting everything) Tj T* (i) Tj (>) Tj ( set a 1) Tj T* (setting a=1) Tj T* (i) Tj (>) Tj ( set b 2) Tj T* (setting b=2) Tj T* (i) Tj (>) Tj ( show a) Tj T* (a = 1) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 414.6236 cm
-q
-BT 1 0 0 1 0 14 Tm 2.145697 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the first line specifies a test database ) Tj /F3 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 396.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 351.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL ($ plac --test ishelve2.placet) Tj T* (run 1 plac test\(s\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 307.4236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 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 /F4 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 277.4236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 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 244.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 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL ($ find . -name \\*.placet | xargs plac_runner.py -t) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 212.2236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 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 194.2236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 188.2236 cm
-Q
-q
-1 0 0 1 62.69291 176.2236 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL ($ plac module.py args ...) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 176.2236 cm
-Q
-q
-1 0 0 1 62.69291 158.2236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is an example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 89.02362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL ($ plac ishelve.py a=1) Tj T* (setting a=1) Tj T* ($ plac ishelve.py .show) Tj T* (a=1) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (31) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R382': class PDFStream
-382 0 obj
-% page stream
-<< /Length 3928 >>
-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 14 Tm .01561 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that in non-interactive mode the runner just invokes ) Tj /F3 10 Tf (plac.call ) Tj /F1 10 Tf (on the ) Tj /F3 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 711.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (A non class-based example) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 669.0236 cm
-q
-BT 1 0 0 1 0 26 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 /F3 10 Tf (.commands ) Tj /F1 10 Tf (attribute and possibly ) Tj /F3 10 Tf (__enter__) Tj T* 0 Tw /F1 10 Tf (and/or ) Tj /F3 10 Tf (__exit__ ) Tj /F1 10 Tf (attributes.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 639.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 257.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 372 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 350 Tm /F3 10 Tf 12 TL ("A Fake Version Control System") Tj T* T* (import plac) Tj T* T* (commands = 'checkout', 'commit', 'status') Tj T* T* (@plac.annotations\(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 213.8236 cm
-q
-BT 1 0 0 1 0 26 Tm .431318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that I have defined both an ) Tj /F3 10 Tf (__exit__ ) Tj /F1 10 Tf (hook and a ) Tj /F3 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 /F3 10 Tf (main = __import__\(__name__\)) Tj /F1 10 Tf (, which define ) Tj /F3 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 183.8236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.259986 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F3 10 Tf (vcs ) Tj /F1 10 Tf (module does not contain an ) Tj /F3 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 /F3 10 Tf (plac vcs.py -h) Tj /F1 10 Tf (\):) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 90.62362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 84 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 62 Tm /F3 10 Tf 12 TL (usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...) Tj T* T* (A Fake Version Control System) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (32) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R383': class PDFStream
-383 0 obj
-% page stream
-<< /Length 3250 >>
-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
-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 62 Tm /F3 10 Tf 12 TL T* (subcommands:) Tj T* ( {status,commit,checkout}) Tj T* ( checkout A fake checkout command) Tj T* ( commit A fake commit command) Tj T* ( status A fake status command) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 659.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can get help for the subcommands by postponing ) Tj /F3 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 542.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 86 Tm /F3 10 Tf 12 TL ($ plac vcs.py status -h) Tj T* (usage: vcs.py status [-h] [-q]) Tj T* T* (A fake status command) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -q, --quiet summary information) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 510.6236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (-q) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 492.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here is an example of a non-interactive session:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 363.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 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 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 343.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (and here is an interactive session:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 142.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 192 re B*
-Q
-q
-BT 1 0 0 1 0 170 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ plac -i vcs.py) Tj T* (usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...) Tj T* (i) Tj (>) Tj ( check url) Tj T* (checkout) Tj T* (url) Tj T* (i) Tj (>) Tj ( st -q) Tj T* (status) Tj T* (True) Tj T* (i) Tj (>) Tj ( co) Tj T* (commit) Tj T* (None) Tj T* (i) Tj (>) Tj ( sto) Tj T* (Command 'sto' does not exist) Tj T* (i) Tj (>) Tj ( [CTRL-D]) Tj T* (ok) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 110.2236 cm
-q
-BT 1 0 0 1 0 14 Tm 2.986905 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the invocation of the ) Tj /F3 10 Tf (__missing__ ) Tj /F1 10 Tf (hook for non-existing commands. Notice also that the) Tj T* 0 Tw /F3 10 Tf (__exit__ ) Tj /F1 10 Tf (hook gets called only in interactive mode.) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (33) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R384': class PDFStream
-384 0 obj
-% page stream
-<< /Length 5749 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 741.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 711.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Writing your own plac runner) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 657.0236 cm
-q
-BT 1 0 0 1 0 38 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 /F3 10 Tf (Interpreter ) Tj /F1 10 Tf (class is the ) Tj /F3 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 /F3 10 Tf (.str) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (.etype) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (.exc ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (.tb) Tj /F1 10 Tf (:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 651.0236 cm
-Q
-q
-1 0 0 1 62.69291 651.0236 cm
-Q
-q
-1 0 0 1 62.69291 639.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F3 10 Tf 0 0 0 rg (.str ) Tj /F1 10 Tf (is the output of the command, if successful \(a string\);) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 633.0236 cm
-Q
-q
-1 0 0 1 62.69291 621.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F3 10 Tf 0 0 0 rg (.etype ) Tj /F1 10 Tf (is the class of the exception, if the command fail;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 615.0236 cm
-Q
-q
-1 0 0 1 62.69291 603.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F3 10 Tf 0 0 0 rg (.exc ) Tj /F1 10 Tf (is the exception instance;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 597.0236 cm
-Q
-q
-1 0 0 1 62.69291 585.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F3 10 Tf 0 0 0 rg (.tb ) Tj /F1 10 Tf (is the traceback.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 585.0236 cm
-Q
-q
-1 0 0 1 62.69291 543.0236 cm
-q
-BT 1 0 0 1 0 26 Tm .937485 Tw 12 TL /F1 10 Tf 0 0 0 rg (Moreover the ) Tj /F3 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 525.0236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (For instance, if you send a mispelled option to the interpreter a ) Tj /F3 10 Tf (SystemExit ) Tj /F1 10 Tf (will be trapped:) 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 84 re B*
-Q
-q
-BT 1 0 0 1 0 62 Tm 12 TL /F3 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 399.8236 cm
-q
-BT 1 0 0 1 0 14 Tm 2.90561 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is important to invoke the ) Tj /F3 10 Tf (.send ) Tj /F1 10 Tf (method inside the context manager, otherwise you will get a) Tj T* 0 Tw /F3 10 Tf (RuntimeError) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 357.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 26 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 120.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 228 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 206 Tm /F3 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 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (34) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R385': class PDFStream
-385 0 obj
-% page stream
-<< /Length 4478 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 741.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 711.0236 cm
-q
-BT 1 0 0 1 0 14 Tm .259988 Tw 12 TL /F1 10 Tf 0 0 0 rg (An example of GUI program built on top of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is given later on, in the paragraph ) Tj /F4 10 Tf (Managing the output of) Tj T* 0 Tw (concurrent commands ) Tj /F1 10 Tf (\(using Tkinter for simplicity and portability\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 657.0236 cm
-q
-BT 1 0 0 1 0 38 Tm 2.090651 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is a final ) Tj /F4 10 Tf (caveat) Tj /F1 10 Tf (: since the plac interpreter loop is implemented via extended generators, plac) Tj T* 0 Tw .988651 Tw (interpreters are single threaded: you will get an error if you ) Tj /F3 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 599.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 48 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 26 Tm /F3 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 567.8236 cm
-q
-BT 1 0 0 1 0 14 Tm .106098 Tw 12 TL /F1 10 Tf 0 0 0 rg (The same trick also work for processes; you could run the interpreter loop in a separate process and send) Tj T* 0 Tw (commands to it via the Queue class provided by the ) Tj 0 0 .501961 rg (multiprocessing ) Tj 0 0 0 rg (module.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 537.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Long running commands) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 495.8236 cm
-q
-BT 1 0 0 1 0 26 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 234.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 252 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 230 Tm /F3 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.call\(FakeImporter\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 202.6236 cm
-q
-BT 1 0 0 1 0 14 Tm 1.466457 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you run the ) Tj /F3 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 97.42362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 96 re B*
-Q
-q
-BT 1 0 0 1 0 74 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ python importer1.py dsn -i) Tj T* (A fake importer with an import_file command) Tj T* (i) Tj (>) Tj ( import_file file1) Tj T* (... ) Tj (<) Tj (wait 3+ minutes) Tj (>) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) Tj T* (Imported 300 lines) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (35) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R386': class PDFStream
-386 0 obj
-% page stream
-<< /Length 5336 >>
-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
-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
-BT 1 0 0 1 0 26 Tm 12 TL /F3 10 Tf 0 0 0 rg (...) 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 671.8236 cm
-q
-BT 1 0 0 1 0 26 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 641.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Threaded commands) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 611.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 605.8236 cm
-Q
-q
-1 0 0 1 62.69291 593.8236 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL (commands = ['import_file']) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 593.8236 cm
-Q
-q
-1 0 0 1 62.69291 575.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (with) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 569.8236 cm
-Q
-q
-1 0 0 1 62.69291 557.8236 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL (thcommands = ['import_file']) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 557.8236 cm
-Q
-q
-1 0 0 1 62.69291 527.8236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 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 482.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-BT 1 0 0 1 0 14 Tm 12 TL /F3 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 450.6236 cm
-q
-BT 1 0 0 1 0 14 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 /F3 10 Tf (.output) Tj /F1 10 Tf (:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 381.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-BT 1 0 0 1 0 38 Tm 12 TL /F3 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 361.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 268.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 84 re B*
-Q
-q
-BT 1 0 0 1 0 62 Tm 12 TL /F3 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 248.2236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 203.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 36 re B*
-Q
-q
-BT 1 0 0 1 0 14 Tm 12 TL /F3 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 171.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F1 10 Tf 12 TL .819573 Tw (It is possible to store the output of a task into a file, to be read later \(this is useful for tasks with a large) Tj T* 0 Tw (output\):) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 125.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 14 Tm 12 TL /F3 10 Tf 0 0 0 rg (i) Tj (>) Tj ( .output 1 /tmp/out.txt) Tj T* (saved output of 1 into /tmp/out.txt) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 93.82362 cm
-q
-BT 1 0 0 1 0 14 Tm 1.045868 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can even skip the number argument: then ) Tj /F3 10 Tf (.output ) Tj /F1 10 Tf (will the return the output of the last launched) Tj T* 0 Tw (command \(the special commands like .output do not count\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (36) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R387': class PDFStream
-387 0 obj
-% page stream
-<< /Length 4197 >>
-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 2 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 683.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-BT 1 0 0 1 0 38 Tm 12 TL /F3 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 663.8236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj /F3 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 606.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 48 re B*
-Q
-q
-BT 1 0 0 1 0 26 Tm 12 TL /F3 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 586.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 493.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 62 Tm 12 TL /F3 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 401.4236 cm
-q
-BT 1 0 0 1 0 74 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 /F3 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 /F3 10 Tf (TOBEKILLED) Tj /F1 10 Tf (. Internally the generator corresponding to the) Tj T* 0 Tw .632927 Tw (command is executed in the thread and the status is checked at each iteration: when the status become) Tj T* 0 Tw 2.578443 Tw /F3 10 Tf (TOBEKILLED ) Tj /F1 10 Tf (a ) Tj /F3 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 /F3 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 116.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 276 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 254 Tm /F3 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.call\(FakeImporter\)) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (37) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R388': class PDFStream
-388 0 obj
-% page stream
-<< /Length 5986 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 747.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Running commands as external processes) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 693.0236 cm
-q
-BT 1 0 0 1 0 38 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 675.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 669.0236 cm
-Q
-q
-1 0 0 1 62.69291 657.0236 cm
-0 0 0 rg
-BT /F1 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 2 Tm /F3 10 Tf 12 TL (thcommands = ['import_file']) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 657.0236 cm
-Q
-q
-1 0 0 1 62.69291 639.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (with) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 633.0236 cm
-Q
-q
-1 0 0 1 62.69291 621.0236 cm
-0 0 0 rg
-BT /F1 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 2 Tm 12 TL /F3 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 621.0236 cm
-Q
-q
-1 0 0 1 62.69291 591.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 449.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 132 re B*
-Q
-q
-BT 1 0 0 1 0 110 Tm 12 TL /F3 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 62.69291 393.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 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 339.8236 cm
-q
-BT 1 0 0 1 0 38 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 /F3 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 /F3 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 309.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 279.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Managing the output of concurrent commands) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 165.8236 cm
-q
-BT 1 0 0 1 0 98 Tm 1.895542 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (acts as a command-line task launcher and can be used as the base to build a GUI-based task) Tj T* 0 Tw .38561 Tw (launcher and task monitor. To this aim the interpreter class provides a ) Tj /F3 10 Tf (.submit ) Tj /F1 10 Tf (method which returns a) Tj T* 0 Tw 1.792339 Tw (task object and a ) Tj /F3 10 Tf (.tasks ) Tj /F1 10 Tf (method returning the list of all the tasks submitted to the interpreter. The) Tj T* 0 Tw .373516 Tw /F3 10 Tf (submit ) Tj /F1 10 Tf (method does not start the task and thus it is nonblocking. Each task has an ) Tj /F3 10 Tf (.outlist ) Tj /F1 10 Tf (attribute) Tj T* 0 Tw .106098 Tw (which is a list storing the value yielded by the generator underlying the task \(the ) Tj /F3 10 Tf (None ) Tj /F1 10 Tf (values are skipped) Tj T* 0 Tw .633318 Tw (though\): the ) Tj /F3 10 Tf (.outlist ) Tj /F1 10 Tf (grows as the task runs and more values are yielded. Accessing the ) Tj /F3 10 Tf (.outlist) Tj T* 0 Tw 1.051654 Tw /F1 10 Tf (is nonblocking and can be done freely. Finally there is a ) Tj /F3 10 Tf (.result ) Tj /F1 10 Tf (property which waits for the task to) Tj T* 0 Tw .830574 Tw (finish and returns the last yielded value or raises an exception. The code below provides an example of) Tj T* 0 Tw (how you could implement a GUI over the importer example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 96.62362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL (from __future__ import with_statement) Tj T* (from Tkinter import *) Tj T* (from importer3 import FakeImporter) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (38) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R389': class PDFStream
-389 0 obj
-% page stream
-<< /Length 3716 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 439.8236 cm
-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 324 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 302 Tm /F3 10 Tf 12 TL (def taskwidget\(root, task, tick=500\):) Tj T* ( "A Label widget showing the output of a task every 500 ms") Tj T* ( sv = StringVar\(root\)) Tj T* ( lb = Label\(root, textvariable=sv\)) Tj T* ( def show_outlist\(\):) Tj T* ( try:) Tj T* ( out = task.outlist[-1]) Tj T* ( except IndexError: # no output yet) Tj T* ( out = '') Tj T* ( sv.set\('%s %s' % \(task, out\)\)) Tj T* ( root.after\(tick, show_outlist\)) Tj T* ( root.after\(0, show_outlist\)) Tj T* ( return lb) Tj T* T* (def monitor\(tasks\):) Tj T* ( root = Tk\(\)) Tj T* ( for task in tasks:) Tj T* ( task.run\(\)) Tj T* ( taskwidget\(root, task\).pack\(\)) Tj T* ( root.mainloop\(\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac) Tj T* ( with plac.Interpreter\(plac.call\(FakeImporter\)\) as i:) Tj T* ( tasks = [i.submit\('import_file f1'\), i.submit\('import_file f2'\)]) Tj T* ( monitor\(tasks\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 409.8236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Monitor support) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 295.8236 cm
-q
-BT 1 0 0 1 0 98 Tm .493555 Tw 12 TL /F1 10 Tf 0 0 0 rg (Starting from release 0.8 ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides builtin support for monitoring the output of concurrent commands,) Tj T* 0 Tw 1.277318 Tw (at least for platforms where multiprocessing is fully supported. You can define your own monitor class,) Tj T* 0 Tw .839979 Tw (simply by inheriting from ) Tj /F3 10 Tf (plac.Monitor ) Tj /F1 10 Tf (and by overriding the methods ) Tj /F3 10 Tf (add_listener\(self, no\)) Tj /F1 10 Tf (,) Tj T* 0 Tw 2.953953 Tw /F3 10 Tf (del_listener\(self, taskno\)) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (notify_listener\(self, taskno, msg\)) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (schedule\(self,) Tj T* 0 Tw .367674 Tw (seconds, func, arg\) ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (run\(self\)) Tj /F1 10 Tf (. Then, you can a monitor object to any ) Tj /F3 10 Tf (plac.Interpreter) Tj T* 0 Tw 1.149983 Tw /F1 10 Tf (object by simply calling the ) Tj /F3 10 Tf (add_monitor ) Tj /F1 10 Tf (method. For convenience, ) Tj /F3 10 Tf (plac ) Tj /F1 10 Tf (comes with a very simple) Tj T* 0 Tw .82683 Tw /F3 10 Tf (TkMonitor ) Tj /F1 10 Tf (based on Tkinter \(I chose Tkinter because it is easy to use and it is in the standard library,) Tj T* 0 Tw .08332 Tw (but you can use any GUI\): you can just look at how the ) Tj /F3 10 Tf (TkMonitor ) Tj /F1 10 Tf (is implemented in ) Tj /F3 10 Tf (plac_tk.py ) Tj /F1 10 Tf (and) Tj T* 0 Tw (adapt it. Here is an example of usage of the ) Tj /F3 10 Tf (TkMonitor) Tj /F1 10 Tf (:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 118.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 168 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 146 Tm /F3 10 Tf 12 TL (from __future__ import with_statement) Tj T* (import plac) Tj T* T* (class Hello\(object\):) Tj T* ( mpcommands = ['hello']) Tj T* ( def hello\(self\):) Tj T* ( yield 'hello') Tj T* T* (if __name__ == '__main__':) Tj T* ( i = plac.Interpreter\(Hello\(\)\)) Tj T* ( i.add_monitor\(plac.TkMonitor\('tkmon'\)\)) Tj T* ( with i:) Tj T* ( i.interact\(\)) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (39) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R390': class PDFStream
-390 0 obj
-% page stream
-<< /Length 4914 >>
-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 26 Tm 1.657633 Tw 12 TL /F1 10 Tf 0 0 0 rg (Try to give the ) Tj /F3 10 Tf (hello ) Tj /F1 10 Tf (command from the interactive interpreter: each time a new text widget will be) Tj T* 0 Tw 1.321235 Tw (added displaying the output of the command. Notice that if ) Tj /F3 10 Tf (Tkinter ) Tj /F1 10 Tf (is not installed correctly on your) Tj T* 0 Tw (system the ) Tj /F3 10 Tf (TkMonitor ) Tj /F1 10 Tf (class will not be available.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 699.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Parallel computing with plac) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 585.0236 cm
-q
-BT 1 0 0 1 0 98 Tm 1.174751 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is certainly not intended as a tool for parallel computing, but still you can use it to launch a set of) Tj T* 0 Tw .497984 Tw (commands and to collect the results, similarly to the MapReduce pattern popularized by Google. In order) Tj T* 0 Tw .669431 Tw (to give an example, I will consider the "Hello World" of parallel computing, i.e. the computation of pi with) Tj T* 0 Tw .537633 Tw (independent processes. There is a huge number of algorithms to compute pi; here I will describe a trivial) Tj T* 0 Tw .101567 Tw (one chosen for simplicity, not per efficienty. The trick is to consider the first quadrant of a circle with radius) Tj T* 0 Tw .602488 Tw (1 and to extract a number of points ) Tj /F3 10 Tf (\(x, y\) ) Tj /F1 10 Tf (with ) Tj /F3 10 Tf (x ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (y ) Tj /F1 10 Tf (random variables in the interval ) Tj /F3 10 Tf ([0,1]) Tj /F1 10 Tf (. The) Tj T* 0 Tw .928876 Tw (probability of extracting a number inside the quadrant \(i.e. with ) Tj /F3 10 Tf (x^2 + y^2 < 1) Tj /F1 10 Tf (\) is proportional to the) Tj T* 0 Tw .433145 Tw (area of the quadrant \(i.e. ) Tj /F3 10 Tf (pi/4) Tj /F1 10 Tf (\). The value of ) Tj /F3 10 Tf (pi ) Tj /F1 10 Tf (therefore can be extracted by multiplying by 4 the ratio) Tj T* 0 Tw (between the number of points in the quadrant versus the total number of points ) Tj /F3 10 Tf (N) Tj /F1 10 Tf (, for ) Tj /F3 10 Tf (N ) Tj /F1 10 Tf (large:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 479.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 96 re B*
-Q
-q
-BT 1 0 0 1 0 74 Tm 12 TL /F3 10 Tf 0 0 0 rg (def calc_pi\(N\):) Tj T* ( inside = 0) Tj T* ( for j in xrange\(N\):) Tj T* ( x, y = random\(\), random\(\)) Tj T* ( if x*x + y*y ) Tj (<) Tj ( 1:) Tj T* ( inside += 1) Tj T* ( return \(4.0 * inside\) / N) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 411.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 50 Tm /F1 10 Tf 12 TL .046654 Tw (The algorithm is trivially parallelizable: if you have n CPUs, you can compute pi n times with N/n iterations,) Tj T* 0 Tw 1.122488 Tw (sum the results and divide the total by n. I have a Macbook with two cores, therefore I would expect a) Tj T* 0 Tw 2.347984 Tw (speedup factor of 2 with respect to a sequential computation. Moreover, I would expect a threaded) Tj T* 0 Tw 2.827984 Tw (computation to be even slower than a sequential computation, due to the GIL and the scheduling) Tj T* 0 Tw (overhead.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 381.8236 cm
-q
-BT 1 0 0 1 0 14 Tm .313984 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a script implementing the algorithm and working in three different modes \(parallel mode, threaded) Tj T* 0 Tw (mode and sequential mode\) depending on a ) Tj /F3 10 Tf (mode ) Tj /F1 10 Tf (option:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 96.62362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 276 re B*
-Q
-q
-BT 1 0 0 1 0 254 Tm 12 TL /F3 10 Tf 0 0 0 rg (from __future__ import with_statement) Tj T* (from random import random) Tj T* (import multiprocessing) Tj T* (import plac) Tj T* T* (class PiCalculator\(object\):) Tj T* ( """Compute pi in parallel with threads or processes""") Tj T* ( ) Tj T* ( @plac.annotations\() Tj T* ( npoints=\('number of integration points', 'positional', None, int\),) Tj T* ( mode=\('sequential|parallel|threaded', 'option', 'm', str, 'SPT'\)\)) Tj T* ( def __init__\(self, npoints, mode='S'\):) Tj T* ( self.npoints = npoints) Tj T* ( if mode == 'P':) Tj T* ( self.mpcommands = ['calc_pi']) Tj T* ( elif mode == 'T':) Tj T* ( self.thcommands = ['calc_pi']) Tj T* ( elif mode == 'S':) Tj T* ( self.commands = ['calc_pi']) Tj T* ( self.n_cpu = multiprocessing.cpu_count\(\)) Tj T* ( ) Tj T* ( def submit_tasks\(self\):) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (40) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R391': class PDFStream
-391 0 obj
-% page stream
-<< /Length 3511 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 259.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 504 re B*
-Q
-q
-BT 1 0 0 1 0 482 Tm 12 TL /F3 10 Tf 0 0 0 rg ( self.i = plac.Interpreter\(self\).__enter__\(\) ) Tj T* ( return [self.i.submit\('calc_pi %d' % \(self.npoints / self.n_cpu\)\)) Tj T* ( for _ in range\(self.n_cpu\)]) Tj T* T* ( def close\(self\):) Tj T* ( self.i.close\(\)) Tj T* T* ( @plac.annotations\() Tj T* ( npoints=\('npoints', 'positional', None, int\)\)) Tj T* ( def calc_pi\(self, npoints\):) Tj T* ( counts = 0) Tj T* ( for j in xrange\(npoints\):) Tj T* ( n, r = divmod\(j, 1000000\)) Tj T* ( if r == 0:) Tj T* ( yield '%dM iterations' % n) Tj T* ( x, y = random\(\), random\(\)) Tj T* ( if x*x + y*y ) Tj (<) Tj ( 1:) Tj T* ( counts += 1) Tj T* ( yield \(4.0 * counts\)/npoints) Tj T* T* ( def run\(self\):) Tj T* ( tasks = self.i.tasks\(\)) Tj T* ( for t in tasks:) Tj T* ( t.run\(\)) Tj T* ( try:) Tj T* ( total = 0) Tj T* ( for task in tasks:) Tj T* ( total += task.result) Tj T* ( except: # the task was killed) Tj T* ( print tasks) Tj T* ( return) Tj T* ( return total / self.n_cpu) Tj T* T* (if __name__ == '__main__':) Tj T* ( pc = plac.call\(PiCalculator\)) Tj T* ( pc.submit_tasks\(\)) Tj T* ( try:) Tj T* ( import time; t0 = time.time\(\)) Tj T* ( print '%f in %f seconds ' % \(pc.run\(\), time.time\(\) - t0\)) Tj T* ( finally:) Tj T* ( pc.close\(\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 179.8236 cm
-q
-BT 1 0 0 1 0 62 Tm .381797 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice the ) Tj /F3 10 Tf (submit_tasks ) Tj /F1 10 Tf (method, which instantiates and initializes a ) Tj /F3 10 Tf (plac.Interpreter ) Tj /F1 10 Tf (object and) Tj T* 0 Tw 3.38152 Tw (submits a number of commands corresponding to the number of available CPUs. The ) Tj /F3 10 Tf (calc_pi) Tj T* 0 Tw 3.796651 Tw /F1 10 Tf (command yield a log message every million of interactions, just to monitor the progress of the) Tj T* 0 Tw 1.751318 Tw (computation. The ) Tj /F3 10 Tf (run ) Tj /F1 10 Tf (method starts all the submitted commands in parallel and sums the results. It) Tj T* 0 Tw 1.17104 Tw (returns the average value of ) Tj /F3 10 Tf (pi ) Tj /F1 10 Tf (after the slowest CPU has finished its job \(if the CPUs are equal and) Tj T* 0 Tw (equally busy they should finish more or less at the same time\).) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 161.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL (Here are the results on my old Macbook with Ubuntu 10.04 and Python 2.6, for 10 million of iterations:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 92.62362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL ($ python picalculator.py -mP 10000000 # two processes) Tj T* (3.141904 in 5.744545 seconds) Tj T* ($ python picalculator.py -mT 10000000 # two threads) Tj T* (3.141272 in 13.875645 seconds) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (41) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R392': class PDFStream
-392 0 obj
-% page stream
-<< /Length 5158 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 727.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F3 10 Tf 12 TL ($ python picalculator.py -mS 10000000 # sequential) Tj T* (3.141586 in 11.353841 seconds) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 695.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 Tm /F1 10 Tf 12 TL 1.711751 Tw (As you see using processes one gets a 2x speedup indeed, where the threaded mode is some 20%) Tj T* 0 Tw (slower than the sequential mode.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 629.8236 cm
-q
-BT 1 0 0 1 0 50 Tm .167765 Tw 12 TL /F1 10 Tf 0 0 0 rg (Since the pattern submit a bunch of tasks, starts them and collect the results is so common, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (provides) Tj T* 0 Tw 2.487251 Tw (an utility function ) Tj /F3 10 Tf (runp\(genseq, mode='p', monitors=\(\), start=True\) ) Tj /F1 10 Tf (to start a bunch a) Tj T* 0 Tw .598876 Tw (generators and return a list of task objects. By default ) Tj /F3 10 Tf (runp ) Tj /F1 10 Tf (use processes, but you can use threads by) Tj T* 0 Tw .225542 Tw (passing ) Tj /F3 10 Tf (mode='t') Tj /F1 10 Tf (. If you do not wont to start the tasks, you can say so \() Tj /F3 10 Tf (start=False) Tj /F1 10 Tf (\). With ) Tj /F3 10 Tf (runp ) Tj /F1 10 Tf (the) Tj T* 0 Tw (parallel pi calculation becomes a one-liner:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 597.2159 cm
-q
-q
-.976496 0 0 .976496 0 0 cm
-q
-1 0 0 1 6.6 6.758862 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 480 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F3 10 Tf 12 TL (sum\(task.result for task in plac.runp\(calc_pi\(N\) for i in range\(ncpus\)\)\)/ncpus) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 565.2159 cm
-q
-BT 1 0 0 1 0 14 Tm 1.05186 Tw 12 TL /F1 10 Tf 0 0 0 rg (The file ) Tj /F3 10 Tf (test_runp ) Tj /F1 10 Tf (in the ) Tj /F3 10 Tf (doc ) Tj /F1 10 Tf (directory of the plac distribution shows another couple of examples of) Tj T* 0 Tw (usage, including how to show the results of the running computation on a ) Tj /F3 10 Tf (TkMonitor) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 535.2159 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (The plac server) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 421.2159 cm
-q
-BT 1 0 0 1 0 98 Tm 1.258443 Tw 12 TL /F1 10 Tf 0 0 0 rg (A command-line oriented interface can be easily converted into a socket-based interface. Starting from) Tj T* 0 Tw .930574 Tw (release 0.7 plac features a builtin server which is able to accept commands from multiple clients and to) Tj T* 0 Tw .994692 Tw (execute them. The server works by instantiating a separate interpreter for each client, so that if a client) Tj T* 0 Tw 1.08784 Tw (interpreter dies for any reason the other interpreters keep working. To avoid external dependencies the) Tj T* 0 Tw .872209 Tw (server is based on the ) Tj /F3 10 Tf (asynchat ) Tj /F1 10 Tf (module in the standard library, but it would not be difficult to replace) Tj T* 0 Tw 2.386412 Tw (the server with a different one \(for instance, a Twisted server\). Since ) Tj /F3 10 Tf (asynchat) Tj /F1 10 Tf (-based servers are) Tj T* 0 Tw .755984 Tw (asynchronous, any blocking command in the interpreter should be run in a separated process or thread.) Tj T* 0 Tw 1.157633 Tw (The default port for the ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (server is 2199, and the command to signal end-of-connection is EOF. For) Tj T* 0 Tw (instance, here is how you could manage remote import on a database \(say a SQLite db\):) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 292.0159 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (import plac) Tj T* (from importer2 import FakeImporter) Tj T* T* (def main\(port=2199\):) Tj T* ( main = FakeImporter\('dsn'\)) Tj T* ( plac.Interpreter\(main\).start_server\(port\)) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 272.0159 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (You can connect to the server with ) Tj /F3 10 Tf (telnet ) Tj /F1 10 Tf (on port 2199, as follows:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 94.81593 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 168 re B*
-Q
-q
-BT 1 0 0 1 0 146 Tm 12 TL /F3 10 Tf 0 0 0 rg ($ telnet localhost 2199) Tj T* (Trying ::1...) Tj T* (Trying 127.0.0.1...) Tj T* (Connected to localhost.) Tj T* (Escape character is '^]'.) Tj T* (i) Tj (>) Tj ( import_file f1) Tj T* (i) Tj (>) Tj ( .list) Tj T* (<) Tj (ThreadedTask 1 [import_file f1] RUNNING) Tj (>) Tj T* (i) Tj (>) Tj ( .out) Tj T* (Imported 100 lines) Tj T* (Imported 200 lines) Tj T* (i) Tj (>) Tj ( EOF) Tj T* (Connection closed by foreign host.) 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (42) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R393': class PDFStream
-393 0 obj
-% page stream
-<< /Length 6758 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 747.0236 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Summary) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 705.0236 cm
-q
-BT 1 0 0 1 0 26 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 699.0236 cm
-Q
-q
-1 0 0 1 62.69291 699.0236 cm
-Q
-q
-1 0 0 1 62.69291 687.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (1.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you want to implement a command-line script, use ) Tj /F3 10 Tf (plac.call) Tj /F1 10 Tf (;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 681.0236 cm
-Q
-q
-1 0 0 1 62.69291 633.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 33 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (2.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 33 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (if you want to implement a command interpreter, use ) Tj /F3 10 Tf (plac.Interpreter) Tj /F1 10 Tf (:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 23 27 cm
-Q
-q
-1 0 0 1 23 27 cm
-Q
-q
-1 0 0 1 23 15 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (for an interactive interpreter, call the ) Tj /F3 10 Tf (.interact ) Tj /F1 10 Tf (method;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 23 9 cm
-Q
-q
-1 0 0 1 23 -3 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 -3 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 10.5 0 Td (\177) Tj T* -10.5 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (for an batch interpreter, call the ) Tj /F3 10 Tf (.execute ) Tj /F1 10 Tf (method;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 627.0236 cm
-Q
-q
-1 0 0 1 62.69291 603.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (3.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 14 Tm 5.126647 Tw 12 TL /F1 10 Tf 0 0 0 rg (for testing call the ) Tj /F3 10 Tf (Interpreter.check ) Tj /F1 10 Tf (method in the appropriate context or use the) Tj T* 0 Tw /F3 10 Tf (Interpreter.doctest ) Tj /F1 10 Tf (feature;) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 597.0236 cm
-Q
-q
-1 0 0 1 62.69291 573.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 14 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 /F3 10 Tf (Interpreter.send ) Tj /F1 10 Tf (method which) Tj T* 0 Tw (returns a \(finished\) ) Tj /F3 10 Tf (Task ) Tj /F1 10 Tf (object.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 567.0236 cm
-Q
-q
-1 0 0 1 62.69291 543.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 14 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 /F3 10 Tf (thcommands ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (mpcommands ) Tj /F1 10 Tf (respectively.) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 537.0236 cm
-Q
-q
-1 0 0 1 62.69291 513.0236 cm
-0 0 0 rg
-BT /F1 10 Tf 12 TL ET
-q
-1 0 0 1 6 9 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 5.66 0 Td (6.) Tj T* -5.66 0 Td ET
-Q
-Q
-q
-1 0 0 1 23 -3 cm
-q
-BT 1 0 0 1 0 14 Tm 2.171647 Tw 12 TL /F1 10 Tf 0 0 0 rg (the ) Tj /F3 10 Tf (.start_server ) Tj /F1 10 Tf (method starts an asynchronous server on the given port number \(default) Tj T* 0 Tw (2199\)) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 513.0236 cm
-Q
-q
-1 0 0 1 62.69291 495.0236 cm
-q
-BT 1 0 0 1 0 2 Tm 12 TL /F1 10 Tf 0 0 0 rg (Moreover, remember that ) Tj /F3 10 Tf (plac_runner.py ) Tj /F1 10 Tf (is your friend.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 466.6772 cm
-n 0 14.17323 m 469.8898 14.17323 l S
-Q
-q
-1 0 0 1 62.69291 436.6772 cm
-q
-BT 1 0 0 1 0 3 Tm 18 TL /F2 15 Tf 0 0 0 rg (Appendix: custom annotation objects) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 406.6772 cm
-q
-BT 1 0 0 1 0 14 Tm .578651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses an ) Tj /F3 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the tuples in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F3 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 376.6772 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 14 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 247.4772 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (# annotations.py) Tj T* (class Positional\(object\):) Tj T* ( def __init__\(self, help='', type=None, choices=None, metavar=None\):) Tj T* ( self.help = help) Tj T* ( self.kind = 'positional') Tj T* ( self.abbrev = None) Tj T* ( self.type = type) Tj T* ( self.choices = choices) Tj T* ( self.metavar = metavar) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 227.4772 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 98.27717 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (# example11.py) Tj T* (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* ( n=Positional\("This is a float", float\),) Tj T* ( rest=Positional\("Other arguments"\)\)) Tj T* (def main\(i, n, *rest\):) Tj T* 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 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (43) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R394': class PDFStream
-394 0 obj
-% page stream
-<< /Length 1709 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 703.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 38 Tm /F3 10 Tf 12 TL ( print\(i, n, rest\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 683.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 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 554.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 98 Tm /F3 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* 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 510.6236 cm
-q
-BT 1 0 0 1 0 26 Tm .713516 Tw 12 TL /F1 10 Tf 0 0 0 rg (You can go on and define ) Tj /F3 10 Tf (Option ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (Flag ) Tj /F1 10 Tf (classes, if you like. Using custom annotation objects you) Tj T* 0 Tw .17528 Tw (could do advanced things like extracting the annotations from a configuration file or from a database, but I) Tj T* 0 Tw (expect such use cases to be quite rare: the default mechanism should work pretty well for most users.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 56.69291 56.69291 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 2 Tm /F1 10 Tf 12 TL 235.3849 0 Td (44) Tj T* -235.3849 0 Td ET
-Q
-Q
-
-endstream
-endobj
-% 'R395': class PDFPageLabels
-395 0 obj
-% Document Root
-<< /Nums [ 0
- 396 0 R
- 1
- 397 0 R
- 2
- 398 0 R
- 3
- 399 0 R
- 4
- 400 0 R
- 5
- 401 0 R
- 6
- 402 0 R
- 7
- 403 0 R
- 8
- 404 0 R
- 9
- 405 0 R
- 10
- 406 0 R
- 11
- 407 0 R
- 12
- 408 0 R
- 13
- 409 0 R
- 14
- 410 0 R
- 15
- 411 0 R
- 16
- 412 0 R
- 17
- 413 0 R
- 18
- 414 0 R
- 19
- 415 0 R
- 20
- 416 0 R
- 21
- 417 0 R
- 22
- 418 0 R
- 23
- 419 0 R
- 24
- 420 0 R
- 25
- 421 0 R
- 26
- 422 0 R
- 27
- 423 0 R
- 28
- 424 0 R
- 29
- 425 0 R
- 30
- 426 0 R
- 31
- 427 0 R
- 32
- 428 0 R
- 33
- 429 0 R
- 34
- 430 0 R
- 35
- 431 0 R
- 36
- 432 0 R
- 37
- 433 0 R
- 38
- 434 0 R
- 39
- 435 0 R
- 40
- 436 0 R
- 41
- 437 0 R
- 42
- 438 0 R
- 43
- 439 0 R ] >>
-endobj
-% 'R396': class PDFPageLabel
-396 0 obj
-% None
-<< /S /D
- /St 1 >>
-endobj
-% 'R397': class PDFPageLabel
-397 0 obj
-% None
-<< /S /D
- /St 2 >>
-endobj
-% 'R398': class PDFPageLabel
-398 0 obj
-% None
-<< /S /D
- /St 3 >>
-endobj
-% 'R399': class PDFPageLabel
-399 0 obj
-% None
-<< /S /D
- /St 4 >>
-endobj
-% 'R400': class PDFPageLabel
-400 0 obj
-% None
-<< /S /D
- /St 5 >>
-endobj
-% 'R401': class PDFPageLabel
-401 0 obj
-% None
-<< /S /D
- /St 6 >>
-endobj
-% 'R402': class PDFPageLabel
-402 0 obj
-% None
-<< /S /D
- /St 7 >>
-endobj
-% 'R403': class PDFPageLabel
-403 0 obj
-% None
-<< /S /D
- /St 8 >>
-endobj
-% 'R404': class PDFPageLabel
-404 0 obj
-% None
-<< /S /D
- /St 9 >>
-endobj
-% 'R405': class PDFPageLabel
-405 0 obj
-% None
-<< /S /D
- /St 10 >>
-endobj
-% 'R406': class PDFPageLabel
-406 0 obj
-% None
-<< /S /D
- /St 11 >>
-endobj
-% 'R407': class PDFPageLabel
-407 0 obj
-% None
-<< /S /D
- /St 12 >>
-endobj
-% 'R408': class PDFPageLabel
-408 0 obj
-% None
-<< /S /D
- /St 13 >>
-endobj
-% 'R409': class PDFPageLabel
-409 0 obj
-% None
-<< /S /D
- /St 14 >>
-endobj
-% 'R410': class PDFPageLabel
-410 0 obj
-% None
-<< /S /D
- /St 15 >>
-endobj
-% 'R411': class PDFPageLabel
-411 0 obj
-% None
-<< /S /D
- /St 16 >>
-endobj
-% 'R412': class PDFPageLabel
-412 0 obj
-% None
-<< /S /D
- /St 17 >>
-endobj
-% 'R413': class PDFPageLabel
-413 0 obj
-% None
-<< /S /D
- /St 18 >>
-endobj
-% 'R414': class PDFPageLabel
-414 0 obj
-% None
-<< /S /D
- /St 19 >>
-endobj
-% 'R415': class PDFPageLabel
-415 0 obj
-% None
-<< /S /D
- /St 20 >>
-endobj
-% 'R416': class PDFPageLabel
-416 0 obj
-% None
-<< /S /D
- /St 21 >>
-endobj
-% 'R417': class PDFPageLabel
-417 0 obj
-% None
-<< /S /D
- /St 22 >>
-endobj
-% 'R418': class PDFPageLabel
-418 0 obj
-% None
-<< /S /D
- /St 23 >>
-endobj
-% 'R419': class PDFPageLabel
-419 0 obj
-% None
-<< /S /D
- /St 24 >>
-endobj
-% 'R420': class PDFPageLabel
-420 0 obj
-% None
-<< /S /D
- /St 25 >>
-endobj
-% 'R421': class PDFPageLabel
-421 0 obj
-% None
-<< /S /D
- /St 26 >>
-endobj
-% 'R422': class PDFPageLabel
-422 0 obj
-% None
-<< /S /D
- /St 27 >>
-endobj
-% 'R423': class PDFPageLabel
-423 0 obj
-% None
-<< /S /D
- /St 28 >>
-endobj
-% 'R424': class PDFPageLabel
-424 0 obj
-% None
-<< /S /D
- /St 29 >>
-endobj
-% 'R425': class PDFPageLabel
-425 0 obj
-% None
-<< /S /D
- /St 30 >>
-endobj
-% 'R426': class PDFPageLabel
-426 0 obj
-% None
-<< /S /D
- /St 31 >>
-endobj
-% 'R427': class PDFPageLabel
-427 0 obj
-% None
-<< /S /D
- /St 32 >>
-endobj
-% 'R428': class PDFPageLabel
-428 0 obj
-% None
-<< /S /D
- /St 33 >>
-endobj
-% 'R429': class PDFPageLabel
-429 0 obj
-% None
-<< /S /D
- /St 34 >>
-endobj
-% 'R430': class PDFPageLabel
-430 0 obj
-% None
-<< /S /D
- /St 35 >>
-endobj
-% 'R431': class PDFPageLabel
-431 0 obj
-% None
-<< /S /D
- /St 36 >>
-endobj
-% 'R432': class PDFPageLabel
-432 0 obj
-% None
-<< /S /D
- /St 37 >>
-endobj
-% 'R433': class PDFPageLabel
-433 0 obj
-% None
-<< /S /D
- /St 38 >>
-endobj
-% 'R434': class PDFPageLabel
-434 0 obj
-% None
-<< /S /D
- /St 39 >>
-endobj
-% 'R435': class PDFPageLabel
-435 0 obj
-% None
-<< /S /D
- /St 40 >>
-endobj
-% 'R436': class PDFPageLabel
-436 0 obj
-% None
-<< /S /D
- /St 41 >>
-endobj
-% 'R437': class PDFPageLabel
-437 0 obj
-% None
-<< /S /D
- /St 42 >>
-endobj
-% 'R438': class PDFPageLabel
-438 0 obj
-% None
-<< /S /D
- /St 43 >>
-endobj
-% 'R439': class PDFPageLabel
-439 0 obj
-% None
-<< /S /D
- /St 44 >>
-endobj
-xref
-0 440
-0000000000 65535 f
-0000000113 00000 n
-0000000246 00000 n
-0000000411 00000 n
-0000000598 00000 n
-0000000850 00000 n
-0000001100 00000 n
-0000001349 00000 n
-0000001508 00000 n
-0000001840 00000 n
-0000002080 00000 n
-0000002321 00000 n
-0000002563 00000 n
-0000002805 00000 n
-0000003047 00000 n
-0000003290 00000 n
-0000003534 00000 n
-0000003778 00000 n
-0000004022 00000 n
-0000004266 00000 n
-0000004510 00000 n
-0000004754 00000 n
-0000004998 00000 n
-0000005242 00000 n
-0000005486 00000 n
-0000005730 00000 n
-0000005974 00000 n
-0000006218 00000 n
-0000006462 00000 n
-0000006706 00000 n
-0000006950 00000 n
-0000007194 00000 n
-0000007438 00000 n
-0000007682 00000 n
-0000007926 00000 n
-0000008170 00000 n
-0000008414 00000 n
-0000008658 00000 n
-0000008902 00000 n
-0000009146 00000 n
-0000009390 00000 n
-0000009634 00000 n
-0000009878 00000 n
-0000010122 00000 n
-0000010366 00000 n
-0000010610 00000 n
-0000010854 00000 n
-0000011098 00000 n
-0000011342 00000 n
-0000011586 00000 n
-0000011830 00000 n
-0000012074 00000 n
-0000012318 00000 n
-0000012562 00000 n
-0000012806 00000 n
-0000013050 00000 n
-0000013294 00000 n
-0000013538 00000 n
-0000013782 00000 n
-0000014026 00000 n
-0000014270 00000 n
-0000014514 00000 n
-0000014758 00000 n
-0000015002 00000 n
-0000015246 00000 n
-0000015490 00000 n
-0000015734 00000 n
-0000015978 00000 n
-0000016222 00000 n
-0000016466 00000 n
-0000016710 00000 n
-0000016954 00000 n
-0000017198 00000 n
-0000017442 00000 n
-0000017686 00000 n
-0000017930 00000 n
-0000018174 00000 n
-0000018418 00000 n
-0000018662 00000 n
-0000018906 00000 n
-0000019150 00000 n
-0000019378 00000 n
-0000020334 00000 n
-0000020596 00000 n
-0000020859 00000 n
-0000021109 00000 n
-0000021358 00000 n
-0000021624 00000 n
-0000021876 00000 n
-0000022126 00000 n
-0000022378 00000 n
-0000022628 00000 n
-0000022880 00000 n
-0000023130 00000 n
-0000023382 00000 n
-0000023633 00000 n
-0000023872 00000 n
-0000024052 00000 n
-0000024487 00000 n
-0000024748 00000 n
-0000025012 00000 n
-0000025262 00000 n
-0000025515 00000 n
-0000025768 00000 n
-0000026020 00000 n
-0000026271 00000 n
-0000026524 00000 n
-0000026762 00000 n
-0000027160 00000 n
-0000027411 00000 n
-0000027665 00000 n
-0000027919 00000 n
-0000028157 00000 n
-0000028508 00000 n
-0000028762 00000 n
-0000029016 00000 n
-0000029290 00000 n
-0000029631 00000 n
-0000029885 00000 n
-0000030175 00000 n
-0000030465 00000 n
-0000030719 00000 n
-0000030957 00000 n
-0000031302 00000 n
-0000031601 00000 n
-0000031855 00000 n
-0000032093 00000 n
-0000032424 00000 n
-0000032678 00000 n
-0000032932 00000 n
-0000033184 00000 n
-0000033436 00000 n
-0000033688 00000 n
-0000033942 00000 n
-0000034179 00000 n
-0000034545 00000 n
-0000034844 00000 n
-0000035098 00000 n
-0000035346 00000 n
-0000035610 00000 n
-0000035951 00000 n
-0000036205 00000 n
-0000036443 00000 n
-0000036774 00000 n
-0000037013 00000 n
-0000037334 00000 n
-0000037586 00000 n
-0000037825 00000 n
-0000038156 00000 n
-0000038409 00000 n
-0000038661 00000 n
-0000038913 00000 n
-0000039165 00000 n
-0000039419 00000 n
-0000039671 00000 n
-0000039923 00000 n
-0000040177 00000 n
-0000040431 00000 n
-0000040683 00000 n
-0000040922 00000 n
-0000041343 00000 n
-0000041595 00000 n
-0000041848 00000 n
-0000042102 00000 n
-0000042385 00000 n
-0000042639 00000 n
-0000042893 00000 n
-0000043175 00000 n
-0000043458 00000 n
-0000043712 00000 n
-0000043964 00000 n
-0000044282 00000 n
-0000044536 00000 n
-0000044825 00000 n
-0000045062 00000 n
-0000045513 00000 n
-0000045765 00000 n
-0000046019 00000 n
-0000046273 00000 n
-0000046527 00000 n
-0000046786 00000 n
-0000047043 00000 n
-0000047304 00000 n
-0000047556 00000 n
-0000047810 00000 n
-0000048068 00000 n
-0000048320 00000 n
-0000048578 00000 n
-0000048832 00000 n
-0000049085 00000 n
-0000049346 00000 n
-0000049600 00000 n
-0000049854 00000 n
-0000050108 00000 n
-0000050367 00000 n
-0000050621 00000 n
-0000050873 00000 n
-0000051126 00000 n
-0000051378 00000 n
-0000051631 00000 n
-0000051885 00000 n
-0000052137 00000 n
-0000052389 00000 n
-0000052642 00000 n
-0000052896 00000 n
-0000053150 00000 n
-0000053404 00000 n
-0000053658 00000 n
-0000053941 00000 n
-0000054194 00000 n
-0000054484 00000 n
-0000054738 00000 n
-0000055049 00000 n
-0000055301 00000 n
-0000055538 00000 n
-0000056239 00000 n
-0000056528 00000 n
-0000056780 00000 n
-0000057032 00000 n
-0000057290 00000 n
-0000057544 00000 n
-0000057798 00000 n
-0000058052 00000 n
-0000058306 00000 n
-0000058560 00000 n
-0000058814 00000 n
-0000059059 00000 n
-0000059313 00000 n
-0000059567 00000 n
-0000059821 00000 n
-0000060075 00000 n
-0000060325 00000 n
-0000060781 00000 n
-0000061080 00000 n
-0000061319 00000 n
-0000061640 00000 n
-0000061894 00000 n
-0000062133 00000 n
-0000062464 00000 n
-0000062718 00000 n
-0000062980 00000 n
-0000063234 00000 n
-0000063497 00000 n
-0000063750 00000 n
-0000064013 00000 n
-0000064267 00000 n
-0000064521 00000 n
-0000064759 00000 n
-0000065160 00000 n
-0000065413 00000 n
-0000065678 00000 n
-0000065932 00000 n
-0000066186 00000 n
-0000066425 00000 n
-0000066786 00000 n
-0000067025 00000 n
-0000067346 00000 n
-0000067600 00000 n
-0000067861 00000 n
-0000068097 00000 n
-0000068423 00000 n
-0000068722 00000 n
-0000068961 00000 n
-0000069282 00000 n
-0000069540 00000 n
-0000069794 00000 n
-0000070062 00000 n
-0000070316 00000 n
-0000070570 00000 n
-0000070831 00000 n
-0000071077 00000 n
-0000071458 00000 n
-0000071710 00000 n
-0000071963 00000 n
-0000072215 00000 n
-0000072469 00000 n
-0000072723 00000 n
-0000072977 00000 n
-0000073231 00000 n
-0000073471 00000 n
-0000073862 00000 n
-0000074099 00000 n
-0000074420 00000 n
-0000074659 00000 n
-0000074965 00000 n
-0000075264 00000 n
-0000075502 00000 n
-0000075823 00000 n
-0000076077 00000 n
-0000076350 00000 n
-0000076589 00000 n
-0000076930 00000 n
-0000077184 00000 n
-0000077423 00000 n
-0000077739 00000 n
-0000078038 00000 n
-0000078292 00000 n
-0000078565 00000 n
-0000078804 00000 n
-0000079145 00000 n
-0000079384 00000 n
-0000079705 00000 n
-0000079944 00000 n
-0000080250 00000 n
-0000080549 00000 n
-0000080803 00000 n
-0000081042 00000 n
-0000081373 00000 n
-0000081627 00000 n
-0000081866 00000 n
-0000082182 00000 n
-0000082467 00000 n
-0000082631 00000 n
-0000082881 00000 n
-0000083010 00000 n
-0000083264 00000 n
-0000083461 00000 n
-0000083675 00000 n
-0000083889 00000 n
-0000084115 00000 n
-0000084317 00000 n
-0000084526 00000 n
-0000084723 00000 n
-0000084926 00000 n
-0000085127 00000 n
-0000085345 00000 n
-0000085546 00000 n
-0000085760 00000 n
-0000085955 00000 n
-0000086153 00000 n
-0000086389 00000 n
-0000086569 00000 n
-0000086793 00000 n
-0000087003 00000 n
-0000087202 00000 n
-0000087404 00000 n
-0000087612 00000 n
-0000087817 00000 n
-0000088017 00000 n
-0000088216 00000 n
-0000088426 00000 n
-0000088639 00000 n
-0000088845 00000 n
-0000089047 00000 n
-0000089270 00000 n
-0000089497 00000 n
-0000089697 00000 n
-0000089910 00000 n
-0000090110 00000 n
-0000090302 00000 n
-0000090487 00000 n
-0000091025 00000 n
-0000094059 00000 n
-0000103789 00000 n
-0000109846 00000 n
-0000114110 00000 n
-0000118422 00000 n
-0000122396 00000 n
-0000127676 00000 n
-0000131835 00000 n
-0000136678 00000 n
-0000143033 00000 n
-0000146830 00000 n
-0000151468 00000 n
-0000155384 00000 n
-0000159558 00000 n
-0000165727 00000 n
-0000171766 00000 n
-0000179481 00000 n
-0000188078 00000 n
-0000194864 00000 n
-0000198677 00000 n
-0000203639 00000 n
-0000209507 00000 n
-0000215979 00000 n
-0000220983 00000 n
-0000224818 00000 n
-0000229308 00000 n
-0000234261 00000 n
-0000239022 00000 n
-0000243642 00000 n
-0000249719 00000 n
-0000254639 00000 n
-0000258668 00000 n
-0000262019 00000 n
-0000267869 00000 n
-0000272448 00000 n
-0000277885 00000 n
-0000282183 00000 n
-0000288270 00000 n
-0000292087 00000 n
-0000297102 00000 n
-0000300714 00000 n
-0000305973 00000 n
-0000312832 00000 n
-0000314646 00000 n
-0000315378 00000 n
-0000315457 00000 n
-0000315536 00000 n
-0000315615 00000 n
-0000315694 00000 n
-0000315773 00000 n
-0000315852 00000 n
-0000315931 00000 n
-0000316010 00000 n
-0000316089 00000 n
-0000316169 00000 n
-0000316249 00000 n
-0000316329 00000 n
-0000316409 00000 n
-0000316489 00000 n
-0000316569 00000 n
-0000316649 00000 n
-0000316729 00000 n
-0000316809 00000 n
-0000316889 00000 n
-0000316969 00000 n
-0000317049 00000 n
-0000317129 00000 n
-0000317209 00000 n
-0000317289 00000 n
-0000317369 00000 n
-0000317449 00000 n
-0000317529 00000 n
-0000317609 00000 n
-0000317689 00000 n
-0000317769 00000 n
-0000317849 00000 n
-0000317929 00000 n
-0000318009 00000 n
-0000318089 00000 n
-0000318169 00000 n
-0000318249 00000 n
-0000318329 00000 n
-0000318409 00000 n
-0000318489 00000 n
-0000318569 00000 n
-0000318649 00000 n
-0000318729 00000 n
-0000318809 00000 n
-trailer
-<< /ID
- % ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(@\241\272\314$\341\035g\323\330\247\312\366\021\262\304) (@\241\272\314$\341\035g\323\330\247\312\366\021\262\304)]
-
- /Info 312 0 R
- /Root 311 0 R
- /Size 440 >>
-startxref
-318858
-%%EOF
diff --git a/plac/doc/plac.txt b/plac/doc/plac.txt
deleted file mode 100644
index e7083f8..0000000
--- a/plac/doc/plac.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-.. include:: plac_core.txt
-.. include:: plac_adv.txt
diff --git a/plac/doc/plac_adv.txt b/plac/doc/plac_adv.txt
deleted file mode 100644
index 936f4af..0000000
--- a/plac/doc/plac_adv.txt
+++ /dev/null
@@ -1,1266 +0,0 @@
-Advanced usages of plac
-=========================================================
-
-Introduction
------------------------------------------------------
-
-One of the design goals of plac_ is to make it dead easy to write a
-scriptable and testable interface for an application. You can use
-plac_ whenever you have an API with strings in input and strings in
-output, and that includes a *huge* domain of applications.
-
-A string-oriented interface is a scriptable interface by
-construction. That means that you can define a command language for
-your application and that it is possible to write scripts which are
-interpretable by plac_ and can be run as batch scripts.
-
-Actually, at the most general level, you can see plac_ as a generic tool to
-write domain specific languages (DSL). With plac_ you
-can test your application interactively as well as with batch
-scripts, and even with the analogous of Python doctests for your
-defined language.
-
-You can easily replace the ``cmd`` module of the standard library and
-you could easily write an application like twill_ with plac_. Or you
-could use it to script your building procedure. plac_ also supports
-parallel execution of multiple commands and can be used as
-task manager and monitor. It is also quite easy to build a GUI
-or a Web application on top of plac_. When speaking of things
-you can do with plac_, your imagination is the only limit!
-
-From scripts to interactive applications
-------------------------------------------------------------
-
-Command-line scripts have many advantages, but they are no substitute
-for interactive applications.
-In particular, if you have a script with a large startup time which must be run
-multiple times, it is best to turn it into an interactive application,
-so that the startup is performed only once. ``plac`` provides an
-``Interpreter`` class just for this purpose.
-
-The ``Interpreter`` class wraps the main function of a script and
-provides an ``.interact`` method to start an interactive interpreter
-reading commands from the console.
-
-For instance, you can define an interactive interpreter on top of the
-``ishelve`` script introduced in the `basic documentation`_ as
-follows:
-
-.. include:: shelve_interpreter.py
- :literal:
-
-A trick has been used here: the ishelve command-line interface has been
-hidden inside an external interface. They are distinct: for instance
-the external interface recognizes the ``-h/--help`` flag whereas the
-internal interface only recognizes the ``.help`` command::
-
- $ python shelve_interpreter.py -h
-
-.. include:: shelve_interpreter.help
- :literal:
-
-Thanks to this ingenuous trick, the script can be run both interactively
-and non-interactively::
-
- $ python shelve_interpreter.py .clear # non-interactive use
- cleared the shelve
-
-Here is an usage session::
-
- $ 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
- 1
- i> b=2
- setting b=2
- i> a b
- 1
- 2
- i> .del a
- deleted a
- i> a
- a: not found
- i> .show
- b=2
- i> [CTRL-D]
-
-The ``.interact`` method
-reads commands from the console and send them to the
-underlying interpreter, until the user send a CTRL-D
-command (CTRL-Z in Windows). There is a default
-argument ``prompt='i> '`` which
-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``.
-
-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:
-
-.. include:: test_ishelve.py
- :literal:
-
-However, using ``plac.call`` is not especially nice. The big
-issue is that ``plac.call`` responds to invalid input by printing an
-error message on stderr and by raising a ``SystemExit``: this is
-certainly not a nice thing to do in a test.
-
-As a consequence of this behavior it is impossible to test for invalid
-commands, unless you wrap the ``SystemExit`` exception by
-hand each time (a possibly you do something with the error message in
-stderr too). Luckily, ``plac`` offers a better testing support through
-the ``check`` method of ``Interpreter`` objects:
-
-.. include:: test_ishelve_more.py
- :literal:
-
-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.
-Notice that ``AssertionError`` is catched by tools like ``py.test`` and
-``nosetests`` and actually ``plac`` tests are intended to be run with
-such tools.
-
-Interpreters offer a minor syntactic advantage with respect to calling
-``plac.call`` directly, but they offer a *major* semantic advantage when things
-go wrong (read exceptions): an ``Interpreter`` object internally invokes
-something like ``plac.call``, but it wraps all exceptions, so that ``i.check``
-is guaranteed not to raise any exception except ``AssertionError``.
-
-Even the ``SystemExit`` exception is captured and you can write your test as
-
- ``i.check('-cler', 'SystemExit: unrecognized arguments: -cler')``
-
-without risk of exiting from the Python interpreter.
-
-There is a second advantage of interpreters: if the main function contains some
-initialization code and finalization code
-(``__enter__`` and ``__exit__`` functions) they will be run only
-once at the beginning and at the end of the interpreter loop.
-``plac.call`` instead ignores the initialization/finalization code.
-
-Plac easy tests
----------------------------------------------------------
-
-Writing your tests in terms of ``Interpreter.check`` is certainly an
-improvement over writing them in terms of ``plac.call``, but they
-are still too low-level for my taste. The ``Interpreter`` class provides
-support for doctest-style tests, a.k.a. *plac easy tests*.
-
-By using plac easy tests you can cut and paste your interactive session and
-turn it into a runnable automatics test.
-Consider for instance the following file ``ishelve.placet`` (the ``.placet``
-extension is a mnemonic for plac easy tests):
-
-.. include:: ishelve.placet
- :literal:
-
-Notice the precence of the shebang line containing the name of the
-plac_ tool to test (a plac_ tool is just a Python module with a
-function called ``main``). The shebang is ignored by the interpreter
-(it looks like a comment to it) but it is there so that external
-tools (say a test runner) can infer the plac interpreter
-to use to test the file.
-
-You can test ``ishelve.placet`` file by calling the
-``.doctest`` method of the interpreter, as in this example::
-
- $ python -c"import plac, ishelve
- plac.Interpreter(ishelve.main).doctest(open('ishelve.placet'), verbose=True)"
-
-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.
-
-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 ``#``), escape sequences and more. Look at the
-shlex_ documentation if you need to customize how the language is
-interpreted. For more flexibility, it is even possible to pass to the
-interpreter a custom split function with signature ``split(line,
-commentchar)``.
-
-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, 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.
-
-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).
-
-It is straighforward to integrate your ``.placet`` tests with standard
-testing tools. For instance, you can integrate your doctests with ``nose``
-or ``py.test`` as follow::
-
- import os, shlex, plac
-
- def test_doct():
- """
- Find all the doctests in the current directory and run them with the
- corresponding plac interpreter (the shebang rules!)
- """
- placets = [f for f in os.listdir('.') if f.endswith('.placet')]
- for placet in placets:
- lines = list(open(placet))
- assert lines[0].startswith('#!'), 'Missing or incorrect shebang line!'
- firstline = lines[0][2:] # strip the shebang
- main = plac.import_main(*shlex.split(firstline))
- yield plac.Interpreter(main).doctest, lines[1:]
-
-Here you should notice that usage of ``plac.import_main``, an utility
-which is able to import the main function of the script specified in
-the 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
-a string containing directory names separated by colons). If the variable
-``PLACPATH`` is not defined, it just looks in the current directory.
-If the plac tool is not found, an ``ImportError`` is raised.
-
-Plac batch scripts
---------------------------------------------------
-
-It is pretty easy to realize that an interactive interpreter can
-also be used to run batch scripts: instead of reading the commands from
-the console, it is enough to read the commands from a file.
-plac_ interpreters provide an ``.execute`` method to perform just that.
-
-There is just a subtle point to notice: whereas in an interactive loop
-one wants to manage all exceptions, a batch script should not in the
-background in case of unexpected errors. The implementation of
-``Interpreter.execute`` makes sure that any error raised by
-``plac.call`` internally is re-raised. In other words, plac_
-interpreters *wrap the errors, but does not eat them*: the errors are
-always accessible and can be re-raised on demand.
-
-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 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)"
- i> .clear
- cleared the shelve
- i> a=1 b=2
- setting a=1
- setting b=2
- i> .show
- b=2
- a=1
- i> .del a
- deleted a
- i> .dl b
- 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?).
-
-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 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
-implement *subcommands* (a familiar example of a command-line
-application featuring subcommands is subversion).
-
-Technically a container of commands is any object with a ``.commands`` attribute
-listing a set of functions or methods which are valid commands. A command
-container may have initialization/finalization hooks (``__enter__/__exit__``)
-and dispatch hooks (``__missing__``, invoked for invalid command names).
-Moreover, only when using command containers plac_ is able to provide
-automatic autocompletion of commands.
-
-The shelve interface can be rewritten in an object-oriented way as follows:
-
-.. include:: ishelve2.py
- :literal:
-
-``plac.Interpreter`` objects wrap context manager objects
-consistently. In other words, if you wrap an object with
-``__enter__`` and ``__exit__`` methods, they are invoked in the right
-order (``__enter__`` before the interpreter loop starts and
-``__exit__`` after the interpreter loop ends, both in the regular and
-in the exceptional case). In our example, the methods ``__enter__``
-and ``__exit__`` make sure the the shelve is opened and closed
-correctly even in the case of exceptions. Notice that I have not
-implemented any error checking in the ``show`` and ``delete`` methods
-on purpose, to verify that plac_ works correctly in the presence of
-exceptions.
-
-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::
-
- $ python ishelve2.py
- A minimal interface over a shelve object.
- Operating on /home/micheles/conf.shelve.
- Use help to see the available commands.
- i> help
-
- special commands
- ================
- last_tb
-
- custom commands
- ===============
- delete set show showall
-
- i> delete
- deleting everything
- i> set a pippo
- setting a=pippo
- i> set b lippo
- setting b=lippo
- i> showall
- b = lippo
- a = pippo
- i> show a b
- a = pippo
- b = lippo
- i> del a
- deleting a
- i> showall
- b = lippo
- i> delete a
- 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.
-
-CHANGED IN VERSION 0.9: if you have an old version of plac_ the
-``help`` command must be prefixed with a dot, i.e. you must write
-``.help``. The old behavior was more consistent in my opinion, since
-it made it clear that the ``help`` command was special and threated
-differently from the regular commands. However many users complained against
-the dot, so I changed it to make them happy. Starting from release 0.9
-the ``help`` command is just an alias for ``--help``, in the
-sense that there is a preprocessor step replacing ``help`` with ``--help``
-in the argument list.
-Notice that if you implement a custom ``help`` command in the commander class
-the preprocessor will be automatically disabled: passing ``help``
-will call the custom help command, just as you would expect.
-
-plac.Interpreter.call
---------------------------------------------
-
-At the core of ``plac`` there is the ``call`` function which invokes
-a callable with the list of arguments passed at the command-line
-(``sys.argv[1:]``). Thanks to ``plac.call`` you can launch your module
-by simply adding the lines::
-
- if __name__ == '__main__':
- plac.call(main)
-
-Everything works fine if ``main`` is a simple callable performing some
-action; however, in many cases, one has a ``main`` "function" which
-is a actually a factory returning a command container object. For
-instance, in my second shelve example the main function is the class
-``ShelveInterface``, and the two lines needed to run the module are
-a bit ugly::
-
- if __name__ == '__main__':
- plac.Interpreter(plac.call(ShelveInterface)).interact()
-
-Moreover, now the program runs, but only in interactive mode, i.e.
-it is not possible to run it as a script. Instead, it would be nice
-to be able to specify the command to execute on the command-line
-and have the interpreter start, execute the command and finish
-properly (I mean by calling ``__enter__`` and ``__exit__``)
-without needing user input. Then the script could be called from
-a batch shell script working in the background.
-In order to provide such functionality ``plac.Interpreter`` provides
-a classmethod named ``.call`` which takes the factory, instantiates
-it with the arguments read from the command line, wraps the resulting
-container object as an interpreter and runs it with the rest arguments
-found in the command line. Here is the code to turn the ``ShelveInterface``
-into a script
-
-.. include:: ishelve3.py
- :literal:
-
-and here are a few examples of usage::
-
- $ python ishelve3.py -h
- usage: ishelve3.py [-h] [-i] [-configfile CONFIGFILE] [args [args ...]]
-
- positional arguments:
- args
-
- optional arguments:
- -h, --help show this help message and exit
- -i, --interact start interactive interpreter
- -configfile CONFIGFILE
- path name of the shelve
-
- $ python ishelve3.py set a 1
- setting a=1
- $ python ishelve3.py show a
- a = 1
-
-If you pass the ``-i`` flag in the command line, then the
-script will enter in interactive mode and ask the user
-for the commands to execute::
-
- $ python ishelve3.py -i
- A minimal interface over a shelve object.
- Operating on /home/micheles/conf.shelve.
- Use help to see the available commands.
-
- i>
-
-In a sense, I have closed the circle: at the beginning of this
-document I discussed how to turn a script into an interactive
-application (the ``shelve_interpreter.py`` example), whereas here I
-have show how to turn an interactive application into a script.
-
-The complete signature of ``plac.Interpreter.call`` is the following::
-
- call(factory, arglist=sys.argv[1:],
- commentchar='#', split=shlex.split,
- stdin=sys.stdin, prompt='i> ', verbose=False)
-
-The factory must have a fixed number of positional arguments (no
-default arguments, no varargs, no kwargs), otherwise a ``TypeError``
-is raised: the reason is that we want to be able to distinguish the
-command-line arguments needed to instantiate the factory from the rest
-arguments that must be sent to the corresponding interpreter object.
-It is also possible to specify a list of arguments different from
-``sys.argv[1:]`` (useful in tests), the character to be recognized as
-a comment, the splitting function, the input source and the prompt to
-use while in interactive mode, and a verbose flag.
-
-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 sensitivity,
-the way to go is to pass a ``plac.ReadlineInput`` object to the
-interpreter. Here is an example, assuming you want to build a
-database interface understanding SQL commands:
-
-.. include:: sql_interface.py
- :literal:
-
-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: I
-tried it only once and it worked, but your mileage may vary).
-For people worried about licenses, I will notice that plac_ uses the
-readline library only if available, it does not include it and it does
-not rely on it in any fundamental way, so that the plac_ licence does
-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> help delete
- usage: delete [name]
-
- delete given parameter (or everything)
-
- positional arguments:
- name [None]
-
- i> help show
- usage: show [names [names ...]]
-
- 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 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
---------------------------------------------------------
-
-The distribution of plac_ includes a runner script named ``plac_runner.py``,
-which will be installed in a suitable directory in your system by distutils_
-(say in ``\usr\local\bin\plac_runner.py`` in a Unix-like operative system).
-The runner provides many facilities to run ``.plac`` scripts and
-``.placet`` files, as well as Python modules containg a ``main``
-object, which can be a function, a command container object or
-even a command container class.
-
-For instance, suppose you want to execute a script containing commands
-defined in the ``ishelve2`` module like the following one:
-
-.. include:: ishelve2.plac
- :literal:
-
-The first line of the ``.plac`` script contains the name of the
-python module containing the plac interpreter and the arguments
-which must be passed to its main function in order to be able
-to instantiate an interpreter object. In this case I appended
-``:ShelveInterface`` to the name of the module to specify the
-object that must be imported: if not specified, by default the
-object named 'main' is imported.
-The other lines contains commands.
-You can run the script as follows::
-
- $ plac_runner.py --batch ishelve2.plac
- setting a=1
- deleting a
- Traceback (most recent call last):
- ...
- _bsddb.DBNotFoundError: (-30988, 'DB_NOTFOUND: No matching key/data pair found')
-
-The last command intentionally contained an error, to show that the
-plac runner does not eat the traceback.
-
-The runner can also be used to run Python modules in interactive
-mode and non-interactive mode. If you put this alias in your bashrc
-
- ``alias plac="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:ShelveInterface
- 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
- setting a=1
- i> set b 2
- setting b=2
- i> show b
- b = 2
-
-Now you can cut and paste the interactive session an turns into into
-a ``.placet`` file like the following:
-
-.. include:: ishelve2.placet
- :literal:
-
-Notice that the first line specifies a test database
-``~/test.shelve``, to avoid clobbering your default shelve. If you
-mispell the arguments in the first line plac will give you an
-argparse_ error message (just try).
-
-You can run placets following the shebang convention directly with
-the plac runner::
-
- $ plac --test ishelve2.placet
- run 1 plac test(s)
-
-If you want to see the output of the tests, pass the ``-v/--verbose`` flag.
-Notice that he runner ignore the extension, so you can actually use any
-extension your like, but *it relies on the first line of the file to invoke
-the corresponding plac tool with the given arguments*.
-
-The plac runner does not provide any test discovery facility,
-but you can use standard Unix tools to help. For instance, you can
-run all the ``.placet`` files into a directory and its subdirectories
-as follows::
-
- $ find . -name \*.placet | xargs plac_runner.py -t
-
-The plac runner expects the main function of your script to
-return a plac tool, i.e. a function or an object with a ``.commands``
-attribute. It this is not the case the runner gracefully exits.
-
-It also works in non-interactive mode, if you call it as
-
- ``$ plac module.py args ...``
-
-Here is an example::
-
- $ plac ishelve.py a=1
- setting a=1
- $ plac ishelve.py .show
- a=1
-
-Notice that in non-interactive mode the runner just invokes ``plac.call``
-on the ``main`` object of the Python module.
-
-A non class-based example
---------------------------------------------------------
-
-plac_ does not force you to use classes to define command containers.
-Even a simple function can be a valid command container, it is
-enough to add to it a ``.commands`` attribute and possibly
-``__enter__`` and/or ``__exit__`` attributes.
-
-In particular, a Python module is a perfect container of commands. As an
-example, consider the following module implementing a fake Version
-Control System:
-
-.. include:: vcs.py
- :literal:
-
-Notice that I have defined both an ``__exit__`` hook and a ``__missing__``
-hook, invoked for non-existing commands.
-The real trick here is the line ``main = __import__(__name__)``, which
-define ``main`` to be an alias for the current module.
-
-The ``vcs`` module does not contain an ``if __name__ == '__main__'``
-block, but you can still run it through the plac runner
-(try ``plac vcs.py -h``):
-
-.. include:: vcs.help
- :literal:
-
-You can get help for the subcommands by postponing ``-h`` after the
-name of the command::
-
- $ plac vcs.py status -h
- usage: vcs.py status [-h] [-q]
-
- A fake status command
-
- optional arguments:
- -h, --help show this help message and exit
- -q, --quiet summary information
-
-Notice how the docstring of the command is automatically shown in
-usage message, as well as the documentation for the sub flag ``-q``.
-
-Here is an example of a non-interactive session::
-
- $ plac vcs.py check url
- checkout
- url
- $ plac vcs.py st -q
- status
- True
- $ plac vcs.py co
- commit
- None
-
-and here is an interactive session::
-
- $ plac -i vcs.py
- usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
- i> check url
- checkout
- url
- i> st -q
- status
- True
- i> co
- commit
- None
- i> sto
- Command 'sto' does not exist
- i> [CTRL-D]
- ok
-
-Notice the invocation of the ``__missing__`` hook for non-existing commands.
-Notice also that the ``__exit__`` hook gets called only in interactive
-mode.
-
-If the commands are completely independent, a module is a good fit for
-a method container. In other situations, it is best to use a custom
-class.
-
-Writing your own plac runner
-----------------------------------------------------------
-
-The runner included in the plac_ distribution is intentionally kept
-small (around 50 lines of code) so that you can study it and write
-your own runner if want to. If you need to go to such level
-of detail, you should know that the most important method of
-the ``Interpreter`` class is the ``.send`` method, which takes
-strings in input and returns a four-tuple with attributes
-``.str``, ``.etype``, ``.exc`` and ``.tb``:
-
-- ``.str`` is the output of the command, if successful (a string);
-- ``.etype`` is the class of the exception, if the command fail;
-- ``.exc`` is the exception instance;
-- ``.tb`` is the traceback.
-
-Moreover the ``__str__`` representation of the output object is redefined
-to return the output string if the command was successful or the error
-message if the command failed (actually it returns the error message
-preceded by the name of the exception class).
-
-For instance, if you send a mispelled option to
-the interpreter a ``SystemExit`` will be trapped:
-
->>> import plac
->>> from ishelve import ishelve
->>> with plac.Interpreter(ishelve) as i:
-... print(i.send('.cler'))
-...
-SystemExit: unrecognized arguments: .cler
-
-It is important to invoke the ``.send`` method inside the context manager,
-otherwise you will get a ``RuntimeError``.
-
-For instance, suppose you want to implement a graphical runner for a
-plac-based interpreter with two text widgets: one to enter the commands
-and one to display the results. Suppose you want to display the errors
-with tracebacks in red. You will need to code something like that
-(pseudocode follows)::
-
- input_widget = WidgetReadingInput()
- output_widget = WidgetDisplayingOutput()
-
- def send(interpreter, line):
- out = interpreter.send(line)
- if out.tb: # there was an error
- output_widget.display(out.tb, color='red')
- else:
- output_widget.display(out.str)
-
- main = plac.import_main(tool_path) # get the main object
-
- with plac.Interpreter(main) as i:
- def callback(event):
- if event.user_pressed_ENTER():
- send(i, input_widget.last_line)
- input_widget.addcallback(callback)
- gui_mainloop.start()
-
-You can adapt the pseudocode to your GUI toolkit of choice and you can
-also change the file associations in such a way that clicking on a
-plac tool file the graphical user interface starts.
-
-An example of GUI program built on top of plac_ is given later on, in the
-paragraph *Managing the output of concurrent commands* (using Tkinter
-for simplicity and portability).
-
-There is a final *caveat*: since the plac interpreter loop is
-implemented via extended generators, plac interpreters are single threaded: you
-will get an error if you ``.send`` commands from separated threads.
-You can circumvent the problem by using a queue. If EXIT is a sentinel
-value to signal exiting from the interpreter look, you can write code
-like this::
-
- with interpreter:
- for input_value in iter(input_queue.get, EXIT):
- output_queue.put(interpreter.send(input_value))
-
-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 -i
- A fake importer with an import_file command
- i> import_file file1
- ... <wait 3+ minutes>
- Imported 100 lines
- Imported 200 lines
- Imported 300 lines
- ...
- 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>
-
-It is possible to store the output of a task into a file, to be read
-later (this is useful for tasks with a large output)::
-
- i> .output 1 /tmp/out.txt
- saved output of 1 into /tmp/out.txt
-
-You can even skip the number argument: then ``.output`` will the return
-the output of the last launched command (the special commands like .output
-do not count).
-
-You can launch many tasks one after the other::
-
- i> import_file file2
- <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.
-
-Managing the output of concurrent commands
----------------------------------------------
-
-plac_ acts as a command-line task launcher and can be used as the base
-to build a GUI-based task launcher and task monitor. To this aim the
-interpreter class provides a ``.submit`` method which returns a task
-object and a ``.tasks`` method returning the list of all the tasks
-submitted to the interpreter. The ``submit`` method does not start the task
-and thus it is nonblocking.
-Each task has an ``.outlist`` attribute which is a list
-storing the value yielded by the generator underlying the task (the
-``None`` values are skipped though): the ``.outlist`` grows as the
-task runs and more values are yielded. Accessing the ``.outlist`` is
-nonblocking and can be done freely.
-Finally there is a ``.result``
-property which waits for the task to finish and returns the last yielded
-value or raises an exception. The code below provides an example of
-how you could implement a GUI over the importer example:
-
-.. include:: importer_ui.py
- :literal:
-
-Monitor support
---------------------------------
-
-Starting from release 0.8 plac_ provides builtin support for monitoring the
-output of concurrent commands, at least for platforms where multiprocessing
-is fully supported. You can define your own monitor
-class, simply by inheriting from ``plac.Monitor`` and by
-overriding the methods ``add_listener(self, no)``,
-``del_listener(self, taskno)``, ``notify_listener(self, taskno, msg)``,
-``schedule(self, seconds, func, arg)`` and ``run(self)``.
-Then, you can a monitor object to any ``plac.Interpreter`` object
-by simply calling the ``add_monitor`` method.
-For convenience,
-``plac`` comes with a very simple ``TkMonitor`` based on Tkinter
-(I chose Tkinter because it is easy to use and it is
-in the standard library, but you can use any GUI): you can just
-look at how the ``TkMonitor`` is implemented in ``plac_tk.py``
-and adapt it. Here is an example of usage of the ``TkMonitor``:
-
-.. include:: tkmon.py
- :literal:
-
-Try to give the ``hello`` command from the interactive interpreter:
-each time a new text widget will be added displaying the output
-of the command. Notice that if ``Tkinter`` is not installed correctly
-on your system the ``TkMonitor`` class will not be available.
-
-Parallel computing with plac
----------------------------------------------
-
-plac_ is certainly not intended as a tool for parallel computing, but
-still you can use it to launch a set of commands and to collect the
-results, similarly to the MapReduce pattern popularized by
-Google. In order to give an example, I will consider the "Hello
-World" of parallel computing, i.e. the computation of pi with
-independent processes. There is a huge number of algorithms to
-compute pi; here I will describe a trivial one chosen for simplicity,
-not per efficienty. The trick is to consider the first quadrant of a
-circle with radius 1 and to extract a number of points ``(x, y)`` with
-``x`` and ``y`` random variables in the interval ``[0,1]``. The
-probability of extracting a number inside the quadrant (i.e. with
-``x^2 + y^2 < 1``) is proportional to the area of the quadrant
-(i.e. ``pi/4``). The value of ``pi`` therefore can be extracted by
-multiplying by 4 the ratio between the number of points in the
-quadrant versus the total number of points ``N``, for ``N`` large::
-
- def calc_pi(N):
- inside = 0
- for j in xrange(N):
- x, y = random(), random()
- if x*x + y*y < 1:
- inside += 1
- return (4.0 * inside) / N
-
-The algorithm is trivially parallelizable: if you have n CPUs, you can
-compute pi n times with N/n iterations, sum the results and divide the total
-by n. I have a Macbook with two cores, therefore I would expect a speedup
-factor of 2 with respect to a sequential computation. Moreover, I would
-expect a threaded computation to be even slower than a sequential
-computation, due to the GIL and the scheduling overhead.
-
-Here is a script implementing the algorithm and working in three different
-modes (parallel mode, threaded mode and sequential mode) depending on a
-``mode`` option:
-
-.. include:: picalculator.py
- :literal:
-
-Notice the ``submit_tasks`` method, which instantiates and initializes a
-``plac.Interpreter`` object and submits a number of commands corresponding
-to the number of available CPUs. The ``calc_pi`` command yield a log
-message every million of interactions, just to monitor the progress of
-the computation. The ``run`` method starts all the submitted commands
-in parallel and sums the results. It returns the average value of ``pi``
-after the slowest CPU has finished its job (if the CPUs are equal and
-equally busy they should finish more or less at the same time).
-
-Here are the results on my old Macbook with Ubuntu 10.04 and Python 2.6,
-for 10 million of iterations::
-
- $ python picalculator.py -mP 10000000 # two processes
- 3.141904 in 5.744545 seconds
- $ python picalculator.py -mT 10000000 # two threads
- 3.141272 in 13.875645 seconds
- $ python picalculator.py -mS 10000000 # sequential
- 3.141586 in 11.353841 seconds
-
-As you see using processes one gets a 2x speedup indeed, where the threaded
-mode is some 20% slower than the sequential mode.
-
-Since the pattern submit a bunch of tasks, starts them and collect the
-results is so common, plac_ provides an utility function
-``runp(genseq, mode='p', monitors=(), start=True)`` to start
-a bunch a generators and return a list of task objects. By default
-``runp`` use processes, but you can use threads by passing ``mode='t'``.
-If you do not wont to start the tasks, you can say so (``start=False``).
-With ``runp`` the parallel pi calculation becomes a one-liner::
-
- sum(task.result for task in plac.runp(calc_pi(N) for i in range(ncpus)))/ncpus
-
-The file ``test_runp`` in the ``doc`` directory of the plac distribution
-shows another couple of examples of usage, including how to show the
-results of the running computation on a ``TkMonitor``.
-
-The plac server
--------------------------------------------------------
-
-A command-line oriented interface can be easily converted into a
-socket-based interface. Starting from release 0.7 plac features
-a builtin server which is able to accept commands from multiple
-clients and to execute them. The server works by instantiating
-a separate interpreter for each client, so that if a client interpreter
-dies for any reason the other interpreters keep working.
-To avoid external dependencies the server is based on the ``asynchat``
-module in the standard library, but it would not be difficult to
-replace the server with a different one (for instance, a Twisted server).
-Since ``asynchat``-based servers are asynchronous, any blocking command
-in the interpreter should be run in a separated process or thread.
-The default port for the plac_ server is 2199, and the command to
-signal end-of-connection is EOF.
-For instance, here is how you could manage remote import on a database
-(say a SQLite db):
-
-.. include:: server_ex.py
- :literal:
-
-You can connect to the server with ``telnet`` on port 2199, as follows::
-
- $ telnet localhost 2199
- Trying ::1...
- Trying 127.0.0.1...
- Connected to localhost.
- Escape character is '^]'.
- i> import_file f1
- i> .list
- <ThreadedTask 1 [import_file f1] RUNNING>
- i> .out
- Imported 100 lines
- Imported 200 lines
- i> EOF
- Connection closed by foreign host.
-
-Summary
--------------------------------------------------------
-
-Once plac_ claimed to be the easiest command-line arguments parser
-in the world. Having read this document you may think that it is not
-so easy after all. But it is a false impression. Actually the
-rules are quite simple:
-
-1.
- if you want to implement a command-line script, use ``plac.call``;
-
-2.
- if you want to implement a command interpreter, use ``plac.Interpreter``:
-
- - for an interactive interpreter, call the ``.interact`` method;
- - for an batch interpreter, call the ``.execute`` method;
-
-3. for testing call the ``Interpreter.check`` method in the appropriate context
- or use the ``Interpreter.doctest`` feature;
-
-4. if you need to go at a lower level, you may need to call the
- ``Interpreter.send`` method 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.
-
-6. the ``.start_server`` method starts an asynchronous server on the
- given port number (default 2199)
-
-Moreover, remember that ``plac_runner.py`` is your friend.
-
-------
-
-Appendix: custom annotation objects
---------------------------------------------------------
-
-Internally plac_ uses an ``Annotation`` class to convert the tuples
-in the function signature into annotation objects, i.e. objects with
-six attributes ``help, kind, short, type, choices, metavar``.
-
-Advanced users can implement their own annotation objects.
-For instance, here is an example of how you could implement annotations for
-positional arguments:
-
-.. include:: annotations.py
- :literal:
-
-You can use such annotations objects as follows:
-
-.. include:: example11.py
- :literal:
-
-Here is the usage message you get:
-
-.. include:: example11.help
- :literal:
-
-You can go on and define ``Option`` and ``Flag`` classes, if you like.
-Using custom annotation objects you could do advanced things like extracting the
-annotations from a configuration file or from a database, but I expect such
-use cases to be quite rare: the default mechanism should work
-pretty well for most users.
-
-.. _argparse: http://argparse.googlecode.com
-.. _optparse: http://docs.python.org/library/optparse.html
-.. _getopt: http://docs.python.org/library/getopt.html
-.. _optionparse: http://code.activestate.com/recipes/278844-parsing-the-command-line/
-.. _plac: http://pypi.python.org/pypi/plac
-.. _scaling down: http://www.welton.it/articles/scalable_systems
-.. _ArgumentParser: http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html
-.. _argparse.FileType: http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType
-.. _Clap: http://pypi.python.org/pypi/Clap/0.7
-.. _OptionParser: http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser
-.. _SQLAlchemy: http://www.sqlalchemy.org/
-.. _SqlSoup: http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html
-.. _advanced usage document: in-writing
-.. _twill: http://twill.idyll.org/
-.. _basic documentation: http://plac.googlecode.com/hg/doc/plac.html
-.. _shlex: http://docs.python.org/library/shlex.html
-.. _multiprocessing: http://docs.python.org/library/multiprocessing.html
-.. _distutils: http://docs.python.org/distutils/
-.. _cmd: http://docs.python.org/library/cmd.html
-.. _rlwrap: http://freshmeat.net/projects/rlwrap/
-.. _pyreadline: http://ipython.scipy.org/moin/PyReadline/Intro
diff --git a/plac/doc/plac_core.txt b/plac/doc/plac_core.txt
deleted file mode 100644
index c4ffc9d..0000000
--- a/plac/doc/plac_core.txt
+++ /dev/null
@@ -1,793 +0,0 @@
-Plac: Parsing the Command Line the Easy Way
-=====================================================================
-
-:Author: Michele Simionato
-:E-mail: michele.simionato@gmail.com
-:Date: June 2011
-:Download page: http://pypi.python.org/pypi/plac
-:Project page: http://plac.googlecode.com/hg/doc/plac.html
-:Requires: Python 2.3+
-:Installation: ``easy_install -U plac``
-:License: BSD license
-
-.. contents::
-
-The importance of scaling down
-------------------------------------------------
-
-There is no want of command line arguments parsers in the Python
-world. The standard library alone contains three different modules:
-getopt_ (from the stone age),
-optparse_ (from Python 2.3) and argparse_ (from Python 2.7). All of
-them are quite powerful and especially argparse_ is an industrial
-strength solution; unfortunately, all of them feature a non-zero learning
-curve and a certain verbosity. They do not scale down well, at
-least in my opinion.
-
-It should not be necessary to stress the importance `scaling down`_;
-nevertheless, a lot of people are obsessed with features and concerned with
-the possibility of scaling up, forgetting the equally important
-issue of scaling down. This is an old meme in
-the computing world: programs should address the common cases simply and
-simple things should be kept simple, while at the same keeping
-difficult things possible. plac_ adhere as much as possible to this
-philosophy and it is designed to handle well the simple cases, while
-retaining the ability to handle complex cases by relying on the
-underlying power of argparse_.
-
-Technically plac_ is just a simple wrapper over argparse_ which hides
-most of its complexity by using a declarative interface: the argument
-parser is inferred rather than written down by imperatively. Still, plac_ is
-surprisingly scalable upwards, even without using the underlying
-argparse_. I have been using Python for 8 years and in my experience
-it is extremely unlikely that you will ever need to go beyond the
-features provided by the declarative interface of plac_: they should
-be more than enough for 99.9% of the use cases.
-
-plac_ is targetting especially unsophisticated users,
-programmers, sys-admins, scientists and in general people writing
-throw-away scripts for themselves, choosing the command line
-interface because it is the quick and simple. Such users are not
-interested in features, they are interested in a small learning curve:
-they just want to be able to write a simple command line tool from a
-simple specification, not to build a command-line parser by
-hand. Unfortunately, the modules in the standard library forces them
-to go the hard way. They are designed to implement power user tools
-and they have a non-trivial learning curve. On the contrary, plac_
-is designed to be simple to use and extremely concise, as the examples
-below will show.
-
-Scripts with required arguments
----------------------------------------------
-
-Let me start with the simplest possible thing: a script that takes a
-single argument and does something to it. It cannot get simpler
-than that, unless you consider a script without command-line
-arguments, where there is nothing to parse. Still, it is a use
-case *extremely common*: I need to write scripts like that nearly
-every day, I wrote hundreds of them in the last few years and I have
-never been happy. Here is a typical example of code I have been
-writing by hand for years:
-
-.. include:: example1.py
- :literal:
-
-As you see the whole ``if __name__ == '__main__'`` block (nine lines)
-is essentially boilerplate that should not exist. Actually I think
-the language should recognize the main function and pass to it the
-command-line arguments automatically; unfortunaly this is unlikely to
-happen. I have been writing boilerplate like this in hundreds of
-scripts for years, and every time I *hate* it. The purpose of using a
-scripting language is convenience and trivial things should be
-trivial. Unfortunately the standard library does not help for this
-incredibly common use case. Using getopt_ and optparse_ does not help,
-since they are intended to manage options and not positional
-arguments; the argparse_ module helps a bit and it is able to reduce
-the boilerplate from nine lines to six lines:
-
-.. include:: example2.py
- :literal:
-
-However, it just feels too complex to instantiate a class and to
-define a parser by hand for such a trivial task.
-
-The plac_ module is designed to manage well such use cases, and it is able
-to reduce the original nine lines of boiler plate to two lines. With the
-plac_ module all you need to write is
-
-.. include:: example3.py
- :literal:
-
-The plac_ module provides for free (actually the work is done by the
-underlying argparse_ module) a nice usage message::
-
- $ python example3.py -h
-
-.. include:: example3.help
- :literal:
-
-Moreover plac_ manages the case of missing arguments and of too many arguments.
-This is only the tip of the iceberg: plac_ is able to do much more than that.
-
-Scripts with default arguments
---------------------------------------------------
-
-The need to have suitable defaults for command-line scripts is quite
-common. For instance I have encountered this use case at work hundreds
-of times:
-
-.. include:: example4.py
- :literal:
-
-Here I want to perform a query on a database table, by extracting the
-most recent data: it makes sense for ``today`` to be a default argument.
-If there is a most used table (in this example a table called ``'product'``)
-it also makes sense to make it a default argument. Performing the parsing
-of the command-line arguments by hand takes 8 ugly lines of boilerplate
-(using argparse_ would require about the same number of lines).
-With plac_ the entire ``__main__`` block reduces to the usual two lines::
-
- if __name__ == '__main__':
- import plac; plac.call(main)
-
-In other words, six lines of boilerplate have been removed, and we get
-the usage message for free:
-
-.. include:: example5.help
- :literal:
-
-Notice that by default plac_ prints the string representation
-of the default values (with square brackets) in the usage message.
-plac_ manages transparently even the case when you want to pass a
-variable number of arguments. Here is an example, a script running
-on a database a series of SQL scripts:
-
-.. include:: example7.py
- :literal:
-
-Here is the usage message:
-
-.. include:: example7.help
- :literal:
-
-The examples here should have made clear that *plac is able to figure out
-the command-line arguments parser to use from the signature of the main
-function*. This is the whole idea behind plac_: if the intent is clear,
-let's the machine take care of the details.
-
-plac_ is inspired to an old Python Cookbook recipe of mine (optionparse_), in
-the sense that it delivers the programmer from the burden of writing
-the parser, but is less of a hack: instead of extracting the parser
-from the docstring of the module, it extracts it from the signature of
-the ``main`` function.
-
-The idea comes from the `function annotations` concept, a new
-feature of Python 3. An example is worth a thousand words, so here
-it is:
-
-.. include:: example7_.py
- :literal:
-
-Here the arguments of the ``main`` function have been annotated with
-strings which are intented to be used in the help message:
-
-.. include:: example7_.help
- :literal:
-
-plac_ is able to recognize much more complex annotations, as
-I will show in the next paragraphs.
-
-
-Scripts with options (and smart options)
------------------------------------------
-
-It is surprising how few command-line scripts with options I have
-written over the years (probably less than a hundred), compared to the
-number of scripts with positional arguments I wrote (certainly more
-than a thousand of them). Still, this use case cannot be neglected.
-The standard library modules (all of them) are quite verbose when it
-comes to specifying the options and frankly I have never used them
-directly. Instead, I have always relied on the
-optionparse_ recipe, which provides a convenient wrapper over
-optionparse_. Alternatively, in the simplest cases, I have just
-performed the parsing by hand. In plac_ the parser is inferred by the
-function annotations. Here is an example:
-
-.. include:: example8.py
- :literal:
-
-Here the argument ``command`` has been annotated with the tuple
-``("SQL query", 'option', 'c')``: the first string is the help string
-which will appear in the usage message, the second string tells plac_
-that ``command`` is an option and the third string that there is also
-a short form of the option ``-c``, the long form being ``--command``.
-The usage message is the following:
-
-.. include:: example8.help
- :literal:
-
-Here are two examples of usage::
-
- $ python3 example8.py -c"select * from table" dsn
- executing select * from table on dsn
-
- $ python3 example8.py --command="select * from table" dsn
- executing select * from table on dsn
-
-The third argument in the function annotation can be omitted: in such
-case it will be assumed to be ``None``. The consequence is that
-the usual dichotomy between long and short options (GNU-style options)
-disappears: we get *smart options*, which have the single character prefix
-of short options and behave like both long and short options, since
-they can be abbreviated. Here is an example featuring smart options:
-
-.. include:: example6.py
- :literal:
-
-.. include:: example6.help
- :literal:
-
-The following are all valid invocations ot the script::
-
- $ python3 example6.py -c "select" dsn
- executing 'select' on dsn
- $ python3 example6.py -com "select" dsn
- executing 'select' on dsn
- $ python3 example6.py -command="select" dsn
- executing 'select' on dsn
-
-Notice that the form ``-command=SQL`` is recognized only for the full
-option, not for its abbreviations::
-
- $ python3 example6.py -com="select" dsn
- usage: example6.py [-h] [-command COMMAND] dsn
- example6.py: error: unrecognized arguments: -com=select
-
-If the option is not passed, the variable ``command``
-will get the value ``None``. However, it is possible to specify a non-trivial
-default. Here is an example:
-
-.. include:: example8_.py
- :literal:
-
-Notice that the default value appears in the help message:
-
-.. include:: example8_.help
- :literal:
-
-When you run the script and you do not pass the ``-command`` option, the
-default query will be executed::
-
- $ python3 example8_.py dsn
- executing 'select * from table' on dsn
-
-Scripts with flags
---------------------
-
-plac_ is able to recognize flags, i.e. boolean options which are
-``True`` if they are passed to the command line and ``False``
-if they are absent. Here is an example:
-
-.. include:: example9.py
- :literal:
-
-.. include:: example9.help
- :literal:
-
-::
-
- $ python3 example9.py -v dsn
- connecting to dsn
-
-Notice that it is an error trying to specify a default for flags: the
-default value for a flag is always ``False``. If you feel the need to
-implement non-boolean flags, you should use an option with two
-choices, as explained in the "more features" section.
-
-For consistency with the way the usage message is printed, I suggest
-you to follow the Flag-Option-Required-Default (FORD) convention: in
-the ``main`` function write first the flag arguments, then the option
-arguments, then the required arguments and finally the default
-arguments. This is just a convention and you are not forced to use it,
-except for the default arguments (including the varargs) which must
-stay at the end as it is required by the Python syntax.
-
-I also suggests to specify a one-character abbreviation for flags: in
-this way you can use the GNU-style composition of flags (i.e. ``-zxvf``
-is an abbreviation of ``-z -x -v -f``). I usually do not provide
-the one-character abbreviation for options, since it does not make sense
-to compose them.
-
-plac for Python 2.X users
---------------------------------------------------
-
-I do not use Python 3. At work we are just starting to think about
-migrating to Python 2.6. It will take years before we
-think to migrate to Python 3. I am pretty much sure most Pythonistas
-are in the same situation. Therefore plac_ provides a way to work
-with function annotations even in Python 2.X (including Python 2.3).
-There is no magic involved; you just need to add the annotations
-by hand. For instance the annotated function declaration
-
-::
-
- def main(dsn: "Database dsn", *scripts: "SQL scripts"):
- ...
-
-is equivalent to the following code::
-
- def main(dsn, *scripts):
- ...
- main.__annotations__ = dict(
- dsn="Database dsn",
- scripts="SQL scripts")
-
-One should be careful to match the keys of the annotation dictionary
-with the names of the arguments in the annotated function; for lazy
-people with Python 2.4 available the simplest way is to use the
-``plac.annotations`` decorator that performs the check for you::
-
- @plac.annotations(
- dsn="Database dsn",
- scripts="SQL scripts")
- def main(dsn, *scripts):
- ...
-
-In the rest of this article I will assume that you are using Python 2.X with
-X >= 4 and I will use the ``plac.annotations`` decorator. Notice however
-that the core features of plac_ run even on Python 2.3.
-
-More features
---------------------------------------------------
-
-One of the goals of plac_ is to have a learning curve of *minutes* for
-its core features, compared to the learning curve of *hours* of
-argparse_. In order to reach this goal, I have *not* sacrificed all
-the features of argparse_. Actually a lot of argparse_ power persists
-in plac_. Until now, I have only showed simple annotations, but in
-general an annotation is a 6-tuple of the form
-
- ``(help, kind, abbrev, type, choices, metavar)``
-
-where ``help`` is the help message, ``kind`` is a string in the set {
-``"flag"``, ``"option"``, ``"positional"``}, ``abbrev`` is a
-one-character string or ``None``, ``type`` is a callable taking a
-string in input,
-``choices`` is a discrete sequence of values and ``metavar`` is a string.
-
-``type`` is used to automagically convert the command line arguments
-from the string type to any Python type; by default there is no
-conversion and ``type=None``.
-
-``choices`` is used to restrict the number of the valid
-options; by default there is no restriction i.e. ``choices=None``.
-
-``metavar`` has two meanings. For a positional argument it is used to
-change the argument name in the usage message (and only there). By
-default the metavar is ``None`` and the name in the usage message is
-the same as the argument name. For an option
-the ``metavar`` is used differently in the usage message, which has
-now the form ``[--option-name METAVAR]``. If the ``metavar`` is ``None``,
-then it is equal to the uppercased name of the argument, unless the
-argument has a default and in such a case is equal to the stringified
-form of the default.
-
-Here is an example showing many of the features (copied from the
-argparse_ documentation):
-
-.. include:: example10.py
- :literal:
-
-Here is the usage:
-
-.. include:: example10.help
- :literal:
-
-Notice that the docstring of the ``main`` function has been automatically added
-to the usage message. Here are a couple of examples of use::
-
- $ python example10.py add 1 2 3 4
- 10.0
- $ python example10.py mul 1 2 3 4
- 24.0
- $ python example10.py ad 1 2 3 4 # a mispelling error
- usage: example10.py [-h] {add,mul} [n [n ...]]
- example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul')
-
-``plac.call`` can also be used in doctests like this:
-
->>> import plac, example10
->>> plac.call(example10.main, ['add', '1', '2'])
-3.0
-
-``plac.call`` works for generators too:
-
->>> def main(n):
-... for i in range(int(n)):
-... yield i
->>> plac.call(main, ['3'])
-[0, 1, 2]
-
-Internally ``plac.call`` tries to convert the output of the main function
-into a list, if possible. If the output is not iterable or it is a
-string, it is left unchanged, but if it is iterable it is converted.
-In particular, generator objects are exhausted by ``plac.call``.
-
-This behavior avoids mistakes like forgetting of applying
-``list(result)`` to the result of ``plac.call``; moreover it makes
-errors visible early, and avoids mistakes in code like the following::
-
- try:
- result = plac.call(main, args)
- except:
- # do something
-
-Without the "listify" functionality, a main function returning a
-generator object would not raise any exception until the generator
-is iterated over.
-
-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
----------------------------------------
-
-Here is a more realistic script using most of the features of plac_ to
-run SQL queries on a database by relying on SQLAlchemy_. Notice the usage
-of the ``type`` feature to automagically convert a SQLAlchemy connection
-string into a SqlSoup_ object:
-
-.. include:: dbcli.py
- :literal:
-
-You can see the *yield-is-print* pattern here: instead of using
-``print`` in the main function, I use ``yield``, and I perform the
-print in the ``__main__`` block. The advantage of the pattern is that
-tests invoking ``plac.call`` and checking the result become trivial:
-had I performed the printing in the main function, the test would have
-involved an ugly hack like redirecting ``sys.stdout`` to a
-``StringIO`` object.
-
-Here is the usage message:
-
-.. include:: dbcli.help
- :literal:
-
-You can check for yourself that the script works.
-
-Keyword arguments
----------------------------------------
-
-Starting from release 0.4, plac_ supports keyword arguments. In
-practice that means that if your main function has keyword arguments,
-plac_ treats specially arguments of the form ``"name=value"`` in the
-command line. Here is an example:
-
-.. include:: example12.py
- :literal:
-
-Here is the generated usage message:
-
-.. include:: example12.help
- :literal:
-
-Here is how you call the script::
-
- $ python example12.py -o X a1 a2 name=value
- opt=X
- args=('a1', 'a2')
- kw={'name': 'value'}
-
-When using keyword arguments, one must be careful to use names which
-are not alreay taken; for instance in this examples the name ``opt``
-is taken::
-
- $ python example12.py 1 2 kw1=1 kw2=2 opt=0
- usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
- example12.py: error: colliding keyword arguments: opt
-
-The names taken are the names of the flags, of the options, and of the
-positional arguments, excepted varargs and keywords. This limitation
-is a consequence of the way the argument names are managed in function calls
-by the Python language.
-
-Final example: a shelve interface
-----------------------------------------------------------
-
-Here is a less trivial example for the keyword arguments feature.
-The use case is the following: suppose we have stored the
-configuration parameters of a given application into a Python shelve
-and we need a command-line tool to edit the shelve.
-A possible implementation using plac_ could be the following:
-
-.. include:: ishelve.py
- :literal:
-
-A few notes are in order:
-
-1. I have disabled the ordinary help provided by argparse_ and I have
- implemented a custom help command.
-2. I have changed the prefix character used to recognize the options
- to a dot.
-3. Keyword arguments recognition (in the ``**setters``) is used to make it
- possible to store a value in the shelve with the syntax
- ``param_name=param_value``.
-4. ``*params`` are used to retrieve parameters from the shelve and some
- error checking is performed in the case of missing parameters
-5. A command to clear the shelve is implemented as a flag (``.clear``).
-6. A command to delete a given parameter is implemented as an option
- (``.delete``).
-7. There is an option with default (``.filename=conf.shelve``) to store
- the filename of the shelve.
-8. All things considered, the code looks like a poor man object oriented
- interface implemented with a chain of elifs instead of methods. Of course,
- plac_ can do better than that, but let me start from a low-level approach
- first.
-
-If you run ``ishelve.py`` without arguments you get the following
-message::
-
- $ python ishelve.py
- no arguments passed, use .help to see the available commands
-
-If you run ``ishelve.py`` with the option ``.h`` (or any abbreviation
-of ``.help``) you get::
-
- $ python ishelve.py .h
- Commands: .help, .showall, .clear, .delete
- <param> ...
- <param=value> ...
-
-You can check by hand that the tool work::
-
- $ python ishelve.py .clear # start from an empty shelve
- cleared the shelve
- $ python ishelve.py a=1 b=2
- setting a=1
- setting b=2
- $ python ishelve.py .showall
- b=2
- a=1
- $ python ishelve.py .del b # abbreviation for .delete
- deleted b
- $ python ishelve.py a
- 1
- $ python ishelve.py b
- b: not found
- $ python ishelve.py .cler # mispelled command
- usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
- [.filename /home/micheles/conf.shelve]
- [params [params ...]] [setters [setters ...]]
- ishelve.py: error: unrecognized arguments: .cler
-
-plac vs argparse
----------------------------------------------
-
-plac_ is opinionated and by design it does not try to make available
-all of the features of argparse_ in an easy way. In particular you
-should be aware of the following limitations/differences (the
-following assumes knowledge of argparse_):
-
-- plac does not support the destination concept: the destination
- coincides with the name of the argument, always. This restriction
- has some drawbacks. For instance, suppose you want to define a long
- option called ``--yield``. In this case the destination would be ``yield``,
- which is a Python keyword, and since you cannot introduce an
- argument with that name in a function definition, it is impossible
- to implement it. Your choices are to change the name of the long
- option, or to use argparse_ with a suitable destination.
-
-- plac_ does not support "required options". As the argparse_
- documentation puts it: *Required options are generally considered bad
- form - normal users expect options to be optional. You should avoid
- the use of required options whenever possible.* Notice that since
- argparse_ supports them, plac_ can manage them too, but not directly.
-
-- plac_ supports only regular boolean flags. argparse_ has the ability to
- define generalized two-value flags with values different from ``True``
- and ``False``. An earlier version of plac_ had this feature too, but
- since you can use options with two choices instead, and in any case
- the conversion from ``{True, False}`` to any couple of values
- can be trivially implemented with a ternary operator
- (``value1 if flag else value2``), I have removed it (KISS rules!).
-
-- plac_ does not support ``nargs`` options directly (it uses them internally,
- though, to implement flag recognition). The reason it that all the use
- cases of interest to me are covered by plac_ and did not feel the need
- to increase the learning curve by adding direct support for ``nargs``.
-
-- plac_ does support subparsers, but you must read the `advanced usage
- document`_ to see how it works.
-
-- plac_ does not support actions directly. This also
- looks like a feature too advanced for the goals of plac_. Notice however
- that the ability to define your own annotation objects (again, see
- the `advanced usage document`_) may mitigate the need for custom actions.
-
-plac_ can leverage directly on many argparse_ features.
-
-For instance, you can make invisible an argument in the usage message
-simply by using ``'==SUPPRESS=='`` as help string (or
-``argparse.SUPPRESS``). Similarly, you can use argparse.FileType_
-directly.
-
-It is also possible to pass options to the underlying
-``argparse.ArgumentParser`` object (currently it accepts the default
-arguments ``description``, ``epilog``, ``prog``, ``usage``,
-``add_help``, ``argument_default``, ``parents``, ``prefix_chars``,
-``fromfile_prefix_chars``, ``conflict_handler``, ``formatter_class``).
-It is enough to set such attributes on the ``main`` function. For
-instance
-
-::
-
- def main(...):
- pass
-
- main.add_help = False
-
-disables the recognition of the help flag ``-h, --help``. This
-mechanism does not look particularly elegant, but it works well
-enough. I assume that the typical user of plac_ will be happy with
-the defaults and would not want to change them; still it is possible
-if she wants to.
-
-For instance, by setting the ``description`` attribute, it is possible
-to add a comment to the usage message (by default the docstring of the
-``main`` function is used as description).
-
-It is also possible to change the option prefix; for
-instance if your script must run under Windows and you want to use "/"
-as option prefix you can add the line::
-
- main.prefix_chars='/-'
-
-The first prefix char (``/``) is used
-as the default for the recognition of options and flags;
-the second prefix char (``-``) is kept to keep the ``-h/--help`` option
-working: however you can disable it and reimplement it, if you like,
-as seen in the ``ishelve`` example.
-
-It is possible to access directly the underlying ArgumentParser_ object, by
-invoking the ``plac.parser_from`` utility function:
-
->>> import plac
->>> def main(arg):
-... pass
-...
->>> print(plac.parser_from(main)) #doctest: +ELLIPSIS
-ArgumentParser(prog=...)
-
-Internally ``plac.call`` uses ``plac.parser_from`` and adds the parser
-to the main function as an attribute. When ``plac.call(func)`` is
-invoked multiple time, the parser is re-used and not rebuilt from scratch again.
-
-I use ``plac.parser_from`` in the unit tests of the module, but regular
-users should not need to use it, unless they want to access *all*
-of the features of argparse_ directly without calling the main function.
-
-Interested readers should read the documentation of argparse_ to
-understand the meaning of the other options. If there is a set of
-options that you use very often, you may consider writing a decorator
-adding such options to the ``main`` function for you. For simplicity,
-plac_ does not perform any magic except the addition of the ``.p``
-attribute.
-
-plac vs the rest of the world
-------------------------------------------
-
-Originally plac_ boasted about being "the easiest command-line
-arguments parser in the world". Since then, people started pointing
-out to me various projects which are based on the same idea
-(extracting the parser from the main function signature) and are
-arguably even easier than plac_:
-
-- opterator_ by Dusty Phillips
-- CLIArgs_ by Pavel Panchekha
-- commandline_ by David Laban
-
-Luckily for me none of such projects had the idea of using
-function annotations and argparse_; as a consequence, they are
-no match for the capabilities of plac_.
-
-Of course, there are tons of other libraries to parse the command
-line. For instance Clap_ by Matthew Frazier which appeared on PyPI
-just the day before plac_; Clap_ is fine but it is certainly not
-easier than plac_.
-
-plac_ can also be used as a replacement of the cmd_ module in the standard
-library and as such it shares many features with the module cmd2_ by
-Catherine Devlin. However, this is completely coincidental, since I became
-aware of the cmd2_ module only after writing plac_.
-
-Command-line argument parsers keep coming out; between the newcomers I
-will notice `marrow.script`_ by Alice Bevan-McGregor, which is quite
-similar to plac_ in spirit, but does not rely on argparse_ at all.
-Argh_ by Andrey Mikhaylenko is also worth mentioning: it is also based
-on argparse_, it came after plac_ and I must give credit to the author
-for the choice of the name, much funnier than plac!
-
-The future
--------------------------------
-
-Currently the core of plac_ is around 200 lines of code, not counting blanks,
-comments and docstrings. I do not plan to extend the core much in the
-future. The idea is to keep the module short: it is and it should
-remain a little wrapper over argparse_. Actually I have thought about
-contributing the core back to argparse_ if plac_ becomes successfull
-and gains a reasonable number of users. For the moment it should be
-considered in alpha status.
-
-Notice that even if plac_ has been designed to be simple to use for
-simple stuff, its power should not be underestimated; it is actually a
-quite advanced tool with a domain of applicability which far exceeds
-the realm of command-line arguments parsers.
-
-Version 0.5 of plac_ doubled the code base and the documentation: it is
-based on the idea of using plac_ to implement command-line interpreters,
-i.e. something akin to the ``cmd`` module in the standard library, only better.
-The new features of plac_ are described in the `advanced usage document`_ .
-They are implemented in a separated module (``plac_ext.py``), since
-they require Python 2.5 to work, whereas ``plac_core.py`` only requires
-Python 2.3.
-
-Trivia: the story behind the name
------------------------------------------
-
-The plac_ project started very humbly: I just wanted to make
-easy_installable my old optionparse_ recipe, and to publish it on PyPI.
-The original name of plac_ was optionparser and the idea behind it was
-to build an OptionParser_ object from the docstring of the module.
-However, before doing that, I decided to check out the argparse_ module,
-since I knew it was going into Python 2.7 and Python 2.7 was coming out.
-Soon enough I realized two things:
-
-1. the single greatest idea of argparse_ was unifying the positional arguments
- and the options in a single namespace object;
-2. parsing the docstring was so old-fashioned, considering the existence
- of functions annotations in Python 3.
-
-Putting together these two observations with the original idea of inferring the
-parser I decided to build an ArgumentParser_ object from function
-annotations. The ``optionparser`` name was ruled out, since I was
-now using argparse_; a name like ``argparse_plus`` was also ruled out,
-since the typical usage was completely different from the argparse_ usage.
-
-I made a research on PyPI and the name *clap* (Command Line Arguments Parser)
-was not taken, so I renamed everything to clap. After two days
-a Clap_ module appeared on PyPI <expletives deleted>!
-
-Having little imagination, I decided to rename everything again to plac,
-an anagram of clap: since it is a non-existing English name, I hope nobody
-will steal it from me!
-
-That concludes the section about the basic usage of plac_. You are now ready to
-read about the advanced usage.
-
-.. _argparse: http://argparse.googlecode.com
-.. _optparse: http://docs.python.org/library/optparse.html
-.. _getopt: http://docs.python.org/library/getopt.html
-.. _optionparse: http://code.activestate.com/recipes/278844-parsing-the-command-line/
-.. _plac: http://pypi.python.org/pypi/plac
-.. _scaling down: http://www.welton.it/articles/scalable_systems
-.. _ArgumentParser: http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html
-.. _argparse.FileType: http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType
-.. _Clap: http://pypi.python.org/pypi/Clap/0.7
-.. _OptionParser: http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser
-.. _SQLAlchemy: http://www.sqlalchemy.org/
-.. _SqlSoup: http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html
-.. _CLIArgs: http://pypi.python.org/pypi/CLIArgs
-.. _opterator: http://pypi.python.org/pypi/opterator
-.. _advanced usage document: in-writing
-.. _cmd2: http://packages.python.org/cmd2/
-.. _cmd: http://docs.python.org/library/cmd.html
-.. _marrow.script: https://github.com/pulp/marrow.script
-.. _commandline: http://pypi.python.org/pypi/commandline
-.. _argh: http://packages.python.org/argh
diff --git a/plac/doc/read_stdin.py b/plac/doc/read_stdin.py
deleted file mode 100644
index 584644f..0000000
--- a/plac/doc/read_stdin.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""
-You can run this script as
-$ python read_stdin.py < ishelve.bat
-"""
-from __future__ import with_statement
-import sys
-from ishelve import ishelve
-import plac
-
-if __name__ == '__main__':
- with plac.Interpreter(ishelve) as i:
- for line in sys.stdin:
- print(i.send(line))
diff --git a/plac/doc/server_ex.py b/plac/doc/server_ex.py
deleted file mode 100644
index e3d76ec..0000000
--- a/plac/doc/server_ex.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import plac
-from importer2 import FakeImporter
-
-def main(port=2199):
- main = FakeImporter('dsn')
- plac.Interpreter(main).start_server(port)
-
-if __name__ == '__main__':
- plac.call(main)
diff --git a/plac/doc/shelve_interpreter.help b/plac/doc/shelve_interpreter.help
deleted file mode 100644
index f666445..0000000
--- a/plac/doc/shelve_interpreter.help
+++ /dev/null
@@ -1,13 +0,0 @@
-usage: shelve_interpreter.py [-h] [-interactive]
- [subcommands [subcommands ...]]
-
- This script works both interactively and non-interactively.
- Use .help to see the internal commands.
-
-
-positional arguments:
- subcommands the commands of the underlying ishelve interpreter
-
-optional arguments:
- -h, --help show this help message and exit
- -interactive start interactive interface
diff --git a/plac/doc/shelve_interpreter.py b/plac/doc/shelve_interpreter.py
deleted file mode 100644
index b7fb688..0000000
--- a/plac/doc/shelve_interpreter.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# shelve_interpreter.py
-import plac, ishelve
-
-@plac.annotations(
- interactive=('start interactive interface', 'flag'),
- subcommands='the commands of the underlying ishelve interpreter')
-def main(interactive, *subcommands):
- """
- This script works both interactively and non-interactively.
- Use .help to see the internal commands.
- """
- if interactive:
- plac.Interpreter(ishelve.main).interact()
- else:
- for out in plac.call(ishelve.main, subcommands):
- print(out)
-
-if __name__ == '__main__':
- plac.call(main)
diff --git a/plac/doc/sql_interface.py b/plac/doc/sql_interface.py
deleted file mode 100644
index 67c6233..0000000
--- a/plac/doc/sql_interface.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import os, plac
-from sqlalchemy.ext.sqlsoup import SqlSoup
-
-SQLKEYWORDS = set(['help', '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, argstring):
- sql = 'SELECT ' + argstring
- for row in self.soup.bind.execute(sql):
- yield str(row) # the formatting can be much improved
-
-rl_input = plac.ReadlineInput(
- COMPLETIONS, histfile=os.path.expanduser('~/.sql_interface.history'),
- case_sensitive=False)
-
-def split_on_first_space(line, commentchar):
- return line.strip().split(' ', 1) # ignoring comments
-
-if __name__ == '__main__':
- plac.Interpreter.call(SqlInterface, split=split_on_first_space,
- stdin=rl_input, prompt='sql> ')
diff --git a/plac/doc/test_ishelve.py b/plac/doc/test_ishelve.py
deleted file mode 100644
index a34809d..0000000
--- a/plac/doc/test_ishelve.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# test_ishelve.py
-import plac, ishelve
-
-def test():
- assert plac.call(ishelve.main, ['.clear']) == ['cleared the shelve']
- assert plac.call(ishelve.main, ['a=1']) == ['setting a=1']
- assert plac.call(ishelve.main, ['a']) == ['1']
- assert plac.call(ishelve.main, ['.delete=a']) == ['deleted a']
- assert plac.call(ishelve.main, ['a']) == ['a: not found']
-
-if __name__ == '__main__':
- test()
diff --git a/plac/doc/test_ishelve_more.py b/plac/doc/test_ishelve_more.py
deleted file mode 100644
index 234ce0a..0000000
--- a/plac/doc/test_ishelve_more.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# test_ishelve_more.py
-from __future__ import with_statement
-import plac, ishelve
-
-def test():
- with plac.Interpreter(ishelve.main) as i:
- i.check('.clear', 'cleared the shelve')
- i.check('a=1', 'setting a=1')
- i.check('a', '1')
- i.check('.delete=a', 'deleted a')
- i.check('a', 'a: not found')
diff --git a/plac/doc/test_pi.py b/plac/doc/test_pi.py
deleted file mode 100644
index 9f1fbd0..0000000
--- a/plac/doc/test_pi.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import time
-from picalculator import PiCalculator
-
-def test():
- pc = PiCalculator(10, 'T')
- tasks = pc.submit_tasks()
- for task in tasks:
- task.run()
- print(sum(task.result for task in tasks)/pc.n_cpu)
- pc.close()
-
diff --git a/plac/doc/test_plac.py b/plac/doc/test_plac.py
deleted file mode 100644
index 414e2a7..0000000
--- a/plac/doc/test_plac.py
+++ /dev/null
@@ -1,231 +0,0 @@
-"""
-The tests are runnable with nose, with py.test, or even as standalone script
-"""
-
-import os, sys, datetime, doctest, subprocess
-import plac, plac_core
-
-sys_argv0 = sys.argv[0]
-os.chdir(os.path.dirname(__file__) or '.') # work in the current directory
-
-######################## helpers #######################
-
-def fix_today(text):
- return text.replace('YYYY-MM-DD', str(datetime.date.today()))
-
-def expect(errclass, func, *args, **kw):
- try:
- func(*args, **kw)
- except errclass:
- pass
- else:
- raise RuntimeError('%s expected, got none!', errclass.__name__)
-
-def parser_from(f, **kw):
- f.__annotations__ = kw
- return plac.parser_from(f)
-
-def check_help(name):
- sys.argv[0] = name + '.py' # avoid issue with nosetests
- plac_core._parser_registry.clear() # makes different imports independent
- try:
- try:
- main = plac.import_main(name + '.py')
- except SyntaxError:
- if sys.version < '3': # expected for Python 2.X
- return
- else: # not expected for Python 3.X
- raise
- p = plac.parser_from(main)
- expected = fix_today(open(name + '.help').read()).strip()
- got = p.format_help().strip()
- assert got == expected, got
- finally:
- sys.argv[0] = sys_argv0
-
-####################### 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'))
-
-def test_p1():
- arg = p1.parse_args(['-d', 'foo', 'arg1', 'arg2'])
- assert arg.delete == 'foo'
- assert arg.args == ['arg1', 'arg2']
-
- arg = p1.parse_args([])
- assert arg.delete is None, arg.delete
- assert arg.args == [], arg.args
-
-p2 = parser_from(lambda arg1, delete, *args: None,
- delete=('delete a file', 'option', 'd'))
-
-def test_p2():
- arg = p2.parse_args(['-d', 'foo', 'arg1', 'arg2'])
- assert arg.delete == 'foo', arg.delete
- assert arg.arg1 == 'arg1', arg.arg1
- assert arg.args == ['arg2'], arg.args
-
- arg = p2.parse_args(['arg1'])
- assert arg.delete is None, arg.delete
- assert arg.args == [], arg.args
- assert arg, arg
-
- expect(SystemExit, p2.parse_args, [])
-
-p3 = parser_from(lambda arg1, delete: None,
- delete=('delete a file', 'option', 'd'))
-
-def test_p3():
- arg = p3.parse_args(['arg1'])
- assert arg.delete is None, arg.delete
- assert arg.arg1 == 'arg1', arg.args
-
- expect(SystemExit, p3.parse_args, ['arg1', 'arg2'])
- expect(SystemExit, p3.parse_args, [])
-
-p4 = parser_from(lambda delete, delete_all, color="black": None,
- delete=('delete a file', 'option', 'd'),
- delete_all=('delete all files', 'flag', 'a'),
- color=('color', 'option', 'c'))
-
-def test_p4():
- arg = p4.parse_args(['-a'])
- assert arg.delete_all is True, arg.delete_all
-
- arg = p4.parse_args([])
-
- arg = p4.parse_args(['--color=black'])
- assert arg.color == 'black'
-
- arg = p4.parse_args(['--color=red'])
- assert arg.color == 'red'
-
-def test_flag_with_default():
- expect(TypeError, parser_from, lambda yes_or_no='no': None,
- yes_or_no=('A yes/no flag', 'flag', 'f'))
-
-def assert_usage(parser, expected):
- usage = parser.format_usage()
- assert usage == expected, usage
-
-def test_metavar_no_defaults():
- sys.argv[0] = 'test_plac.py'
-
- # positional
- p = parser_from(lambda x: None,
- x=('first argument', 'positional', None, str, [], 'METAVAR'))
- assert_usage(p, 'usage: test_plac.py [-h] METAVAR\n')
-
- # option
- p = parser_from(lambda x: None,
- x=('first argument', 'option', None, str, [], 'METAVAR'))
- assert_usage(p, 'usage: test_plac.py [-h] [-x METAVAR]\n')
- sys.argv[0] = sys_argv0
-
-def test_metavar_with_defaults():
- sys.argv[0] = 'test_plac.py'
-
- # positional
- p = parser_from(lambda x='a': None,
- x=('first argument', 'positional', None, str, [], 'METAVAR'))
- assert_usage(p, 'usage: test_plac.py [-h] [METAVAR]\n')
-
- # option
- p = parser_from(lambda x='a': None,
- x=('first argument', 'option', None, str, [], 'METAVAR'))
- assert_usage(p, 'usage: test_plac.py [-h] [-x METAVAR]\n')
-
- p = parser_from(lambda x='a': None,
- x=('first argument', 'option', None, str, []))
- assert_usage(p, 'usage: test_plac.py [-h] [-x a]\n')
-
- sys.argv[0] = sys_argv0
-
-def test_kwargs():
- def main(opt, arg1, *args, **kw):
- print(opt, arg1)
- return args, kw
- main.__annotations__ = dict(opt=('Option', 'option'))
- argskw = plac.call(main, ['arg1', 'arg2', 'a=1', 'b=2'])
- assert argskw == [('arg2',), {'a': '1', 'b': '2'}], argskw
-
- argskw = plac.call(main, ['arg1', 'arg2', 'a=1', '-o', '2'])
- assert argskw == [('arg2',), {'a': '1'}], argskw
-
- expect(SystemExit, plac.call, main, ['arg1', 'arg2', 'a=1', 'opt=2'])
-
-class Cmds(object):
- add_help = False
- commands = 'help', 'commit'
- def help(self, name):
- return 'help', name
- def commit(self):
- return 'commit'
-
-cmds = Cmds()
-
-def test_cmds():
- assert 'commit' == plac.call(cmds, ['commit'])
- assert ['help', 'foo'] == plac.call(cmds, ['help', 'foo'])
- expect(SystemExit, plac.call, cmds, [])
-
-def test_cmd_abbrevs():
- assert 'commit' == plac.call(cmds, ['comm'])
- assert ['help', 'foo'] == plac.call(cmds, ['h', 'foo'])
- expect(SystemExit, plac.call, cmds, ['foo'])
-
-def test_sub_help():
- c = Cmds()
- c.add_help = True
- expect(SystemExit, plac.call, c, ['commit', '-h'])
-
-def test_yield():
- def main():
- for i in (1, 2, 3):
- yield i
- assert plac.call(main, []) == [1, 2, 3]
-
-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'):
- yield check_script, ['plac_runner.py', '-b', batch]
-
-def test_placet():
- for placet in os.listdir('.'):
- if placet.endswith('.placet'):
- yield check_script, ['plac_runner.py', '-t', placet]
-
-if __name__ == '__main__':
- n = 0
- for name, test in sorted(globals().items()):
- if name.startswith('test_'):
- print('Running ' + name)
- maybegen = test()
- if hasattr(maybegen, '__iter__'):
- for func, arg in maybegen:
- func(arg)
- n += 1
- else:
- n +=1
- print('Executed %d tests OK' % n)
diff --git a/plac/doc/test_runp.py b/plac/doc/test_runp.py
deleted file mode 100644
index 4cdd494..0000000
--- a/plac/doc/test_runp.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-This test should work on Linux if you have Tkinter installed.
-"""
-
-from __future__ import with_statement
-import plac, plac_tk, time
-
-def gen(n):
- for i in range(n):
- yield str(i)
- time.sleep(.1)
-
-def test():
- tasks = plac.runp([gen(3), gen(5), gen(10)])
- for t in tasks:
- t.result
- t.man.stop()
-
-def test_tkmonitor():
- mon = plac_tk.TkMonitor('tkmon')
- tasks = plac.runp([gen(3), gen(5), gen(10)], monitors=[mon])
- for t in tasks:
- t.result
- t.man.stop()
diff --git a/plac/doc/test_server.py b/plac/doc/test_server.py
deleted file mode 100644
index adcc395..0000000
--- a/plac/doc/test_server.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import multiprocessing, subprocess, random, time
-import plac
-from ishelve2 import ShelveInterface
-
-i = plac.Interpreter(ShelveInterface(configfile=None))
-
-COMMANDS = ['''\
-help
-set a 1
-''',
-'''\
-set b 1
-wrong command
-showall
-''']
-
-def telnet(commands, port):
- po = subprocess.Popen(['telnet', 'localhost', str(port)],
- stdin=subprocess.PIPE)
- try:
- for cmd in commands.splitlines():
- po.stdin.write(cmd + '\n')
- time.sleep(.1) # wait a bit for the server to answer
- finally:
- po.stdin.close()
-
-def test():
- port = random.choice(range(2000, 20000))
- server = multiprocessing.Process(target=i.start_server, args=(port,))
- server.start()
- clients = []
- for cmds in COMMANDS:
- cl = multiprocessing.Process(target=telnet, args=(cmds, port))
- clients.append(cl)
- cl.start()
- for cl in clients:
- cl.join()
- server.terminate()
diff --git a/plac/doc/vcs.help b/plac/doc/vcs.help
deleted file mode 100644
index 51bd34a..0000000
--- a/plac/doc/vcs.help
+++ /dev/null
@@ -1,12 +0,0 @@
-usage: plac_runner.py vcs.py [-h] {status,commit,checkout} ...
-
-A Fake Version Control System
-
-optional arguments:
- -h, --help show this help message and exit
-
-subcommands:
- {status,commit,checkout}
- checkout A fake checkout command
- commit A fake commit command
- status A fake status command
diff --git a/plac/doc/vcs.py b/plac/doc/vcs.py
deleted file mode 100644
index c423fb0..0000000
--- a/plac/doc/vcs.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"A Fake Version Control System"
-
-import plac
-
-commands = 'checkout', 'commit', 'status'
-
-@plac.annotations(url='url of the source code')
-def checkout(url):
- "A fake checkout command"
- return ('checkout ', url)
-
-@plac.annotations(message=('commit message', 'option'))
-def commit(message):
- "A fake commit command"
- return ('commit ', message)
-
-@plac.annotations(quiet=('summary information', 'flag', 'q'))
-def status(quiet):
- "A fake status command"
- return ('status ', quiet)
-
-def __missing__(name):
- return 'Command %r does not exist' % name
-
-def __exit__(etype, exc, tb):
- "Will be called automatically at the end of the call/cmdloop"
- if etype in (None, GeneratorExit): # success
- print('ok')
-
-main = __import__(__name__) # the module imports itself!
diff --git a/plac/plac.py b/plac/plac.py
deleted file mode 100644
index 4776afc..0000000
--- a/plac/plac.py
+++ /dev/null
@@ -1,40 +0,0 @@
-########################## LICENCE ###############################
-##
-## Copyright (c) 2010-2011, Michele Simionato
-## All rights reserved.
-##
-## Redistributions of source code must retain the above copyright
-## notice, this list of conditions and the following disclaimer.
-## Redistributions in bytecode form must reproduce the above copyright
-## notice, this list of conditions and the following disclaimer in
-## the documentation and/or other materials provided with the
-## distribution.
-
-## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-## DAMAGE.
-
-"""
-See doc/plac.pdf, doc/plac_adv.pdf for the documentation.
-"""
-
-__version__ = '0.9.0'
-
-from plac_core import *
-
-if sys.version >= '2.5':
- from plac_ext import (Interpreter, import_main, ReadlineInput,
- stdout, runp, Monitor, default_help)
- try:
- from plac_tk import TkMonitor
- except ImportError:
- pass
diff --git a/plac/plac_core.py b/plac/plac_core.py
deleted file mode 100644
index 7b0ad7f..0000000
--- a/plac/plac_core.py
+++ /dev/null
@@ -1,312 +0,0 @@
-# this module should be kept Python 2.3 compatible
-import re, sys, inspect, argparse
-if sys.version >= '3':
- from inspect import getfullargspec
-else:
- class getfullargspec(object):
- "A quick and dirty replacement for getfullargspec for Python 2.X"
- def __init__(self, f):
- self.args, self.varargs, self.varkw, self.defaults = \
- inspect.getargspec(f)
- self.annotations = getattr(f, '__annotations__', {})
-try:
- set
-except NameError: # Python 2.3
- from sets import Set as set
-from gettext import gettext as _
-
-
-def getargspec(callableobj):
- """Given a callable return an object with attributes .args, .varargs,
- .varkw, .defaults. It tries to do the "right thing" with functions,
- methods, classes and generic callables."""
- if inspect.isfunction(callableobj):
- argspec = getfullargspec(callableobj)
- elif inspect.ismethod(callableobj):
- argspec = getfullargspec(callableobj)
- del argspec.args[0] # remove first argument
- elif inspect.isclass(callableobj):
- if callableobj.__init__ is object.__init__: # to avoid an error
- argspec = getfullargspec(lambda self: None)
- else:
- argspec = getfullargspec(callableobj.__init__)
- del argspec.args[0] # remove first argument
- elif hasattr(callableobj, '__call__'):
- argspec = getfullargspec(callableobj.__call__)
- del argspec.args[0] # remove first argument
- else:
- raise TypeError(_('Could not determine the signature of ') +
- str(callableobj))
- return argspec
-
-def annotations(**ann):
- """
- Returns a decorator annotating a function with the given annotations.
- This is a trick to support function annotations in Python 2.X.
- """
- def annotate(f):
- fas = getfullargspec(f)
- args = fas.args
- if fas.varargs:
- args.append(fas.varargs)
- if fas.varkw:
- args.append(fas.varkw)
- for argname in ann:
- if argname not in args:
- raise NameError(
- _('Annotating non-existing argument: %s') % argname)
- f.__annotations__ = ann
- return f
- return annotate
-
-def is_annotation(obj):
- """
- An object is an annotation object if it has the attributes
- help, kind, abbrev, type, choices, metavar.
- """
- return (hasattr(obj, 'help') and hasattr(obj, 'kind') and
- hasattr(obj, 'abbrev') and hasattr(obj, 'type')
- and hasattr(obj, 'choices') and hasattr(obj, 'metavar'))
-
-class Annotation(object):
- def __init__(self, help=None, kind="positional", abbrev=None, type=None,
- choices=None, metavar=None):
- assert kind in ('positional', 'option', 'flag'), kind
- if kind == "positional":
- assert abbrev is None, abbrev
- self.help = help
- self.kind = kind
- self.abbrev = abbrev
- self.type = type
- self.choices = choices
- self.metavar = metavar
-
- def from_(cls, obj):
- "Helper to convert an object into an annotation, if needed"
- if is_annotation(obj):
- return obj # do nothing
- elif hasattr(obj, '__iter__') and not isinstance(obj, basestring):
- return cls(*obj)
- return cls(obj)
- from_ = classmethod(from_)
-
-NONE = object() # sentinel use to signal the absence of a default
-
-PARSER_CFG = getfullargspec(argparse.ArgumentParser.__init__).args[1:]
-# the default arguments accepted by an ArgumentParser object
-
-def pconf(obj):
- "Extracts the configuration of the underlying ArgumentParser from obj"
- 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
-
-_parser_registry = {}
-
-def parser_from(obj, **confparams):
- """
- obj can be a callable or an object with a .commands attribute.
- Returns an ArgumentParser.
- """
- 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)
- parser.obj = obj
- parser.case_sensitive = confparams.get(
- 'case_sensitive', getattr(obj, 'case_sensitive', True))
- if hasattr(obj, 'commands') and obj.commands and not inspect.isclass(obj):
- # a command container instance
- parser.addsubcommands(obj.commands, obj, 'subcommands')
- else:
- parser.populate_from(obj)
- return parser
-
-def _extract_kwargs(args):
- "Returns two lists: regular args and name=value args"
- arglist = []
- kwargs = {}
- for arg in args:
- match = re.match(r'([a-zA-Z_]\w*)=', arg)
- if match:
- name = match.group(1)
- kwargs[name] = arg[len(name)+1:]
- else:
- arglist.append(arg)
- return arglist, kwargs
-
-def _match_cmd(abbrev, commands, case_sensitive=True):
- "Extract the command name from an abbreviation or raise a NameError"
- if not case_sensitive:
- abbrev = abbrev.upper(); commands = [c.upper() for c in commands]
- perfect_matches = [name for name in commands if name == abbrev]
- if len(perfect_matches) == 1:
- return perfect_matches[0]
- matches = [name for name in commands if name.startswith(abbrev)]
- n = len(matches)
- if n == 1:
- return matches[0]
- elif n > 1:
- raise NameError(
- _('Ambiguous command %r: matching %s' % (abbrev, matches)))
-
-class ArgumentParser(argparse.ArgumentParser):
- """
- An ArgumentParser with .func and .argspec attributes, and possibly
- .commands and .subparsers.
- """
- case_sensitive = True
-
- def alias(self, arg):
- "Can be overridden to preprocess command-line arguments"
- return arg
-
- def consume(self, args):
- """Call the underlying function with the args. Works also for
- command containers, by dispatching to the right subparser."""
- arglist = map(self.alias, args)
- cmd = None
- if hasattr(self, 'subparsers'):
- subp, cmd = self._extract_subparser_cmd(arglist)
- if subp is None and cmd is not None:
- return cmd, self.missing(cmd)
- elif subp is not None: # use the subparser
- self = subp
- if hasattr(self, 'argspec') and self.argspec.varkw:
- arglist, kwargs = _extract_kwargs(arglist) # modify arglist!
- else:
- kwargs = {}
- if hasattr(self, 'argspec') and self.argspec.varargs:
- # ignore unrecognized arguments
- ns, extraopts = self.parse_known_args(arglist)
- else:
- ns, extraopts = self.parse_args(arglist), [] # may raise an exit
- 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 + extraopts), **kwargs)
-
- def _extract_subparser_cmd(self, arglist):
- "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(optprefix):
- cmd = _match_cmd(arg, name_parser_map, self.case_sensitive)
- del arglist[i]
- return name_parser_map.get(cmd), cmd or arg
- return None, None
-
- 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=title)
- elif title:
- self.add_argument_group(title=title) # populate ._action_groups
- prefixlen = len(getattr(obj, 'cmdprefix', ''))
- add_help = getattr(obj, 'add_help', True)
- for cmd in commands:
- func = getattr(obj, cmd[prefixlen:]) # strip the prefix
- self.subparsers.add_parser(
- cmd, add_help=add_help, help=func.__doc__, **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
- self.argspec = getargspec(obj)
- _parser_registry[obj] = self
-
- def populate_from(self, func):
- """
- Extract the arguments from the attributes of the passed function
- and return a populated ArgumentParser instance.
- """
- self._set_func_argspec(func)
- f = self.argspec
- defaults = f.defaults or ()
- n_args = len(f.args)
- n_defaults = len(defaults)
- alldefaults = (NONE,) * (n_args - n_defaults) + defaults
- prefix = self.prefix = getattr(func, 'prefix_chars', '-')[0]
- for name, default in zip(f.args, alldefaults):
- ann = f.annotations.get(name, ())
- a = Annotation.from_(ann)
- metavar = a.metavar
- if default is NONE:
- dflt = None
- else:
- dflt = default
- if a.help is None:
- a.help = '[%s]' % dflt
- if a.kind in ('option', 'flag'):
- if a.abbrev:
- shortlong = (prefix + a.abbrev, prefix*2 + name)
- else:
- shortlong = (prefix + name,)
- elif default is NONE: # required argument
- self.add_argument(name, help=a.help, type=a.type,
- choices=a.choices, metavar=metavar)
- else: # default argument
- self.add_argument(
- name, nargs='?', help=a.help, default=dflt,
- type=a.type, choices=a.choices, metavar=metavar)
- if a.kind == 'option':
- if default is not NONE:
- metavar = metavar or str(default)
- self.add_argument(
- help=a.help, default=dflt, type=a.type,
- 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') %
- (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)
- 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)
-
- def missing(self, name):
- miss = getattr(self.obj, '__missing__', lambda name:
- self.error('No command %r' % name))
- return miss(name)
-
- 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:], eager=True):
- """
- If obj is a function or a bound method, parse the given arglist
- 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.
- """
- 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
deleted file mode 100644
index 35106ae..0000000
--- a/plac/plac_ext.py
+++ /dev/null
@@ -1,1063 +0,0 @@
-# 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, multiprocessing, signal, threading
-import plac_core
-
-############################# generic utils ################################
-
-@contextmanager
-def stdout(fileobj):
- "usage: with stdout(file('out.txt', 'a')): do_something()"
- 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))
- sys.stdout.flush()
-
-def gen_val(value):
- "Return a generator object with a single element"
- yield value
-
-def gen_exc(etype, exc, tb):
- "Return a generator object raising an exception"
- raise etype, exc, tb
- yield
-
-def less(text):
- "Send a text to less via a pipe"
- # -c clear the screen before starting less
- po = subprocess.Popen(['less', '-c'], stdin=subprocess.PIPE)
- try:
- po.stdin.write(text)
- except IOError:
- pass
- po.stdin.close()
- po.wait()
-
-use_less = (sys.platform != 'win32') # unices
-
-class TerminatedProcess(Exception):
- pass
-
-def terminatedProcess(signum, frame):
- raise TerminatedProcess
-
-########################### readline support #############################
-
-def read_line(stdin, prompt=''):
- "Read a line from stdin, using readline when possible"
- if isinstance(stdin, ReadlineInput):
- return stdin.readline(prompt)
- else:
- write(prompt)
- return stdin.readline()
-
-def read_long_line(stdin, terminator):
- """
- Read multiple lines from stdin until the terminator character is found, then
- yield a single space-separated long line.
- """
- while True:
- lines = []
- while True:
- line = stdin.readline() # ends with \n
- if not line: # EOF
- return
- line = line.strip()
- if not line:
- continue
- elif line[-1] == terminator:
- lines.append(line[:-1])
- break
- else:
- lines.append(line)
- yield ' '.join(lines)
-
-class ReadlineInput(object):
- """
- An iterable with a .readline method reading from stdin.
- """
- def __init__(self, completions, case_sensitive=True, histfile=None):
- self.completions = completions
- self.case_sensitive = case_sensitive
- self.histfile = histfile
- if not case_sensitive:
- self.completions = map(str.upper, completions)
- import readline
- self.rl = readline
- readline.parse_and_bind("tab: complete")
- readline.set_completer(self.complete)
-
- def __enter__(self):
- self.old_completer = self.rl.get_completer()
- try:
- if self.histfile:
- self.rl.read_history_file(self.histfile)
- except IOError: # the first time
- pass
- return self
-
- def __exit__(self, etype, exc, tb):
- self.rl.set_completer(self.old_completer)
- if self.histfile:
- self.rl.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, prompt=''):
- try:
- return raw_input(prompt) + '\n'
- except EOFError:
- return ''
-
- def __iter__(self):
- return iter(self.readline, '')
-
-################### help functionality in plac interpreters ###################
-
-class HelpSummary(object):
- "Build the help summary consistently with the cmd module"
-
- @classmethod
- def add(cls, obj, specialcommands):
- p = plac_core.parser_from(obj)
- 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)
- p.helpsummary = unicode(c.stdout)
-
- def __init__(self):
- self._ls = []
-
- def write(self, s):
- self._ls.append(s)
-
- def __str__(self):
- return ''.join(self._ls)
-
-def format_help(self):
- "Attached to plac_core.ArgumentParser for plac interpreters"
- try:
- return self.helpsummary
- except AttributeError:
- return super(plac_core.ArgumentParser, self).format_help()
-plac_core.ArgumentParser.format_help = format_help
-
-def default_help(obj, cmd=None):
- "An utility for implementing the help functionality in plac interpreters"
- parser = plac_core.parser_from(obj)
- if cmd is None:
- yield parser.format_help()
- subp = parser.subparsers._name_parser_map.get(cmd)
- if subp is None:
- yield _('Unknown command %s' % cmd)
- else:
- yield subp.format_help()
-
-########################### import management ################################
-
-try:
- PLACDIRS = os.environ.get('PLACPATH', '.').split(':')
-except:
- raise ValueError(_('Ill-formed PLACPATH: got %PLACPATHs') % os.environ)
-
-def partial_call(factory, arglist):
- "Call a container factory with the arglist and return a plac object"
- a = plac_core.parser_from(factory).argspec
- if a.defaults or a.varargs or a.varkw:
- raise TypeError('Interpreter.call must be invoked on '
- 'factories with required arguments only')
- required_args = ', '.join(a.args)
- if required_args:
- required_args += ',' # trailing comma
- code = '''def makeobj(interact, %s *args):
- obj = factory(%s)
- obj._interact_ = interact
- obj._args_ = args
- return obj\n'''% (required_args, required_args)
- dic = dict(factory=factory)
- exec code in dic
- makeobj = dic['makeobj']
- makeobj.add_help = False
- if inspect.isclass(factory):
- makeobj.__annotations__ = getattr(
- factory.__init__, '__annotations__', {})
- else:
- makeobj.__annotations__ = getattr(
- factory, '__annotations__', {})
- makeobj.__annotations__['interact'] = (
- 'start interactive interpreter', 'flag', 'i')
- return plac_core.call(makeobj, arglist)
-
-def import_main(path, *args, **pconf):
- """
- An utility to import the main function of a plac tool. It also
- works with command container factories.
- """
- if ':' in path: # importing a factory
- path, factory_name = path.split(':')
- else: # importing the main function
- factory_name = None
- if not os.path.isabs(path): # relative path, look at PLACDIRS
- for placdir in PLACDIRS:
- fullpath = os.path.join(placdir, path)
- if os.path.exists(fullpath):
- break
- else: # no break
- raise ImportError(_('Cannot find %s' % path))
- else:
- fullpath = path
- name, ext = os.path.splitext(os.path.basename(fullpath))
- module = imp.load_module(name, open(fullpath), fullpath, (ext, 'U', 1))
- if factory_name:
- tool = partial_call(getattr(module, factory_name), args)
- else:
- tool = module.main
- # set the parser configuration
- plac_core.parser_from(tool, **pconf)
- return tool
-
-############################## Task classes ##############################
-
-# base class not instantiated directly
-class BaseTask(object):
- """
- A task is a wrapper over a generator object with signature
- Task(no, arglist, genobj), attributes
- .no
- .arglist
- .outlist
- .str
- .etype
- .exc
- .tb
- .status
- and methods .run and .kill.
- """
- 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.status = 'SUBMITTED'
- self.outlist = []
-
- def notify(self, msg):
- "Notifies the underlying monitor. To be implemented"
-
- def _wrap(self, genobj, stringify_tb=False):
- """
- 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 self.status == 'TOBEKILLED': # exit from the loop
- raise GeneratorExit
- if value is not None: # add output
- self.outlist.append(value)
- self.notify(unicode(value))
- yield
- except (GeneratorExit, TerminatedProcess, KeyboardInterrupt):
- # soft termination
- self.status = 'KILLED'
- except: # unexpected exception
- self.etype, self.exc, tb = sys.exc_info()
- self.tb = ''.join(traceback.format_tb(tb)) if stringify_tb else tb
- self.status = 'ABORTED'
- else: # regular exit
- self.status = 'FINISHED'
- try:
- self.str = '\n'.join(map(unicode, self.outlist))
- except IndexError:
- self.str = 'no result'
-
- def run(self):
- "Run the inner generator"
- for none in self._genobj:
- pass
-
- def kill(self):
- "Set a TOBEKILLED status"
- self.status = 'TOBEKILLED'
-
- def wait(self):
- "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):
- return self.tb
- else:
- return ''.join(traceback.format_tb(self.tb))
-
- @property
- def result(self):
- self.wait()
- if self.exc:
- raise self.etype, self.exc, self.tb or None
- if not self.outlist:
- return None
- return self.outlist[-1]
-
- def __repr__(self):
- "String representation containing class name, number, arglist, status"
- return '<%s %d [%s] %s>' % (
- self.__class__.__name__, self.no,
- ' '.join(self.arglist), self.status)
-
-nulltask = BaseTask(0, [], ('skip' for dummy in (1,)))
-
-########################## synchronous tasks ###############################
-
-class SynTask(BaseTask):
- """
- Synchronous task running in the interpreter loop and displaying its
- output as soon as available.
- """
- 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 '\n'.join(map(unicode, self.outlist))
-
-class ThreadedTask(BaseTask):
- """
- A task running in a separated thread.
- """
- 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, on_error):
- "Return a property to be attached to an MPTask"
- def get(self):
- try:
- return getattr(self.ns, name)
- except: # the process was killed or died hard
- return on_error
- def set(self, value):
- try:
- setattr(self.ns, name, value)
- except: # the process was killed or died hard
- pass
- 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.
- """
- str = sharedattr('str', '')
- etype = sharedattr('etype', None)
- exc = sharedattr('exc', None)
- tb = sharedattr('tb', None)
- status = sharedattr('status', 'ABORTED')
-
- @property
- def outlist(self):
- try:
- return self._outlist
- except: # the process died hard
- return []
-
- def notify(self, msg):
- self.man.send('notify_listener %d %r' % (self.no, msg))
-
- def __init__(self, no, arglist, genobj, manager):
- """
- The monitor has a .send method and a .man multiprocessing.Manager
- """
- self.no = no
- self.arglist = arglist
- self._genobj = self._wrap(genobj, stringify_tb=True)
- self.man = manager
- self._outlist = manager.mp.list()
- self.ns = manager.mp.Namespace()
- self.status = 'SUBMITTED'
- self.etype, self.exc, self.tb = None, None, None
- self.str = repr(self)
- self.proc = multiprocessing.Process(target=super(MPTask, self).run)
-
- def run(self):
- "Run the task into an external process"
- self.proc.start()
-
- def wait(self):
- "Block until the external process ends or is killed"
- 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 TaskManager(object):
- """
- Store the given commands into a task registry. Provides methods to
- manage the submitted tasks.
- """
- cmdprefix = '.'
- specialcommands = set(['.last_tb'])
-
- def __init__(self, obj):
- self.obj = obj
- self.registry = {} # {taskno : task}
- if obj.mpcommands or obj.thcommands:
- self.specialcommands.update(['.kill', '.list', '.output'])
- self.parser = plac_core.parser_from(obj)
- HelpSummary.add(obj, self.specialcommands)
- self.man = Manager() if obj.mpcommands else None
- signal.signal(signal.SIGTERM, terminatedProcess)
-
- def close(self):
- "Kill all the running tasks"
- for task in self.registry.itervalues():
- try:
- if task.status == 'RUNNING':
- task.kill()
- task.wait()
- except: # task killed, nothing to wait
- pass
- if self.man:
- self.man.stop()
-
- 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]
- else:
- tasks = [t for t in self.registry.itervalues()]
- tasks.sort(key=attrgetter('no'))
- if len(tasks) >= abs(taskno):
- return tasks[taskno]
-
- ########################### special commands #########################
-
- @plac_core.annotations(
- taskno=('task to kill', 'positional', None, int))
- 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')
- if task is None:
- yield 'Nothing to kill'
- return
- elif not taskno in self.registry:
- yield 'Unknown task %d' % taskno
- return
- else:
- task = self.registry[taskno]
- if task.status in ('ABORTED', 'KILLED', 'FINISHED'):
- yield 'Already finished %s' % task
- return
- task.kill()
- yield task
-
- @plac_core.annotations(
- 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:
- yield task
-
- @plac_core.annotations(
- taskno=('task number', 'positional', None, int))
- def output(self, taskno=-1, fname=None):
- 'show the output of a given task (and optionally save it to a file)'
- if taskno < 0:
- task = self._get_latest(taskno)
- if task is None:
- yield 'Nothing to show'
- return
- elif taskno not in self.registry:
- yield 'Unknown task %d' % taskno
- return
- else:
- task = self.registry[taskno]
- outstr = '\n'.join(map(unicode, task.outlist))
- if fname:
- open(fname, 'w').write(outstr)
- yield 'saved output of %d into %s' % (taskno, fname); return
- yield task
- if len(task.outlist) > 20 and use_less:
- less(outstr)
- else:
- yield outstr
-
- @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'
-
-########################### SyncProcess ##############################
-
-class Process(subprocess.Popen):
- "Start the interpreter specified by the params in a subprocess"
-
- def __init__(self, params):
- signal.signal(signal.SIGPIPE, signal.SIG_DFL)
- # to avoid broken pipe messages
- code = '''import plac, sys
-sys.argv[0] = '<%s>'
-plac.Interpreter(plac.import_main(*%s)).interact(prompt='i>\\n')
-''' % (params[0], params)
- subprocess.Popen.__init__(
- self, [sys.executable, '-u', '-c', code],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- self.man = multiprocessing.Manager()
-
- def close(self):
- "Close stdin and stdout"
- self.stdin.close()
- self.stdout.close()
- self.man.shutdown()
-
- def recv(self): # char-by-char cannot work
- "Return the output of the subprocess, line-by-line until the prompt"
- lines = []
- while True:
- lines.append(self.stdout.readline())
- if lines[-1] == 'i>\n':
- out = ''.join(lines)
- return out[:-1] + ' ' # remove last newline
-
- def send(self, line):
- """Send a line (adding a newline) to the underlying subprocess
- and wait for the answer"""
- self.stdin.write(line + os.linesep)
- return self.recv()
-
-class Monitor(object):
- """
- Base monitor class with methods add_listener/del_listener/notify_listener
- and start/stop/schedule/slave.
- """
- commands = 'add_listener', 'del_listener', 'notify_listener'
- def __init__(self, name):
- self.name = name
- def add_listener(self, taskno):
- pass
- def del_listener(self, taskno):
- pass
- def notify_listener(self, taskno, msg):
- pass
- def start(self):
- pass
- def stop(self):
- pass
- def schedule(self, seconds, display, arg):
- pass
-
-import Queue
-
-class SlaveProcess(object):
- """
- Spawn a slave process reading from an input queue and displaying
- on a monitor object. Methods are start/send/stop.
- """
- def __init__(self, mon):
- self.mon= mon
- self.queue = multiprocessing.Queue()
- self.proc = multiprocessing.Process(None, self._run)
-
- def start(self):
- self.proc.start()
-
- def send(self, line):
- self.queue.put(line)
-
- def stop(self):
- self.queue.close()
- self.proc.terminate()
-
- def _sendline(self, i):
- "Send a line to the underlying monitor"
- try:
- line = self.queue.get_nowait()
- except Queue.Empty:
- pass
- else:
- i.send(line)
- self.mon.schedule(.1, self._sendline, i)
-
- def _run(self):
- with Interpreter(self.mon) as i:
- # .schedule() must be invoked inside the with block
- self.mon.schedule(.1, self._sendline, i)
- self.mon.run()
-
-class StartStopObject(object):
- started = False
- def start(self): pass
- def stop(self): pass
-
-class Manager(StartStopObject):
- """
- The plac Manager contains a multiprocessing.Manager and a set
- of slave monitor processes to which we can send commands. There
- is a manager for each interpreter with mpcommands.
- """
- def add(self, monitor):
- 'Add or replace a monitor in the registry'
- slave = SlaveProcess(monitor)
- name = slave.name = monitor.name
- self.registry[name] = slave
-
- def delete(self, name):
- 'Remove a named monitor from the registry'
- del self.registry[name]
-
- def __init__(self):
- self.registry = {}
- self.started = False
- self.mp = None
-
- # can be called more than once
- def start(self):
- if self.mp is None:
- self.mp = multiprocessing.Manager()
- for slave in self.registry.itervalues():
- slave.start()
- self.started = True
-
- def stop(self):
- for slave in self.registry.itervalues():
- slave.stop()
- if self.mp:
- self.mp.shutdown()
- self.mp = None
- self.started = False
-
- def send(self, line):
- for slave in self.registry.itervalues():
- slave.send(line)
-
-########################## plac server ##############################
-
-import asyncore, asynchat, socket
-
-class _AsynHandler(asynchat.async_chat):
- "asynchat handler starting a new interpreter loop for each connection"
-
- terminator = '\r\n' # the standard one for telnet
- prompt = 'i> '
-
- def __init__(self, socket, interpreter):
- asynchat.async_chat.__init__(self, socket)
- self.set_terminator(self.terminator)
- self.i = interpreter
- self.i.__enter__()
- self.data = []
- self.write(self.prompt)
-
- def write(self, data, *args):
- "Push a string back to the client"
- if args:
- data %= args
- if data.endswith('\n') and not data.endswith(self.terminator):
- data = data[:-1] + self.terminator # fix newlines
- self.push(data)
-
- def collect_incoming_data(self, data):
- "Collect one character at the time"
- self.data.append(data)
-
- def found_terminator(self):
- "Put in the queue the line received from the client"
- line = ''.join(self.data)
- self.log('Received line %r from %s' % (line, self.addr))
- if line == 'EOF':
- self.i.__exit__(None, None, None)
- self.handle_close()
- else:
- task = self.i.submit(line)
- task.run() # synchronous or not
- if task.etype: # manage exception
- error = '%s: %s\nReceived: %s' % (
- task.etype.__name__, task.exc, ' '.join(task.arglist))
- self.log_info(task.traceback + error) # on the server
- self.write(error + self.terminator) # back to the client
- else: # no exception
- self.write(task.str + self.terminator)
- self.data = []
- self.write(self.prompt)
-
-class _AsynServer(asyncore.dispatcher):
- "asyncore-based server spawning AsynHandlers"
-
- def __init__(self, interpreter, newhandler, port, listen=5):
- self.interpreter = interpreter
- self.newhandler = newhandler
- self.port = port
- asyncore.dispatcher.__init__(self)
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- self.bind(('', port))
- self.listen(listen)
-
- def handle_accept(self):
- clientsock, clientaddr = self.accept()
- self.log('Connected from %s' % str(clientaddr))
- i = self.interpreter.__class__(self.interpreter.obj) # new interpreter
- self.newhandler(clientsock, i) # spawn a new handler
-
-########################### the Interpreter #############################
-
-class Interpreter(object):
- """
- A context manager with a .send method and a few utility methods:
- execute, test and doctest.
- """
- def __init__(self, obj, commentchar='#', split=shlex.split):
- self.obj = obj
- try:
- self.name = obj.__module__
- except AttributeError:
- self.name = 'plac'
- self.commentchar = commentchar
- self.split = split
- self._set_commands(obj)
- self.tm = TaskManager(obj)
- self.man = self.tm.man
- self.parser = plac_core.parser_from(obj, prog='')
- 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):
- "Start the inner interpreter loop"
- self._interpreter = self._make_interpreter()
- self._interpreter.send(None)
- return self
-
- def __exit__(self, exctype, exc, tb):
- "Close the inner interpreter and the task manager"
- self.close(exctype, exc, tb)
-
- def submit(self, line):
- "Send a line to the underlying interpreter and return a task object"
- if self._interpreter is None:
- raise RuntimeError(_('%r not initialized: probably you forgot to '
- 'use the with statement') % self)
- if isinstance(line, basestring):
- arglist = self.split(line, self.commentchar)
- else: # expects a list of strings
- arglist = line
- if not arglist:
- return nulltask
- m = self.tm.man # manager
- if m and not m.started:
- m.start()
- task = self._interpreter.send(arglist) # nonblocking
- if not plac_core._match_cmd(arglist[0], self.tm.specialcommands):
- self.tm.registry[task.no] = task
- if m:
- m.send('add_listener %d' % task.no)
- return task
-
- def send(self, line):
- "Send a line to the underlying interpreter and return the finished task"
- task = self.submit(line)
- BaseTask.run(task) # blocking
- return task
-
- def tasks(self):
- "The full lists of the submitted tasks"
- return self.tm.registry.values()
-
- def close(self, exctype=None, exc=None, tb=None):
- "Can be called to close the interpreter prematurely"
- self.tm.close()
- if exctype is not None:
- self._interpreter.throw(exctype, exc, tb)
- else:
- self._interpreter.close()
-
- def _make_interpreter(self):
- "The interpreter main loop, from lists of arguments to task objects"
- enter = getattr(self.obj, '__enter__', lambda : None)
- exit = getattr(self.obj, '__exit__', lambda et, ex, tb: None)
- enter()
- task = None
- try:
- for no in itertools.count(1):
- arglist = yield task
- try:
- cmd, result = self.parser.consume(arglist)
- except SystemExit: # for invalid commands
- task = SynTask(no, arglist, iter([]))
- continue
- except: # anything else
- task = SynTask(no, arglist, gen_exc(*sys.exc_info()))
- continue
- 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, self.tm.man)
- elif cmd in self.obj.thcommands:
- task = ThreadedTask(no, arglist, result)
- else: # blocking task
- task = SynTask(no, arglist, result)
- except GeneratorExit: # regular exit
- exit(None, None, None)
- except: # exceptional exit
- exit(*sys.exc_info())
- raise
-
- def check(self, given_input, expected_output):
- "Make sure you get the expected_output from the given_input"
- 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)
-
- def _parse_doctest(self, lineiter):
- "Returns the lines of input, the lines of output, and the line number"
- lines = [line.strip() for line in lineiter]
- inputs = []
- positions = []
- for i, line in enumerate(lines):
- if line.startswith('i> '):
- inputs.append(line[3:])
- positions.append(i)
- positions.append(len(lines) + 1) # last position
- outputs = []
- for i, start in enumerate(positions[:-1]):
- end = positions[i + 1]
- outputs.append('\n'.join(lines[start+1:end]))
- return zip(inputs, outputs, positions)
-
- def doctest(self, lineiter, verbose=False):
- """
- Parse a text containing doctests in a context and tests of all them.
- Raise an error even if a single doctest if broken. Use this for
- sequential tests which are logically grouped.
- """
- with self:
- for input, output, no in self._parse_doctest(lineiter):
- if verbose:
- 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)
- if task.exc:
- 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:
- write('i> ' + line)
- task = self.send(line) # finished task
- if task.etype: # there was an error
- raise task.etype, task.exc, task.tb
- write('%s\n' % task.str)
-
- def multiline(self, stdin=sys.stdin, terminator=';', verbose=False):
- "The multiline mode is especially suited for usage with emacs"
- with self:
- for line in read_long_line(stdin, terminator):
- task = self.submit(line)
- task.run()
- write('%s\n' % task.str)
- if verbose and task.traceback:
- write(task.traceback)
-
- def interact(self, stdin=sys.stdin, prompt='i> ', verbose=False):
- "Starts an interactive command loop reading commands from the consolle"
- try:
- import readline
- readline_present = True
- except ImportError:
- readline_present = False
- if stdin is sys.stdin and readline_present: # use readline
- histfile = os.path.expanduser('~/.%s.history' % self.name)
- completions = list(self.commands) + ['help']
- self.stdin = ReadlineInput(completions, histfile=histfile)
- else:
- self.stdin = stdin
- self.prompt = prompt
- self.verbose = verbose
- intro = self.obj.__doc__ or ''
- write(intro + '\n')
- with self:
- if self.stdin is sys.stdin: # do not close stdin automatically
- self._manage_input()
- else:
- with self.stdin: # close stdin automatically
- self._manage_input()
-
- def _manage_input(self):
- "Convert input lines into task which are then executed"
- for line in iter(lambda : read_line(self.stdin, self.prompt), ''):
- line = line.strip()
- if not line:
- continue
- task = self.submit(line)
- task.run() # synchronous or not
- write(str(task) + '\n')
- if self.verbose and task.etype:
- write(task.traceback)
-
- def start_server(self, port=2199, **kw):
- """Starts an asyncore server reading commands for clients and opening
- a new interpreter for each connection."""
- _AsynServer(self, _AsynHandler, port) # register the server
- try:
- asyncore.loop(**kw)
- except (KeyboardInterrupt, TerminatedProcess):
- pass
- finally:
- asyncore.close_all()
-
- def add_monitor(self, mon):
- self.man.add(mon)
-
- def del_monitor(self, name):
- self.man.delete(name)
-
- @classmethod
- def call(cls, factory, arglist=sys.argv[1:],
- commentchar='#', split=shlex.split,
- stdin=sys.stdin, prompt='i> ', verbose=False):
- """
- Call a container factory with the arglist and instantiate an
- interpreter object. If there are remaining arguments, send them to the
- interpreter, else start an interactive session.
- """
- obj = partial_call(factory, arglist)
- if not hasattr(obj, 'help'):
- # help is recognized as an alias for --help
- aliases = dict(help='--help')
- plac_core.parser_from(obj).alias = lambda a: aliases.get(a, a)
- i = cls(obj, commentchar, split)
- if i.obj._args_:
- with i:
- task = i.send(i.obj._args_) # synchronous
- if task.exc:
- raise task.etype, task.exc, task.tb
- out = str(task)
- if out:
- print(out)
- elif i.obj._interact_:
- i.interact(stdin, prompt, verbose)
- else:
- i.parser.print_usage()
-
-#################################### runp #####################################
-
-class _TaskLauncher(object):
- "Helper for runp"
-
- def __init__(self, genseq, mode):
- if mode == 'p':
- self.mpcommands = ['rungen']
- else:
- self.thcommands = ['rungen']
- self.genlist = list(genseq)
-
- def rungen(self, i):
- for out in self.genlist[int(i) - 1]:
- yield out
-
-def runp(genseq, mode='p', monitors=(), start=True):
- """Run a sequence of generators in parallel. Mode can be 'p' (use processes)
- or 't' (use threads). Return a list of running task objects. If start is
- False, the tasks are only submitted and not automatically started.
- """
- assert mode in 'pt', mode
- launcher = _TaskLauncher(genseq, mode)
- inter = Interpreter(launcher).__enter__()
- for mon in monitors: # must be added before submit
- inter.add_monitor(mon)
- for i in range(len(launcher.genlist)):
- inter.submit('rungen %d' % (i + 1))
- if start:
- for task in inter.tasks():
- task.run()
- return inter.tasks()
diff --git a/plac/plac_runner.py b/plac/plac_runner.py
deleted file mode 100644
index 2fdfc6d..0000000
--- a/plac/plac_runner.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#!python
-from __future__ import with_statement
-import os, sys, shlex
-import plac
-
-def run(fnames, cmd, verbose):
- "Run batch scripts and tests"
- for fname in fnames:
- with open(fname) as f:
- lines = list(f)
- if not lines[0].startswith('#!'):
- sys.exit('Missing or incorrect shebang line!')
- firstline = lines[0][2:] # strip the shebang
- init_args = shlex.split(firstline)
- tool = plac.import_main(*init_args)
- command = getattr(plac.Interpreter(tool), cmd) # doctest or execute
- if verbose:
- sys.stdout.write('Running %s with %s' % (fname, firstline))
- command(lines[1:], verbose=verbose)
-
-@plac.annotations(
- verbose=('verbose mode', 'flag', 'v'),
- interactive=('run plac tool in interactive mode', 'flag', 'i'),
- multiline=('run plac tool in multiline mode', 'flag', 'm'),
- serve=('run plac server', 'option', 's', int),
- batch=('run plac batch files', 'flag', 'b'),
- test=('run plac test files', 'flag', 't'),
- fname='script to run (.py or .plac or .placet)',
- extra='additional arguments',
- )
-def main(verbose, interactive, multiline, serve, batch, test, fname=None,
- *extra):
- "Runner for plac tools, plac batch files and plac tests"
- baseparser = plac.parser_from(main)
- if fname is None:
- baseparser.print_help()
- elif sys.argv[1] == fname: # script mode
- plactool = plac.import_main(
- fname, prog=os.path.basename(sys.argv[0]) + ' ' + fname)
- out = plac.call(plactool, sys.argv[2:], eager=False)
- if plac.iterable(out):
- for output in out:
- print(output)
- else:
- print(out)
- elif interactive or multiline or serve:
- plactool = plac.import_main(fname, *extra, **{'prog': ''})
- i = plac.Interpreter(plactool)
- if interactive:
- i.interact(verbose=verbose)
- elif multiline:
- i.multiline(verbose=verbose)
- elif serve:
- i.start_server(serve)
- elif batch:
- run((fname,) + extra, 'execute', verbose)
- elif test:
- run((fname,) + extra, 'doctest', verbose)
- print('run %s plac test(s)' % (len(extra) + 1))
- else:
- baseparser.print_usage()
-main.add_help = False
-
-if __name__ == '__main__':
- plac.call(main)
diff --git a/plac/setup.py b/plac/setup.py
deleted file mode 100644
index 006786a..0000000
--- a/plac/setup.py
+++ /dev/null
@@ -1,50 +0,0 @@
-try:
- from setuptools import setup
-except ImportError:
- from distutils.core import setup
-import os.path
-
-def require(*modules):
- """Check if the given modules are already available; if not add them to
- the dependency list."""
- deplist = []
- for module in modules:
- try:
- __import__(module)
- except ImportError:
- deplist.append(module)
- return deplist
-
-def getversion(fname):
- "Get the __version__ without importing plac"
- for line in open(fname):
- if line.startswith('__version__'):
- return eval(line[13:])
-
-if __name__ == '__main__':
- setup(name='plac',
- version=getversion(
- os.path.join(os.path.dirname(__file__), 'plac.py')),
- description=('The smartest command line arguments parser '
- 'in the world'),
- long_description=open('README.txt').read(),
- author='Michele Simionato',
- author_email='michele.simionato@gmail.com',
- url='http://pypi.python.org/pypi/plac',
- license="BSD License",
- py_modules = ['plac_core', 'plac_ext', 'plac_tk', 'plac'],
- scripts = ['plac_runner.py'],
- install_requires=require('argparse', 'multiprocessing'),
- use_2to3=True,
- keywords="command line arguments parser",
- platforms=["All"],
- classifiers=['Development Status :: 3 - Alpha',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Utilities'],
- zip_safe=False)