diff options
author | Michele Simionato <michele.simionato@gmail.com> | 2010-06-01 14:54:29 +0200 |
---|---|---|
committer | Michele Simionato <michele.simionato@gmail.com> | 2010-06-01 14:54:29 +0200 |
commit | 8fc7947431c6e8350196dce2340cda5a544a3437 (patch) | |
tree | bbe9c5bf327d2271575d37417d0a30b85160f957 | |
parent | 7ee9559aa9313824ea327111b7287b716172c286 (diff) | |
download | micheles-8fc7947431c6e8350196dce2340cda5a544a3437.tar.gz |
Some cleanup and for the first time I try to use the Mercurial pre-commit hook
-rw-r--r-- | plac/CHANGES.txt | 4 | ||||
-rw-r--r-- | plac/Makefile | 4 | ||||
-rw-r--r-- | plac/doc/annotations.py | 1 | ||||
-rw-r--r-- | plac/doc/article.html | 410 | ||||
-rw-r--r-- | plac/doc/dbcli.py | 3 | ||||
-rw-r--r-- | plac/doc/example1.py | 1 | ||||
-rw-r--r-- | plac/doc/example10.py | 1 | ||||
-rw-r--r-- | plac/doc/example11.py | 1 | ||||
-rw-r--r-- | plac/doc/example2.py | 1 | ||||
-rw-r--r-- | plac/doc/example3.py | 1 | ||||
-rw-r--r-- | plac/doc/example4.py | 1 | ||||
-rw-r--r-- | plac/doc/example4_.py | 1 | ||||
-rw-r--r-- | plac/doc/example5.py | 1 | ||||
-rw-r--r-- | plac/doc/example6.py | 1 | ||||
-rw-r--r-- | plac/doc/example7.py | 2 | ||||
-rw-r--r-- | plac/doc/example8.py | 1 | ||||
-rw-r--r-- | plac/doc/example8_.py | 3 | ||||
-rw-r--r-- | plac/doc/example9.py | 2 | ||||
-rw-r--r-- | plac/doc/plac.html | 1036 | ||||
-rw-r--r-- | plac/doc/plac.pdf (renamed from plac/doc/article.pdf) | 1707 | ||||
-rw-r--r-- | plac/doc/plac.txt (renamed from plac/doc/article.txt) | 88 | ||||
-rw-r--r-- | plac/plac.py | 5 |
22 files changed, 1991 insertions, 1284 deletions
diff --git a/plac/CHANGES.txt b/plac/CHANGES.txt new file mode 100644 index 0000000..38a86f1 --- /dev/null +++ b/plac/CHANGES.txt @@ -0,0 +1,4 @@ +HISTORY +---------- + +0.3.0 Initial version. (2010-06-02) diff --git a/plac/Makefile b/plac/Makefile new file mode 100644 index 0000000..5c93cf0 --- /dev/null +++ b/plac/Makefile @@ -0,0 +1,4 @@ +doc/plac.pdf: doc/plac.txt + cd doc; rst2pdf plac.txt; rst2html plac.txt plac.html +upload: + python setup.py register sdist upload diff --git a/plac/doc/annotations.py b/plac/doc/annotations.py index c84cc04..0de2140 100644 --- a/plac/doc/annotations.py +++ b/plac/doc/annotations.py @@ -1,3 +1,4 @@ +# annotations.py class Positional(object): def __init__(self, help='', type=None, choices=None, metavar=None): self.help = help diff --git a/plac/doc/article.html b/plac/doc/article.html deleted file mode 100644 index 59feb07..0000000 --- a/plac/doc/article.html +++ /dev/null @@ -1,410 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" /> -<title></title> -<style type="text/css"> - -.highlight { background: #f8f8f8; } -.highlight .c { color: #408080; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ -.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #808080 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0040D0 } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #008000; font-weight: bold } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #7D9029 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #A0A000 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ - -</style> -</head> -<body> -<div class="document"> - - -<div class="section" id="the-easiest-command-line-arguments-parser-in-the-python-world"> -<h1>The easiest command line arguments parser in the Python world</h1> -<p>Today I want to announce to the general public the birth of my latest -project, which aims to be the easiest command line arguments -parser in the Python world: <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a>.</p> -<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> is designed to be <a class="reference external" href="http://www.welton.it/articles/scalable_systems">downwardly scalable</a>, i.e. to be trivially simple -to use for trivial use cases, while being surprisingly scalable upwards -for less trivial use cases. Still, <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> is not intended to -be an industrial strength command line parsing module. Its -capabilities are limited by design. If you need more power, by all means -use the parsing modules in the standard library. Still, I have been using -Python for 8 years and never once I had to use the full power of the -standard library modules.</p> -<p>Actually I am prettu much convinced that features provided by <tt class="docutils literal">plac</tt> -aee more than enough for 99.9% of the typical use cases of a scripter -working in a Unix-like environment. I am targetting here programmers, -sysadmins, scientists and in general people writing throw-away scripts -for themselves, choosing to use a command line interface because it is -the quick and simple. Such users are not interested in features, -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 current modules in the standard library -forces them to go the hard way. They are designed to implement -power user tools for programmers or system administrators, and -they have a non-trivial learning curve.</p> -<p>This is why, even if there is no want of command line arguments -parsers in Python world, sometime like <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> was still lacking.</p> -<p>The standard library alone contains three different modules for the -parsing of command line options: <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; <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> is just a nice and simple wrapper over -<a class="reference external" href="http://argparse.googlecode.com">argparse</a>, hiding most of the complexity while retaining most of -the power.</p> -</div> -<div class="section" id="the-importance-of-scaling-down"> -<h1>The importance of scaling down</h1> -<p>An ex-coworket of mine, David Welton, once wrote a nice article about -the importance of <a class="reference external" href="http://www.welton.it/articles/scalable_systems">scaling down</a>: most people are concerned with the -possibility of scaling up, but we should also be concerned with the -issue of scaling down: in other worlds, simple things should be kept -simple. To be concrete, let me start with the simplest possible -thing: a script that takes a single argument and does something to it. -It cannot get more trivial than that (discarding the possibility of -a script without command line arguments, where there is nothing to parse), -nevertheless it is a use case <em>extremely important</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:</p> -<blockquote> -<pre class="literal-block"> -def main(dsn): - "Do something with the database" - 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> -</blockquote> -<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (10 lines -counting an empty line) is essentially boilerplate that should not exists. -Actually I think the Python language should recognize the -main function and perform trivial arguments parsing behind the -scenes; unfortunaly this is unlikely to happen. Therefore I have -been writing boilerplate like this for years, and every time -I <em>hate</em> having to check for the <tt class="docutils literal">IndexError</tt> by hand, and I hate having -to write always the same usage message. The purpose of using a -scripting language is convenience and trivial things should be -trivial. Unfortunately the standard library modules do not help -for this use case, which may be trivial, but it is still -incredibly common. 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 10 lines to 7 lines:</p> -<blockquote> -<pre class="literal-block"> -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) - -</pre> -</blockquote> -<p>However saving a three lines does not justify introducing the external -dependency (most people will not switch Python 2.7, currenctly in beta -for many years). -Moreover, 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://www.welton.it/articles/scalable_systems">plac</a> module works pretty well when scaling down, and it is able -to reduce the ten lines of boiler plate to two lines. With the -<a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> module all you need to write is</p> -<blockquote> -<pre class="literal-block"> -def main(dsn): - "Do something with the database" - print(dsn) - -if __name__ == '__main__': - import plac; plac.call(main) - -</pre> -</blockquote> -<p>As an additional bonus she <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> module automatically recognizes the help flag:</p> -<blockquote> -# python example1.py -h</blockquote> -<p>This is only the tip of the iceberg, as you will see.</p> -</div> -<div class="section" id="optional-arguments"> -<h1>Optional arguments</h1> -<p>I have encountered this use case at work hundreds of times:</p> -<blockquote> -<pre class="literal-block"> -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) - -</pre> -</blockquote> -<p>9 lines of boilerplate are removed.</p> -<p>Finally, often you want to pass a variable number of arguments to -your command line script. Here is an example, a script which runs -on the database a series of .sql scripts:</p> -<blockquote> -<pre class="literal-block"> -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__': - if len(sys.argv) < 2: - sys.exit('usage: python %s dsn script.sql ...' % sys.argv[0]) - main(sys.argv[1:]) - -</pre> -</blockquote> -<p>Using <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a>, you can just replace the <tt class="docutils literal">__main__</tt> block with the usual -<tt class="docutils literal">import plac; plac.call(main)</tt>. The importanting is that you get a -much nicer usage message:</p> -<pre class="literal-block"> -usage: example7.py [-h] dsn [scripts [scripts ...]] - -positional arguments: - dsn - scripts - -optional arguments: - -h, --help show this help message and exit -</pre> -</div> -<div class="section" id="options-and-flags"> -<h1>Options and flags</h1> -<p>It is surprising how little 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 certainly have written more than a thousand of them). -Still, this use case is quite common and 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 -an old recipe of mine, 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> or I have just performed the -parsing by hand in the simplest cases.</p> -<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> is inspired to the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, in the sense that -it delivers the programmer from the burden of writing the -parser for the options by hand, 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 is to leverage on the <cite>function annotations</cite> concept, a new -feature of Python 3. An example is worth a thousand words, so here -it is:</p> -<blockquote> -<pre class="literal-block"> -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) - -</pre> -</blockquote> -<p>As you see, the argument <tt class="docutils literal">command</tt> has been annotated with the -tuple <tt class="docutils literal">("SQL query", <span class="pre">'option-c')</span></tt>: the first string is the -help string which will appear in the usage message, whereas the -second string tells <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> that <tt class="docutils literal">command</tt> is an option and that -it can be abbreviated with the letter <tt class="docutils literal">c</tt>. Of course, it also -possible to use the long option format, by prefixing the option -with <tt class="docutils literal"><span class="pre">--command=</span></tt>. The resulting usage message is the following:</p> -<pre class="literal-block"> -$ python3 example8.py -h -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"select * from table" dsn -executing select * from table on dsn - -$ python3 example8.py --command="select * from table" dsn -executing select * from table on dsn -</pre> -<p>Notice that if the option is not passed, the variable <tt class="docutils literal">command</tt> -will get the value <tt class="docutils literal">None</tt>.</p> -<p>Even positional argument can be annotated, but it makes no sense to -specify the "option-c" string, so you can skip it and write:</p> -<pre class="literal-block"> -def main(command: ("SQL query", 'option-c'), dsn: ("Database dsn",)): - ... -</pre> -<p>Alternatively, you can write <tt class="docutils literal">("Database dsn", None)</tt>. In both cases -the usage message now will show a nice help string on the right hand side -of the <tt class="docutils literal">dsn</tt> positional argument. varargs (starred-arguments) can also -be annotated:</p> -<pre class="literal-block"> -def main(dsn: ("Database dsn",), *scripts: ("SQL scripts",)): - ... -</pre> -<p>is a valid signature for <a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a>, which will recognize the help strings -for both <tt class="docutils literal">dsn</tt> and <tt class="docutils literal">scripts</tt>.</p> -<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">plac</a> also recognizes 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:</p> -<pre class="literal-block"> -$ python3 example9.py -v dsn -connecting to dsn -</pre> -<p>For consistency with the way the usage message is printed, I suggest to -follow the FOP convention: in the <tt class="docutils literal">main</tt> function write first -the flag arguments, then the option arguments and finally the positional -arguments. This is a convention and you are not forced to use it, but -it makes sense to put the position arguments at the end, since they -may be default arguments and varargs. In this document I will always use -the FOP convention.</p> -</div> -<div class="section" id="plac-is-also-for-people-not-using-python-3"> -<h1>plac is also for people not using Python 3</h1> -<p>I do not use Python 3. At work we are just starting to think about -migrating to Python 2.6. I think it will take years before we even -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://www.welton.it/articles/scalable_systems">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</p> -<pre class="literal-block"> -def main(dsn: ("Database dsn",), *scripts: ("SQL scripts",)): -</pre> -<p>becomes:</p> -<pre class="literal-block"> -def main(dsn, *scripts): - ... -main.__annotations__ = dict( -dsn= ("Database dsn",), -scripts=("SQL scripts",)) -</pre> -<p>One should be careful to much the keys of the annotations 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"> -@annotations( -dsn= ("Database dsn",), -scripts=("SQL scripts",)) -def main(dsn, *scripts): - ... -</pre> -<p>In the rest of this article I will assume that you are using Python 2.X with -X >= 4 and I will use the <tt class="docutils literal">plac.annotations</tt> decorator.</p> -</div> -<div class="section" id="advanced-usage"> -<h1>Advanced usage</h1> -<p>One of the goals of plac is to have a learning curve of <em>minutes</em>, compared -to the learning curve of <em>hours</em> of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. That does not mean -that I have removed all the advanced 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://www.welton.it/articles/scalable_systems">plac</a>: in particular, the -<tt class="docutils literal">type</tt>, <tt class="docutils literal">choices</tt> and <tt class="docutils literal">metavar</tt> concepts are there. -Here is an example showing all of them:</p> -<blockquote> -<pre class="literal-block"> -import plac - -@plac.annotations( -operator=("The name of an operator", None, str, ['add', 'mul']), -numbers=("A number", None, float, None, "n")) -def main(operator, *numbers): - op = getattr(float, '__%s__' % operator) - result = dict(add=0.0, mul=1.0)[operator] - for n in numbers: - result = op(result, n) - print(result) - -if __name__ == '__main__': - plac.call(main) - -</pre> -</blockquote> -<p>Let me begin by discussing the <tt class="docutils literal">type</tt> feature: given any callable -taking a string in input a returning any Python object, it is possible -to automagically convert the parsed arguments with the callable, simply -by listing it in the annotation</p> -</div> -</div> -</body> -</html> diff --git a/plac/doc/dbcli.py b/plac/doc/dbcli.py index ba6d586..04ea44b 100644 --- a/plac/doc/dbcli.py +++ b/plac/doc/dbcli.py @@ -1,3 +1,4 @@ +# dbcli.py import clap from sqlalchemy.ext.sqlsoup import SqlSoup @@ -19,7 +20,7 @@ def main(db, header, sqlcmd, delimiter="|", *scripts): print(delimiter.join(map(str, row))) for script in scripts: - db.execute(file(script).read()) + db.bind.execute(file(script).read()) if __name__ == '__main__': clap.call(main) diff --git a/plac/doc/example1.py b/plac/doc/example1.py index 59d4ef1..8d5fe7a 100644 --- a/plac/doc/example1.py +++ b/plac/doc/example1.py @@ -1,3 +1,4 @@ +# example1.py def main(dsn): "Do something with the database" print(dsn) diff --git a/plac/doc/example10.py b/plac/doc/example10.py index eb45343..5da3a22 100644 --- a/plac/doc/example10.py +++ b/plac/doc/example10.py @@ -1,3 +1,4 @@ +# example10.py import plac @plac.annotations( diff --git a/plac/doc/example11.py b/plac/doc/example11.py index 0de8395..07fbd40 100644 --- a/plac/doc/example11.py +++ b/plac/doc/example11.py @@ -1,3 +1,4 @@ +# example11.py import plac from annotations import Positional diff --git a/plac/doc/example2.py b/plac/doc/example2.py index 69981ee..74a4a01 100644 --- a/plac/doc/example2.py +++ b/plac/doc/example2.py @@ -1,3 +1,4 @@ +# example2.py def main(dsn): "Do something on the database" print(dsn) diff --git a/plac/doc/example3.py b/plac/doc/example3.py index 1c1a0bd..adfbed6 100644 --- a/plac/doc/example3.py +++ b/plac/doc/example3.py @@ -1,3 +1,4 @@ +# example3.py def main(dsn): "Do something with the database" print(dsn) diff --git a/plac/doc/example4.py b/plac/doc/example4.py index 08a7dd2..2e76b65 100644 --- a/plac/doc/example4.py +++ b/plac/doc/example4.py @@ -1,3 +1,4 @@ +# example4.py from datetime import datetime def main(dsn, table='product', today=datetime.today()): diff --git a/plac/doc/example4_.py b/plac/doc/example4_.py index 2a61d47..c2c92da 100644 --- a/plac/doc/example4_.py +++ b/plac/doc/example4_.py @@ -1,3 +1,4 @@ +# example4_.py from datetime import datetime def main(dsn, table='product', today=datetime.today()): diff --git a/plac/doc/example5.py b/plac/doc/example5.py index d6c8c8a..1da6153 100644 --- a/plac/doc/example5.py +++ b/plac/doc/example5.py @@ -1,3 +1,4 @@ +# example5.py from datetime import datetime def main(dsn, today=datetime.today()): diff --git a/plac/doc/example6.py b/plac/doc/example6.py index 9c7b5fd..a68075f 100644 --- a/plac/doc/example6.py +++ b/plac/doc/example6.py @@ -1,3 +1,4 @@ +# example6.py from datetime import datetime def main(dsn, *scripts): diff --git a/plac/doc/example7.py b/plac/doc/example7.py index c1e6b2a..541c4ac 100644 --- a/plac/doc/example7.py +++ b/plac/doc/example7.py @@ -1,3 +1,5 @@ +# example7.py + from datetime import datetime def main(dsn, *scripts): diff --git a/plac/doc/example8.py b/plac/doc/example8.py index ef5a4e7..1dad399 100644 --- a/plac/doc/example8.py +++ b/plac/doc/example8.py @@ -1,3 +1,4 @@ +# example8.py def main(command: ("SQL query", 'option', 'c'), dsn): if command: print('executing %s on %s' % (command, dsn)) diff --git a/plac/doc/example8_.py b/plac/doc/example8_.py index 1c14748..33ae359 100644 --- a/plac/doc/example8_.py +++ b/plac/doc/example8_.py @@ -1,5 +1,4 @@ -# example8_ - +# example8_.py def main(dsn, command: ("SQL query", 'option', 'c')='select * from table'): print('executing %r on %s' % (command, dsn)) diff --git a/plac/doc/example9.py b/plac/doc/example9.py index 8ab3932..8e39eff 100644 --- a/plac/doc/example9.py +++ b/plac/doc/example9.py @@ -1,3 +1,5 @@ +# example9.py + def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'): if verbose: print('connecting to %s' % dsn) diff --git a/plac/doc/plac.html b/plac/doc/plac.html new file mode 100644 index 0000000..77b998a --- /dev/null +++ b/plac/doc/plac.html @@ -0,0 +1,1036 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" /> +<title>Plac, the Easiest Command Line Arguments Parser in the World</title> +<meta name="author" content="Michele Simionato" /> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 5196 2007-06-03 20:25:28Z wiemann $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, 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.admonition p.admonition-title, 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.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 } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +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 ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +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 } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left { + clear: left } + +img.align-right { + clear: right } + +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.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +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 { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document" id="plac-the-easiest-command-line-arguments-parser-in-the-world"> +<h1 class="title">Plac, the Easiest Command Line Arguments Parser in the World</h1> +<table class="docinfo" frame="void" rules="none"> +<col class="docinfo-name" /> +<col class="docinfo-content" /> +<tbody valign="top"> +<tr><th class="docinfo-name">Author:</th> +<td>Michele Simionato</td></tr> +<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato@gmail.com">michele.simionato@gmail.com</a></td> +</tr> +<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.3+</td> +</tr> +<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/plac">http://pypi.python.org/pypi/plac</a></td> +</tr> +<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal"><span class="pre">easy_install</span> <span class="pre">plac</span></tt></td> +</tr> +<tr class="field"><th class="docinfo-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="#introduction" id="id1">Introduction</a></li> +<li><a class="reference internal" href="#the-importance-of-scaling-down" id="id2">The importance of scaling down</a></li> +<li><a class="reference internal" href="#positional-default-arguments" id="id3">Positional default arguments</a></li> +<li><a class="reference internal" href="#options-and-flags" id="id4">Options and flags</a></li> +<li><a class="reference internal" href="#plac-for-python-2-x-users" id="id5">plac for Python 2.X users</a></li> +<li><a class="reference internal" href="#more-features" id="id6">More features</a></li> +<li><a class="reference internal" href="#a-more-realistic-example" id="id7">A more realistic example</a></li> +<li><a class="reference internal" href="#a-few-notes-on-the-underlying-implementation" id="id8">A few notes on the underlying implementation</a></li> +<li><a class="reference internal" href="#custom-annotation-objects" id="id9">Custom annotation objects</a></li> +<li><a class="reference internal" href="#plac-vs-argparse" id="id10">plac vs argparse</a></li> +<li><a class="reference internal" href="#the-future" id="id11">The future</a></li> +<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id12">Trivia: the story behind the name</a></li> +</ul> +</div> +<div class="section" id="introduction"> +<h1><a class="toc-backref" href="#id1">Introduction</a></h1> +<p>There is no want of command line arguments parsers in the Python +world. The standard library alone contains three different modules for +the parsing of command line options: <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.</p> +<p>Enters <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is designed to be downwardly scalable, i.e. to +be trivially simple to use for trivial use cases, and to have a +next-to-zero learning curve. 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>, hiding most of the complexity while retaining +most of the power. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is surprisingly scalable upwards even for +non-trivial use cases, but it is not intended to be an industrial +strength command line parsing module. Its capabilities are limited by +design. If you need more power, by all means use the parsing modules +in the standard library. Still, I have been using Python for 8 years +and never once I had to use the full power of the standard library +modules.</p> +<p>Actually I am pretty much convinced that features provided by <tt class="docutils literal"><span class="pre">plac</span></tt> +are more than enough for 99.9% of the typical use cases of a scripter. +I am targetting here programmers, sys-admins, scientists and in +general people writing throw-away scripts for themselves, choosing to +use a command line interface because it is the quick and simple. Such +users are not interested in features, 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 current +modules in the standard library forces them to go the hard way. They +are designed to implement power user tools for programmers or system +administrators, and they have a non-trivial learning curve.</p> +</div> +<div class="section" id="the-importance-of-scaling-down"> +<h1><a class="toc-backref" href="#id2">The importance of scaling down</a></h1> +<p>An ex-coworker of mine, David Welton, once wrote a nice article about +the import<ance of <a class="reference external" href="http://www.welton.it/articles/scalable_systems">scaling down</a>: most people are concerned with the +possibility of scaling up, but we should also be concerned with the +issue of scaling down. In other worlds, simple things should be kept +simple, and programs should address the common cases in an easy way, +hopefully without loosing too much power and 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 trivial case, while retaining the +ability to handle complex cases relying on the underlying power of +<a class="reference external" href="http://argparse.googlecode.com">argparse</a>.</p> +<p>To be concrete, let me start with the simplest possible +thing: a script that takes a single argument and does something to it. +It cannot get more trivial than that (discarding the possibility of +a script without command line arguments, where there is nothing to parse), +nevertheless 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> +<blockquote> +<pre class="literal-block"> +# example1.py +def main(dsn): + "Do something with the database" + 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> +</blockquote> +<p>As you see the whole <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__'</span></tt> block (nine lines) is +essentially boilerplate that should not exists. Actually I think the +Python language should recognize the main function and perform trivial +arguments parsing behind the scenes; 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 modules do not help for +this use case, which may be trivial, but it is still incredibly +common. 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> +<blockquote> +<pre class="literal-block"> +# 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) + +</pre> +</blockquote> +<p>However saving three lines does not justify introducing the external +dependency: most people will not switch Python 2.7, which at the time of +this writing is just about to be released, for many years. +Moreover, 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> +<blockquote> +<pre class="literal-block"> +# example3.py +def main(dsn): + "Do something with the database" + print(dsn) + +if __name__ == '__main__': + import plac; plac.call(main) + +</pre> +</blockquote> +<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 +usage: example3.py [-h] dsn + +positional arguments: + dsn + +optional arguments: + -h, --help show this help message and exit +</pre> +<p>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="positional-default-arguments"> +<h1><a class="toc-backref" href="#id3">Positional default arguments</a></h1> +<p>I have encountered this use case at work hundreds of times:</p> +<blockquote> +<pre class="literal-block"> +# 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) + +</pre> +</blockquote> +<p>With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal"><span class="pre">__main__</span></tt> block reduces to the usual two lines:</p> +<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 I have +the usage message for free:</p> +<pre class="literal-block"> +usage: example4_.py [-h] dsn [table] [today] + +positional arguments: + dsn + table + today + +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> 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> +<blockquote> +<pre class="literal-block"> +# example6.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 sys + if len(sys.argv) < 2: + sys.exit('usage: python %s dsn script.sql ...' % sys.argv[0]) + main(sys.argv[1:]) + +</pre> +</blockquote> +<p>Using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, you can just replace the <tt class="docutils literal"><span class="pre">__main__</span></tt> block with the +usual two lines (I have defined an Emacs keybinding for them) +and you get the following usage message:</p> +<pre class="literal-block"> +usage: example7.py [-h] dsn [scripts [scripts ...]] + +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 my intent is clear, +let's the machine take care of the details.</p> +</div> +<div class="section" id="options-and-flags"> +<h1><a class="toc-backref" href="#id4">Options and flags</a></h1> +<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 certainly have written +more than a thousand of them). Still, this use case is quite common +and 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 an +old recipe of mine, 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, instead of manually +building a suitable OptionParser.</p> +<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, in the sense that it +delivers the programmer from the burden of writing the parser, but is +less of a hack: instead of extracting the parser from the docstring of +the module, it extracts it from the signature of the <tt class="docutils literal"><span class="pre">main</span></tt> +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> +<blockquote> +<pre class="literal-block"> +# 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) + +</pre> +</blockquote> +<p>As you see, the argument <tt class="docutils literal"><span class="pre">command</span></tt> has been annotated with the +tuple <tt class="docutils literal"><span class="pre">("SQL</span> <span class="pre">query",</span> <span class="pre">'option',</span> <span class="pre">'c')</span></tt>: the first string is the +help string which will appear in the usage message, whereas the +second and third strings tell <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> that <tt class="docutils literal"><span class="pre">command</span></tt> is an option and that +it can be abbreviated with the letter <tt class="docutils literal"><span class="pre">c</span></tt>. Of course, it also +possible to use the long option format, by prefixing the option +with <tt class="docutils literal"><span class="pre">--command=</span></tt>. The resulting usage message is the following:</p> +<pre class="literal-block"> +$ python3 example8.py -h +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"select * from table" dsn +executing select * from table on dsn + +$ python3 example8.py --command="select * from table" dsn +executing select * from table on dsn +</pre> +<p>Notice that if the option is not passed, the variable <tt class="docutils literal"><span class="pre">command</span></tt> +will get the value <tt class="docutils literal"><span class="pre">None</span></tt>. It is possible to specify a non-trivial +default for an option. Here is an example:</p> +<blockquote> +<pre class="literal-block"> +# example8_.py +def main(dsn, command: ("SQL query", 'option', 'c')='select * from table'): + print('executing %r on %s' % (command, dsn)) + +if __name__ == '__main__': + import clap; clap.call(main) + +</pre> +</blockquote> +<p>Now if you do not pass the <tt class="docutils literal"><span class="pre">command</span> <span class="pre">option</span></tt>, the +default query will be executed:</p> +<pre class="literal-block"> +$ python article/example8_.py dsn +executing 'select * from table' on dsn +</pre> +<p>Positional argument can be annotated too:</p> +<pre class="literal-block"> +def main(command: ("SQL query", 'option', 'c'), + dsn: ("Database dsn", 'positional', None)): + ... +</pre> +<p>Of course explicit is better than implicit, an no special cases are +special enough, but sometimes practicality beats purity, so <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is +smart enough to convert help messages into tuples internally; in other +words, you can just write "Database dsn" instead of <tt class="docutils literal"><span class="pre">("Database</span> <span class="pre">dsn",</span> +<span class="pre">'positional',</span> <span class="pre">None)</span></tt>:</p> +<pre class="literal-block"> +def main(command: ("SQL query", 'option', 'c'), dsn: "Database dsn"): + ... +</pre> +<p>In both cases +the usage message will show a nice help string on the right hand side +of the <tt class="docutils literal"><span class="pre">dsn</span></tt> positional argument. varargs (starred-arguments) can also +be annotated:</p> +<pre class="literal-block"> +def main(dsn: "Database dsn", *scripts: "SQL scripts"): + ... +</pre> +<p>is a valid signature for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, which will recognize the help strings +for both <tt class="docutils literal"><span class="pre">dsn</span></tt> and <tt class="docutils literal"><span class="pre">scripts</span></tt>:</p> +<pre class="literal-block"> +positional arguments: + dsn Database dsn + scripts SQL scripts +</pre> +<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> also recognizes flags, i.e. boolean options which are +<tt class="docutils literal"><span class="pre">True</span></tt> if they are passed to the command line and <tt class="docutils literal"><span class="pre">False</span></tt> +if they are absent. Here is an example:</p> +<pre class="literal-block"> +$ python3 example9.py -v dsn +connecting to dsn +</pre> +<pre class="literal-block"> +$ python3 example9.py -h +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> +<p>Notice that it is an error trying to specify a default for flags: the +default value for a flag is always <tt class="docutils literal"><span class="pre">False</span></tt>. If you feel the need to +implement non-boolean flags, you should use an option with two +choices, as explained in the "more features" section.</p> +<p>For consistency with the way the usage message is printed, I suggest +you to follow the Flag-Option-Required-Default (FORD) convention: in +the <tt class="docutils literal"><span class="pre">main</span></tt> function write first the flag arguments, then the option +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 since it is required by the Python syntax.</p> +</div> +<div class="section" id="plac-for-python-2-x-users"> +<h1><a class="toc-backref" href="#id5">plac for Python 2.X users</a></h1> +<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 even +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</p> +<pre class="literal-block"> +def main(dsn: "Database dsn", *scripts: "SQL scripts"): +</pre> +<p>becomes:</p> +<pre class="literal-block"> +def main(dsn, *scripts): + ... +main.__annotations__ = dict( +dsn="Database dsn", +scripts="SQL scripts") +</pre> +<p>One should be careful to much the keys of the annotations dictionary +with the names of the arguments in the annotated function; for lazy +people with Python 2.4 available the simplest way is to use the +<tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator that performs the check for you.</p> +<pre class="literal-block"> +@annotations( + dsn="Database dsn", + scripts="SQL scripts") +def main(dsn, *scripts): + ... +</pre> +<p>In the rest of this article I will assume that you are using Python 2.X with +<tt class="docutils literal"><span class="pre">X</span> <span class="pre">>=</span> <span class="pre">4</span></tt> and I will use the <tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator. Notice however +that the tests for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> are supposed to run even with Python 2.3.</p> +</div> +<div class="section" id="more-features"> +<h1><a class="toc-backref" href="#id6">More features</a></h1> +<p>One of the goals of plac is to have a learning curve of <em>minutes</em>, compared +to the learning curve of <em>hours</em> of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. That does not mean +that I have removed 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 5-tuple of the form</p> +<blockquote> +<tt class="docutils literal"><span class="pre">(help,</span> <span class="pre">kind,</span> <span class="pre">abbrev,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar)</span></tt></blockquote> +<p>where <tt class="docutils literal"><span class="pre">help</span></tt> is the help message, <tt class="docutils literal"><span class="pre">kind</span></tt> is one of {"flag", +"option ", "positional"}, <tt class="docutils literal"><span class="pre">abbrev</span></tt> is a one-character string, +<tt class="docutils literal"><span class="pre">type</span></tt> is callable taking a string in input, choices is a sequence +of values and <tt class="docutils literal"><span class="pre">metavar</span></tt> is a string.</p> +<p><tt class="docutils literal"><span class="pre">type</span></tt> is used to automagically convert the arguments from string +to any Python type; by default there is no convertion i.e. <tt class="docutils literal"><span class="pre">type=None</span></tt>.</p> +<p><tt class="docutils literal"><span class="pre">choices</span></tt> is used to restrict the number of the valid +options; by default there is no restriction i.e. <tt class="docutils literal"><span class="pre">choices=None</span></tt>.</p> +<p><tt class="docutils literal"><span class="pre">metavar</span></tt> is used to change the argument name in the usage message +(and only there); by default the metavar is equal to the 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 (shamelessly stolen +from the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> documentation):</p> +<blockquote> +<pre class="literal-block"> +# 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" + op = getattr(float, '__%s__' % operator) + result = dict(add=0.0, mul=1.0)[operator] + for n in numbers: + result = op(result, n) + print(result) + +if __name__ == '__main__': + plac.call(main) + +</pre> +</blockquote> +<p>Here is the usage for the script:</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"><span class="pre">main</span></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> +</div> +<div class="section" id="a-more-realistic-example"> +<h1><a class="toc-backref" href="#id7">A more realistic example</a></h1> +<p>Here is a more realistic script using most of the features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to +run SQL queries on a database by relying on <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a>. Notice the usage +of the <tt class="docutils literal"><span class="pre">type</span></tt> feature to automagically convert a SQLAlchemy connection +string into a <a class="reference external" href="http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html">SqlSoup</a> object:</p> +<blockquote> +<pre class="literal-block"> +# dbcli.py +import clap +from sqlalchemy.ext.sqlsoup import SqlSoup + +@clap.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" + print('Working on %s' % db.bind.url) + if sqlcmd: + result = db.bind.execute(sqlcmd) + if header: # print the header + print(delimiter.join(result.keys())) + for row in result: # print the rows + print(delimiter.join(map(str, row))) + + for script in scripts: + db.bind.execute(file(script).read()) + +if __name__ == '__main__': + clap.call(main) + +</pre> +</blockquote> +<p>Here is the usage message:</p> +<pre class="literal-block"> +$ python article/dbcli.py -h +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> +</div> +<div class="section" id="a-few-notes-on-the-underlying-implementation"> +<h1><a class="toc-backref" href="#id8">A few notes on the underlying implementation</a></h1> +<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> relies on a <a class="reference external" href="http://argparse.googlecode.com">argparse</a> for all of the heavy lifting work and it is +possible to leverage on <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features directly or indirectly.</p> +<p>For instance, you can make invisible an argument in the usage message +simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS==`'`</span> <span class="pre">as</span> <span class="pre">help</span> <span class="pre">string</span> <span class="pre">(or</span> +<span class="pre">``argparse.SUPPRESS</span></tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a> +directly.</p> +<p>It is also possible to pass options to the underlying +<tt class="docutils literal"><span class="pre">argparse.ArgumentParser</span></tt> object (currently it accepts the default +arguments <tt class="docutils literal"><span class="pre">description</span></tt>, <tt class="docutils literal"><span class="pre">epilog</span></tt>, <tt class="docutils literal"><span class="pre">prog</span></tt>, <tt class="docutils literal"><span class="pre">usage</span></tt>, +<tt class="docutils literal"><span class="pre">add_help</span></tt>, <tt class="docutils literal"><span class="pre">argument_default</span></tt>, <tt class="docutils literal"><span class="pre">parents</span></tt>, <tt class="docutils literal"><span class="pre">prefix_chars</span></tt>, +<tt class="docutils literal"><span class="pre">fromfile_prefix_chars</span></tt>, <tt class="docutils literal"><span class="pre">conflict_handler</span></tt>, <tt class="docutils literal"><span class="pre">formatter_class</span></tt>). +It is enough to set such attributes on the <tt class="docutils literal"><span class="pre">main</span></tt> function. For +instance</p> +<pre class="literal-block"> +def main(...): + pass + +main.add_help = False +</pre> +<p>disable the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This is not +particularly elegant, but I assume the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be +happy with the default message and would not want to go at this level +of detail; still it is possible if she wants to. For instance, by +setting the <tt class="docutils literal"><span class="pre">description</span></tt> attribute, it is possible to add a comment to the +usage message (by default the docstring of the <tt class="docutils literal"><span class="pre">main</span></tt> function is +used as description). It is also possible to change the option prefix; +for instance if your script must run under Windows and you want to use +"/" as option prefix you can add the lines:</p> +<pre class="literal-block"> +main.prefix_chars='-/' +main.short_prefix = '/' +</pre> +<p>The recognition of the <tt class="docutils literal"><span class="pre">short_prefix</span></tt> attribute is a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> +extension; there is also a companion <tt class="docutils literal"><span class="pre">long_prefix</span></tt> attribute with +default value of <tt class="docutils literal"><span class="pre">--</span></tt>. <tt class="docutils literal"><span class="pre">prefix_chars</span></tt> is an <a class="reference external" href="http://argparse.googlecode.com">argparse</a> feature. +Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> to +understand the meaning of the other options. If there is a set of +options that you use very often, you may consider writing a decorator +adding such options to the <tt class="docutils literal"><span class="pre">main</span></tt> function for you. For simplicity, +<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic of that kind.</p> +<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by +invoking the <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> utility function:</p> +<pre class="doctest-block"> +>>> import plac +>>> def main(arg): +... pass +... +>>> print plac.parser_from(main) +ArgumentParser(prog='', usage=None, description=None, version=None, +formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', +add_help=True) +</pre> +<p>I use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> in the unit tests of the module, but regular +users should never need to use it.</p> +</div> +<div class="section" id="custom-annotation-objects"> +<h1><a class="toc-backref" href="#id9">Custom annotation objects</a></h1> +<p>Internally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses an <tt class="docutils literal"><span class="pre">Annotation</span></tt> class to convert the tuples +in the function signature into annotation objects, i.e. objects with +six attributes <tt class="docutils literal"><span class="pre">help,</span> <span class="pre">kind,</span> <span class="pre">short,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar</span></tt>.</p> +<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> +<blockquote> +<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> +</blockquote> +<p>You can use such annotations objects as follows:</p> +<blockquote> +<pre class="literal-block"> +# 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) + +</pre> +</blockquote> +<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 +</pre> +<p>You can go on and define <tt class="docutils literal"><span class="pre">Option</span></tt> and <tt class="docutils literal"><span class="pre">Flag</span></tt> classes, if you like. +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 class="section" id="plac-vs-argparse"> +<h1><a class="toc-backref" href="#id10">plac vs argparse</a></h1> +<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 particular you should be aware +of the following limitations/differences.</p> +<ul class="simple"> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> automatically defines both a long and short form for each options, +just like <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a>. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> allows you to define only a long form, +or only a short form, if you like. However, since I have always been +happy with the behavior of <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a>, which I feel is pretty much +consistent, I have decided not to support this feature.</li> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support "required options". 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></li> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to +define generalized two-value flags with values different from <tt class="docutils literal"><span class="pre">True</span></tt> +and <tt class="docutils literal"><span class="pre">False</span></tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but +since you can use options with two choices instead, and in any case +the conversion from <tt class="docutils literal"><span class="pre">{True,</span> <span class="pre">False}</span></tt> to any couple of values +can be trivially implemented with a ternary operator +(<tt class="docutils literal"><span class="pre">value1</span> <span class="pre">if</span> <span class="pre">flag</span> <span class="pre">else</span> <span class="pre">value2</span></tt>), I have removed it (KISS rules!).</li> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal"><span class="pre">nargs</span></tt> options directly (it uses them internally, +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 to <tt class="docutils literal"><span class="pre">nargs</span></tt>.</li> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support subparsers directly. For the moment, this +looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</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 may mitigate the +need for custom actions.</li> +</ul> +<p>I should stress again that if you want to access all of the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features +from <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you can use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> and you will get +the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object. The the full power of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> +is then available to you: you can use <tt class="docutils literal"><span class="pre">add_argument</span></tt>, <tt class="docutils literal"><span class="pre">add_subparsers()</span></tt>, +etc. In other words, while some features are not supported directly, +<em>all</em> features are supported indirectly.</p> +</div> +<div class="section" id="the-future"> +<h1><a class="toc-backref" href="#id11">The future</a></h1> +<p>Currently plac is below 100 lines of code, not counting blanks, comments +and docstrings. I do not plan to extend it 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 code 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 experimental: after all +I wrote it in three days, including the tests, the documentation and the +time to learn <a class="reference external" href="http://argparse.googlecode.com">argparse</a>.</p> +</div> +<div class="section" id="trivia-the-story-behind-the-name"> +<h1><a class="toc-backref" href="#id12">Trivia: the story behind the name</a></h1> +<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> project started very humble: I just wanted to make +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"><span class="pre">optionparser</span></tt> name was ruled out, since I was +using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal"><span class="pre">argparse_plus</span></tt> was also ruled out, +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 clap (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! <expletives deleted></p> +<p>Having little fantasy, I decided to rename everything again to plac, as +an anagram of clap: since it is a non-existing English name, I hope nobody +will steal it from me!</p> +</div> +</div> +</body> +</html> diff --git a/plac/doc/article.pdf b/plac/doc/plac.pdf index e3cf4d7..45cc4f1 100644 --- a/plac/doc/article.pdf +++ b/plac/doc/plac.pdf @@ -156,7 +156,7 @@ endobj /Dest [ 56 0 R
/XYZ
62.69291
- 470.6236
+ 458.6236
0 ]
/Rect [ 62.69291
518.5936
@@ -174,7 +174,7 @@ endobj /Dest [ 56 0 R
/XYZ
62.69291
- 470.6236
+ 458.6236
0 ]
/Rect [ 527.0227
518.5936
@@ -192,7 +192,7 @@ endobj /Dest [ 64 0 R
/XYZ
62.69291
- 305.4236
+ 269.4236
0 ]
/Rect [ 62.69291
500.5936
@@ -210,7 +210,7 @@ endobj /Dest [ 64 0 R
/XYZ
62.69291
- 305.4236
+ 269.4236
0 ]
/Rect [ 527.0227
500.5936
@@ -297,14 +297,14 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 83 0 R
+ /Dest [ 82 0 R
/XYZ
62.69291
- 407.2849
+ 395.2849
0 ]
/Rect [ 62.69291
446.5936
- 282.1729
+ 180.5229
458.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -315,10 +315,10 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 83 0 R
+ /Dest [ 82 0 R
/XYZ
62.69291
- 407.2849
+ 395.2849
0 ]
/Rect [ 527.0227
446.5936
@@ -333,14 +333,14 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 90 0 R
+ /Dest [ 88 0 R
/XYZ
62.69291
- 480.6236
+ 448.6236
0 ]
/Rect [ 62.69291
428.5936
- 191.5929
+ 282.1729
440.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -351,10 +351,10 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 90 0 R
+ /Dest [ 88 0 R
/XYZ
62.69291
- 480.6236
+ 448.6236
0 ]
/Rect [ 527.0227
428.5936
@@ -369,14 +369,14 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 113 0 R
+ /Dest [ 95 0 R
/XYZ
62.69291
- 587.8236
+ 525.8236
0 ]
/Rect [ 62.69291
410.5936
- 141.6229
+ 191.5929
422.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -387,10 +387,10 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 113 0 R
+ /Dest [ 95 0 R
/XYZ
62.69291
- 587.8236
+ 525.8236
0 ]
/Rect [ 521.4627
410.5936
@@ -405,14 +405,14 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 113 0 R
+ /Dest [ 121 0 R
/XYZ
62.69291
- 188.8236
+ 587.8236
0 ]
/Rect [ 62.69291
392.5936
- 136.0529
+ 141.6229
404.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -423,10 +423,10 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 113 0 R
+ /Dest [ 121 0 R
/XYZ
62.69291
- 188.8236
+ 587.8236
0 ]
/Rect [ 521.4627
392.5936
@@ -441,10 +441,10 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 118 0 R
+ /Dest [ 121 0 R
/XYZ
62.69291
- 256.6236
+ 188.8236
0 ]
/Rect [ 62.69291
374.5936
@@ -459,10 +459,10 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 118 0 R
+ /Dest [ 121 0 R
/XYZ
62.69291
- 256.6236
+ 188.8236
0 ]
/Rect [ 521.4627
374.5936
@@ -477,7 +477,7 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 129 0 R
+ /Dest [ 132 0 R
/XYZ
62.69291
765.0236
@@ -495,7 +495,7 @@ endobj 0
0 ]
/Contents ()
- /Dest [ 129 0 R
+ /Dest [ 132 0 R
/XYZ
62.69291
765.0236
@@ -515,9 +515,9 @@ endobj /Border [ 0
0
0 ]
- /Rect [ 361.1228
+ /Rect [ 403.6414
290.5936
- 392.9767
+ 435.6067
302.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -530,10 +530,10 @@ endobj /Border [ 0
0
0 ]
- /Rect [ 493.6727
- 290.5936
- 531.3087
- 302.5936 ]
+ /Rect [ 62.69291
+ 278.5936
+ 104.8841
+ 290.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -545,9 +545,9 @@ endobj /Border [ 0
0
0 ]
- /Rect [ 164.0504
+ /Rect [ 206.3791
278.5936
- 206.7572
+ 249.1203
290.5936 ]
/Subtype /Link
/Type /Annot >>
@@ -560,10 +560,10 @@ endobj /Border [ 0
0
0 ]
- /Rect [ 493.1227
- 278.5936
- 532.1158
- 290.5936 ]
+ /Rect [ 62.69291
+ 266.5936
+ 105.1329
+ 278.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -680,12 +680,12 @@ endobj 38 0 R
39 0 R
40 0 R ]
- /Contents 146 0 R
+ /Contents 149 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -759,9 +759,9 @@ endobj 0
0 ]
/Rect [ 504.7827
- 314.3936
+ 302.3936
532.1006
- 326.3936 ]
+ 314.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -774,9 +774,9 @@ endobj 0
0 ]
/Rect [ 82.40665
- 302.3936
+ 290.3936
124.3504
- 314.3936 ]
+ 302.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -789,9 +789,9 @@ endobj 0
0 ]
/Rect [ 62.69291
- 290.3936
+ 278.3936
104.9329
- 302.3936 ]
+ 290.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -804,12 +804,12 @@ endobj 46 0 R
47 0 R
48 0 R ]
- /Contents 147 0 R
+ /Contents 150 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -859,9 +859,9 @@ endobj 0
0 ]
/Rect [ 83.6329
- 635.3936
+ 623.3936
105.6829
- 647.3936 ]
+ 635.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -874,9 +874,9 @@ endobj 0
0 ]
/Rect [ 421.9727
- 635.3936
+ 623.3936
465.1427
- 647.3936 ]
+ 635.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -889,9 +889,9 @@ endobj 0
0 ]
/Rect [ 211.6529
- 486.1936
+ 474.1936
232.7729
- 498.1936 ]
+ 486.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -904,9 +904,9 @@ endobj 0
0 ]
/Rect [ 85.47291
- 229.9936
+ 205.9936
106.5929
- 241.9936 ]
+ 217.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -919,12 +919,12 @@ endobj 53 0 R
54 0 R
55 0 R ]
- /Contents 148 0 R
+ /Contents 151 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -944,9 +944,9 @@ endobj 0
0 ]
/Rect [ 62.69291
- 687.3936
+ 663.3936
84.20915
- 699.3936 ]
+ 675.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -959,9 +959,9 @@ endobj 0
0 ]
/Rect [ 91.59679
- 494.1936
+ 458.1936
109.9368
- 506.1936 ]
+ 470.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -974,9 +974,9 @@ endobj 0
0 ]
/Rect [ 446.6187
- 332.9936
+ 296.9936
464.9587
- 344.9936 ]
+ 308.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -989,9 +989,9 @@ endobj 0
0 ]
/Rect [ 446.8103
- 221.9936
+ 185.9936
502.5727
- 233.9936 ]
+ 197.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1004,9 +1004,9 @@ endobj 0
0 ]
/Rect [ 260.18
- 209.9936
+ 173.9936
312.43
- 221.9936 ]
+ 185.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1019,9 +1019,9 @@ endobj 0
0 ]
/Rect [ 62.69291
- 179.9936
+ 143.9936
84.28901
- 191.9936 ]
+ 155.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1034,9 +1034,9 @@ endobj 0
0 ]
/Rect [ 161.7834
- 179.9936
+ 143.9936
217.2895
- 191.9936 ]
+ 155.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1050,12 +1050,12 @@ endobj 61 0 R
62 0 R
63 0 R ]
- /Contents 149 0 R
+ /Contents 152 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1075,9 +1075,9 @@ endobj 0
0 ]
/Rect [ 133.1479
- 629.3936
+ 617.3936
154.4129
- 641.3936 ]
+ 629.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1085,12 +1085,12 @@ endobj 66 0 obj
% Page dictionary
<< /Annots [ 65 0 R ]
- /Contents 150 0 R
+ /Contents 153 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1168,12 +1168,12 @@ endobj 68 0 R
69 0 R
70 0 R ]
- /Contents 151 0 R
+ /Contents 154 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1283,12 +1283,12 @@ endobj 75 0 R
76 0 R
77 0 R ]
- /Contents 152 0 R
+ /Contents 155 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1307,10 +1307,10 @@ endobj /Border [ 0
0
0 ]
- /Rect [ 62.69291
- 371.8549
- 83.9079
- 383.8549 ]
+ /Rect [ 338.1568
+ 359.8549
+ 360.5113
+ 371.8549 ]
/Subtype /Link
/Type /Annot >>
endobj
@@ -1318,19 +1318,86 @@ endobj 80 0 obj
<< /A << /S /URI
/Type /Action
+ /URI (http://www.sqlalchemy.org/) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 110.6843
+ 347.8549
+ 169.0343
+ 359.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER68': class PDFDictionary
+81 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 168.3029
+ 335.8549
+ 208.8829
+ 347.8549 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Page8': class PDFPage
+82 0 obj
+% Page dictionary
+<< /Annots [ 79 0 R
+ 80 0 R
+ 81 0 R ]
+ /Contents 156 0 R
+ /MediaBox [ 0
+ 0
+ 595.2756
+ 841.8898 ]
+ /Parent 148 0 R
+ /Resources << /Font 1 0 R
+ /ProcSet [ /PDF
+ /Text
+ /ImageB
+ /ImageC
+ /ImageI ] >>
+ /Rotate 0
+ /Trans << >>
+ /Type /Page >>
+endobj
+% 'Annot.NUMBER69': class PDFDictionary
+83 0 obj
+<< /A << /S /URI
+ /Type /Action
+ /URI (http://pypi.python.org/pypi/plac) >>
+ /Border [ 0
+ 0
+ 0 ]
+ /Rect [ 62.69291
+ 413.1936
+ 83.9079
+ 425.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER70': class PDFDictionary
+84 0 obj
+<< /A << /S /URI
+ /Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
/Rect [ 133.1029
- 371.8549
+ 413.1936
175.4379
- 383.8549 ]
+ 425.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER68': class PDFDictionary
-81 0 obj
+% 'Annot.NUMBER71': class PDFDictionary
+85 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1338,14 +1405,29 @@ endobj 0
0 ]
/Rect [ 454.1177
- 371.8549
+ 413.1936
496.4527
- 383.8549 ]
+ 425.1936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER69': class PDFDictionary
-82 0 obj
+% 'Annot.NUMBER72': class PDFDictionary
+86 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 [ 62.69291
+ 359.1936
+ 146.0529
+ 371.1936 ]
+ /Subtype /Link
+ /Type /Annot >>
+endobj
+% 'Annot.NUMBER73': class PDFDictionary
+87 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1353,25 +1435,26 @@ endobj 0
0 ]
/Rect [ 127.9872
- 162.6549
+ 203.9936
149.3819
- 174.6549 ]
+ 215.9936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page8': class PDFPage
-83 0 obj
+% 'Page9': class PDFPage
+88 0 obj
% Page dictionary
-<< /Annots [ 79 0 R
- 80 0 R
- 81 0 R
- 82 0 R ]
- /Contents 153 0 R
+<< /Annots [ 83 0 R
+ 84 0 R
+ 85 0 R
+ 86 0 R
+ 87 0 R ]
+ /Contents 157 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1382,8 +1465,8 @@ endobj /Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER70': class PDFDictionary
-84 0 obj
+% 'Annot.NUMBER74': class PDFDictionary
+89 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1391,14 +1474,14 @@ endobj 0
0 ]
/Rect [ 326.9971
- 711.3936
+ 756.5936
351.8113
- 723.3936 ]
+ 768.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER71': class PDFDictionary
-85 0 obj
+% 'Annot.NUMBER75': class PDFDictionary
+90 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1406,14 +1489,14 @@ endobj 0
0 ]
/Rect [ 407.706
- 699.3936
+ 744.5936
452.2944
- 711.3936 ]
+ 756.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER72': class PDFDictionary
-86 0 obj
+% 'Annot.NUMBER76': class PDFDictionary
+91 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1421,14 +1504,14 @@ endobj 0
0 ]
/Rect [ 259.0928
- 687.3936
+ 732.5936
302.7528
- 699.3936 ]
+ 744.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER73': class PDFDictionary
-87 0 obj
+% 'Annot.NUMBER77': class PDFDictionary
+92 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1436,14 +1519,14 @@ endobj 0
0 ]
/Rect [ 258.3129
- 663.3936
+ 708.5936
279.4329
- 675.3936 ]
+ 720.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER74': class PDFDictionary
-88 0 obj
+% 'Annot.NUMBER78': class PDFDictionary
+93 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -1451,14 +1534,14 @@ endobj 0
0 ]
/Rect [ 327.2261
- 645.3936
+ 690.5936
410.5152
- 657.3936 ]
+ 702.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER75': class PDFDictionary
-89 0 obj
+% 'Annot.NUMBER79': class PDFDictionary
+94 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1466,27 +1549,27 @@ endobj 0
0 ]
/Rect [ 106.6216
- 445.1936
+ 490.3936
128.3202
- 457.1936 ]
+ 502.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Page9': class PDFPage
-90 0 obj
+% 'Page10': class PDFPage
+95 0 obj
% Page dictionary
-<< /Annots [ 84 0 R
- 85 0 R
- 86 0 R
- 87 0 R
- 88 0 R
- 89 0 R ]
- /Contents 154 0 R
+<< /Annots [ 89 0 R
+ 90 0 R
+ 91 0 R
+ 92 0 R
+ 93 0 R
+ 94 0 R ]
+ /Contents 158 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1497,8 +1580,8 @@ endobj /Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER76': class PDFDictionary
-91 0 obj
+% 'Annot.NUMBER80': class PDFDictionary
+96 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1512,8 +1595,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER77': class PDFDictionary
-92 0 obj
+% 'Annot.NUMBER81': class PDFDictionary
+97 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1527,8 +1610,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER78': class PDFDictionary
-93 0 obj
+% 'Annot.NUMBER82': class PDFDictionary
+98 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1542,8 +1625,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER79': class PDFDictionary
-94 0 obj
+% 'Annot.NUMBER83': class PDFDictionary
+99 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html) >>
@@ -1557,8 +1640,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER80': class PDFDictionary
-95 0 obj
+% 'Annot.NUMBER84': class PDFDictionary
+100 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1572,8 +1655,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER81': class PDFDictionary
-96 0 obj
+% 'Annot.NUMBER85': class PDFDictionary
+101 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html) >>
@@ -1587,8 +1670,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER82': class PDFDictionary
-97 0 obj
+% 'Annot.NUMBER86': class PDFDictionary
+102 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1602,8 +1685,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER83': class PDFDictionary
-98 0 obj
+% 'Annot.NUMBER87': class PDFDictionary
+103 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1617,8 +1700,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER84': class PDFDictionary
-99 0 obj
+% 'Annot.NUMBER88': class PDFDictionary
+104 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1632,8 +1715,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER85': class PDFDictionary
-100 0 obj
+% 'Annot.NUMBER89': class PDFDictionary
+105 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1647,8 +1730,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER86': class PDFDictionary
-101 0 obj
+% 'Annot.NUMBER90': class PDFDictionary
+106 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1662,8 +1745,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER87': class PDFDictionary
-102 0 obj
+% 'Annot.NUMBER91': class PDFDictionary
+107 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1677,8 +1760,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER88': class PDFDictionary
-103 0 obj
+% 'Annot.NUMBER92': class PDFDictionary
+108 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1692,8 +1775,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER89': class PDFDictionary
-104 0 obj
+% 'Annot.NUMBER93': class PDFDictionary
+109 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1707,8 +1790,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER90': class PDFDictionary
-105 0 obj
+% 'Annot.NUMBER94': class PDFDictionary
+110 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1722,8 +1805,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER91': class PDFDictionary
-106 0 obj
+% 'Annot.NUMBER95': class PDFDictionary
+111 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1737,8 +1820,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER92': class PDFDictionary
-107 0 obj
+% 'Annot.NUMBER96': class PDFDictionary
+112 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1752,8 +1835,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER93': class PDFDictionary
-108 0 obj
+% 'Annot.NUMBER97': class PDFDictionary
+113 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1767,8 +1850,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER94': class PDFDictionary
-109 0 obj
+% 'Annot.NUMBER98': class PDFDictionary
+114 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1782,8 +1865,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER95': class PDFDictionary
-110 0 obj
+% 'Annot.NUMBER99': class PDFDictionary
+115 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -1797,8 +1880,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER96': class PDFDictionary
-111 0 obj
+% 'Annot.NUMBER100': class PDFDictionary
+116 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1812,64 +1895,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER97': class PDFDictionary
-112 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://pypi.python.org/pypi/plac) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 314.0328
- 153.3936
- 335.3494
- 165.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page10': class PDFPage
-113 0 obj
-% Page dictionary
-<< /Annots [ 91 0 R
- 92 0 R
- 93 0 R
- 94 0 R
- 95 0 R
- 96 0 R
- 97 0 R
- 98 0 R
- 99 0 R
- 100 0 R
- 101 0 R
- 102 0 R
- 103 0 R
- 104 0 R
- 105 0 R
- 106 0 R
- 107 0 R
- 108 0 R
- 109 0 R
- 110 0 R
- 111 0 R
- 112 0 R ]
- /Contents 155 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 145 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER98': class PDFDictionary
-114 0 obj
+% 'Annot.NUMBER101': class PDFDictionary
+117 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1877,14 +1904,14 @@ endobj 0
0 ]
/Rect [ 86.82623
- 197.1936
+ 129.3936
126.2862
- 209.1936 ]
+ 141.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER99': class PDFDictionary
-115 0 obj
+% 'Annot.NUMBER102': class PDFDictionary
+118 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1892,14 +1919,14 @@ endobj 0
0 ]
/Rect [ 415.1627
- 197.1936
+ 129.3936
459.306
- 209.1936 ]
+ 141.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER100': class PDFDictionary
-116 0 obj
+% 'Annot.NUMBER103': class PDFDictionary
+119 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1907,14 +1934,14 @@ endobj 0
0 ]
/Rect [ 468.9894
- 197.1936
+ 129.3936
492.0127
- 209.1936 ]
+ 141.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER101': class PDFDictionary
-117 0 obj
+% 'Annot.NUMBER104': class PDFDictionary
+120 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -1922,25 +1949,46 @@ endobj 0
0 ]
/Rect [ 62.69291
- 161.1936
+ 93.39362
102.1529
- 173.1936 ]
+ 105.3936 ]
/Subtype /Link
/Type /Annot >>
endobj
% 'Page11': class PDFPage
-118 0 obj
+121 0 obj
% Page dictionary
-<< /Annots [ 114 0 R
+<< /Annots [ 96 0 R
+ 97 0 R
+ 98 0 R
+ 99 0 R
+ 100 0 R
+ 101 0 R
+ 102 0 R
+ 103 0 R
+ 104 0 R
+ 105 0 R
+ 106 0 R
+ 107 0 R
+ 108 0 R
+ 109 0 R
+ 110 0 R
+ 111 0 R
+ 112 0 R
+ 113 0 R
+ 114 0 R
115 0 R
116 0 R
- 117 0 R ]
- /Contents 156 0 R
+ 117 0 R
+ 118 0 R
+ 119 0 R
+ 120 0 R ]
+ /Contents 159 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -1951,8 +1999,8 @@ endobj /Trans << >>
/Type /Page >>
endobj
-% 'Annot.NUMBER102': class PDFDictionary
-119 0 obj
+% 'Annot.NUMBER105': class PDFDictionary
+122 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1966,8 +2014,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER103': class PDFDictionary
-120 0 obj
+% 'Annot.NUMBER106': class PDFDictionary
+123 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
@@ -1981,8 +2029,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER104': class PDFDictionary
-121 0 obj
+% 'Annot.NUMBER107': class PDFDictionary
+124 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/plac) >>
@@ -1996,8 +2044,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER105': class PDFDictionary
-122 0 obj
+% 'Annot.NUMBER108': class PDFDictionary
+125 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser) >>
@@ -2011,23 +2059,23 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER106': class PDFDictionary
-123 0 obj
+% 'Annot.NUMBER109': class PDFDictionary
+126 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
/Border [ 0
0
0 ]
- /Rect [ 97.06763
+ /Rect [ 96.54131
693.5936
- 139.815
+ 139.0255
705.5936 ]
/Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER107': class PDFDictionary
-124 0 obj
+% 'Annot.NUMBER110': class PDFDictionary
+127 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2041,8 +2089,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER108': class PDFDictionary
-125 0 obj
+% 'Annot.NUMBER111': class PDFDictionary
+128 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
@@ -2056,8 +2104,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER109': class PDFDictionary
-126 0 obj
+% 'Annot.NUMBER112': class PDFDictionary
+129 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2071,8 +2119,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER110': class PDFDictionary
-127 0 obj
+% 'Annot.NUMBER113': class PDFDictionary
+130 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://argparse.googlecode.com) >>
@@ -2086,8 +2134,8 @@ endobj /Subtype /Link
/Type /Annot >>
endobj
-% 'Annot.NUMBER111': class PDFDictionary
-128 0 obj
+% 'Annot.NUMBER114': class PDFDictionary
+131 0 obj
<< /A << /S /URI
/Type /Action
/URI (http://pypi.python.org/pypi/Clap/0.7) >>
@@ -2102,24 +2150,24 @@ endobj /Type /Annot >>
endobj
% 'Page12': class PDFPage
-129 0 obj
+132 0 obj
% Page dictionary
-<< /Annots [ 119 0 R
- 120 0 R
- 121 0 R
- 122 0 R
+<< /Annots [ 122 0 R
123 0 R
124 0 R
125 0 R
126 0 R
127 0 R
- 128 0 R ]
- /Contents 157 0 R
+ 128 0 R
+ 129 0 R
+ 130 0 R
+ 131 0 R ]
+ /Contents 160 0 R
/MediaBox [ 0
0
595.2756
841.8898 ]
- /Parent 145 0 R
+ /Parent 148 0 R
/Resources << /Font 1 0 R
/ProcSet [ /PDF
/Text
@@ -2130,175 +2178,175 @@ endobj /Trans << >>
/Type /Page >>
endobj
-% 'R130': class PDFCatalog
-130 0 obj
+% 'R133': class PDFCatalog
+133 0 obj
% Document Root
-<< /Outlines 132 0 R
- /PageLabels 158 0 R
+<< /Outlines 135 0 R
+ /PageLabels 161 0 R
/PageMode /UseNone
- /Pages 145 0 R
+ /Pages 148 0 R
/Type /Catalog >>
endobj
-% 'R131': class PDFInfo
-131 0 obj
+% 'R134': class PDFInfo
+134 0 obj
<< /Author (Michele Simionato)
- /CreationDate (D:20100601131016-01'00')
+ /CreationDate (D:20100601145350-01'00')
/Keywords ()
/Producer (ReportLab http://www.reportlab.com)
/Subject (\(unspecified\))
/Title (Plac, the Easiest Command Line Arguments Parser in the World) >>
endobj
-% 'R132': class PDFOutlines
-132 0 obj
+% 'R135': class PDFOutlines
+135 0 obj
<< /Count 12
- /First 133 0 R
- /Last 144 0 R
+ /First 136 0 R
+ /Last 147 0 R
/Type /Outlines >>
endobj
% 'Outline.0': class OutlineEntryObject
-133 0 obj
+136 0 obj
<< /Dest [ 41 0 R
/XYZ
62.69291
338.0236
0 ]
- /Next 134 0 R
- /Parent 132 0 R
+ /Next 137 0 R
+ /Parent 135 0 R
/Title (Introduction) >>
endobj
% 'Outline.1': class OutlineEntryObject
-134 0 obj
+137 0 obj
<< /Dest [ 49 0 R
/XYZ
62.69291
729.0236
0 ]
- /Next 135 0 R
- /Parent 132 0 R
- /Prev 133 0 R
+ /Next 138 0 R
+ /Parent 135 0 R
+ /Prev 136 0 R
/Title (The importance of scaling down) >>
endobj
% 'Outline.2': class OutlineEntryObject
-135 0 obj
+138 0 obj
<< /Dest [ 56 0 R
/XYZ
62.69291
- 470.6236
+ 458.6236
0 ]
- /Next 136 0 R
- /Parent 132 0 R
- /Prev 134 0 R
+ /Next 139 0 R
+ /Parent 135 0 R
+ /Prev 137 0 R
/Title (Positional default arguments) >>
endobj
% 'Outline.3': class OutlineEntryObject
-136 0 obj
+139 0 obj
<< /Dest [ 64 0 R
/XYZ
62.69291
- 305.4236
+ 269.4236
0 ]
- /Next 137 0 R
- /Parent 132 0 R
- /Prev 135 0 R
+ /Next 140 0 R
+ /Parent 135 0 R
+ /Prev 138 0 R
/Title (Options and flags) >>
endobj
% 'Outline.4': class OutlineEntryObject
-137 0 obj
+140 0 obj
<< /Dest [ 71 0 R
/XYZ
62.69291
201.0236
0 ]
- /Next 138 0 R
- /Parent 132 0 R
- /Prev 136 0 R
+ /Next 141 0 R
+ /Parent 135 0 R
+ /Prev 139 0 R
/Title (plac for Python 2.X users) >>
endobj
% 'Outline.5': class OutlineEntryObject
-138 0 obj
+141 0 obj
<< /Dest [ 78 0 R
/XYZ
62.69291
490.6236
0 ]
- /Next 139 0 R
- /Parent 132 0 R
- /Prev 137 0 R
+ /Next 142 0 R
+ /Parent 135 0 R
+ /Prev 140 0 R
/Title (More features) >>
endobj
% 'Outline.6': class OutlineEntryObject
-139 0 obj
-<< /Dest [ 83 0 R
+142 0 obj
+<< /Dest [ 82 0 R
/XYZ
62.69291
- 407.2849
+ 395.2849
0 ]
- /Next 140 0 R
- /Parent 132 0 R
- /Prev 138 0 R
- /Title (A few notes on the underlying implementation) >>
+ /Next 143 0 R
+ /Parent 135 0 R
+ /Prev 141 0 R
+ /Title (A more realistic example) >>
endobj
% 'Outline.7': class OutlineEntryObject
-140 0 obj
-<< /Dest [ 90 0 R
+143 0 obj
+<< /Dest [ 88 0 R
/XYZ
62.69291
- 480.6236
+ 448.6236
0 ]
- /Next 141 0 R
- /Parent 132 0 R
- /Prev 139 0 R
- /Title (Custom annotation objects) >>
+ /Next 144 0 R
+ /Parent 135 0 R
+ /Prev 142 0 R
+ /Title (A few notes on the underlying implementation) >>
endobj
% 'Outline.8': class OutlineEntryObject
-141 0 obj
-<< /Dest [ 113 0 R
+144 0 obj
+<< /Dest [ 95 0 R
/XYZ
62.69291
- 587.8236
+ 525.8236
0 ]
- /Next 142 0 R
- /Parent 132 0 R
- /Prev 140 0 R
- /Title (plac vs argparse) >>
+ /Next 145 0 R
+ /Parent 135 0 R
+ /Prev 143 0 R
+ /Title (Custom annotation objects) >>
endobj
% 'Outline.9': class OutlineEntryObject
-142 0 obj
-<< /Dest [ 113 0 R
+145 0 obj
+<< /Dest [ 121 0 R
/XYZ
62.69291
- 188.8236
+ 587.8236
0 ]
- /Next 143 0 R
- /Parent 132 0 R
- /Prev 141 0 R
- /Title (A final example) >>
+ /Next 146 0 R
+ /Parent 135 0 R
+ /Prev 144 0 R
+ /Title (plac vs argparse) >>
endobj
% 'Outline.10': class OutlineEntryObject
-143 0 obj
-<< /Dest [ 118 0 R
+146 0 obj
+<< /Dest [ 121 0 R
/XYZ
62.69291
- 256.6236
+ 188.8236
0 ]
- /Next 144 0 R
- /Parent 132 0 R
- /Prev 142 0 R
+ /Next 147 0 R
+ /Parent 135 0 R
+ /Prev 145 0 R
/Title (The future) >>
endobj
% 'Outline.11': class OutlineEntryObject
-144 0 obj
-<< /Dest [ 129 0 R
+147 0 obj
+<< /Dest [ 132 0 R
/XYZ
62.69291
765.0236
0 ]
- /Parent 132 0 R
- /Prev 143 0 R
+ /Parent 135 0 R
+ /Prev 146 0 R
/Title (Trivia: the story behind the name) >>
endobj
-% 'R145': class PDFPages
-145 0 obj
+% 'R148': class PDFPages
+148 0 obj
% page tree
<< /Count 12
/Kids [ 41 0 R
@@ -2308,17 +2356,17 @@ endobj 66 0 R
71 0 R
78 0 R
- 83 0 R
- 90 0 R
- 113 0 R
- 118 0 R
- 129 0 R ]
+ 82 0 R
+ 88 0 R
+ 95 0 R
+ 121 0 R
+ 132 0 R ]
/Type /Pages >>
endobj
-% 'R146': class PDFStream
-146 0 obj
+% 'R149': class PDFStream
+149 0 obj
% page stream
-<< /Length 8388 >>
+<< /Length 8404 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -2552,7 +2600,7 @@ Q q
1 0 0 1 0 93 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A few notes on the underlying implementation) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A more realistic example) Tj T* ET
Q
Q
q
@@ -2566,7 +2614,7 @@ Q q
1 0 0 1 0 75 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Custom annotation objects) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A few notes on the underlying implementation) Tj T* ET
Q
Q
q
@@ -2580,7 +2628,7 @@ Q q
1 0 0 1 0 57 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (plac vs argparse) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (Custom annotation objects) Tj T* ET
Q
Q
q
@@ -2594,7 +2642,7 @@ Q q
1 0 0 1 0 39 cm
q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (A final example) Tj T* ET
+BT 1 0 0 1 0 4.82 Tm 12 TL /F2 10 Tf 0 0 .501961 rg (plac vs argparse) Tj T* ET
Q
Q
q
@@ -2602,7 +2650,7 @@ q q
0 0 .501961 rg
0 0 .501961 RG
-BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (10) Tj T* -60.88 0 Td ET
+BT 1 0 0 1 0 4.82 Tm /F2 10 Tf 12 TL 60.88 0 Td (11) Tj T* -60.88 0 Td ET
Q
Q
q
@@ -2645,7 +2693,7 @@ Q q
1 0 0 1 62.69291 251.0236 cm
q
-BT 1 0 0 1 0 52.82 Tm .05061 Tw 12 TL /F1 10 Tf 0 0 0 rg (There is no want of command line arguments parsers in Python world. The standard library alone contains) Tj T* 0 Tw 1.273984 Tw (three different modules for the parsing of command line options: ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (\(from the stone age\), ) Tj 0 0 .501961 rg (optparse) Tj T* 0 Tw .46686 Tw 0 0 0 rg (\(from Python 2.3\) and ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (\(from Python 2.7\). All of them are quite powerful and especially ) Tj 0 0 .501961 rg (argparse) Tj T* 0 Tw .25686 Tw 0 0 0 rg (is an industrial strength solution; unfortunately, all of them feature a non-zero learning curve and a certain) Tj T* 0 Tw (verbosity.) Tj T* ET
+BT 1 0 0 1 0 52.82 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.385318 Tw (contains three different modules for the parsing of command line options: ) Tj 0 0 .501961 rg (getopt ) Tj 0 0 0 rg (\(from the stone age\),) Tj T* 0 Tw .501235 Tw 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (\(from Python 2.3\) and ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (\(from Python 2.7\). All of them are quite powerful and especially) Tj T* 0 Tw .199984 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (is an industrial strength solution; unfortunately, all of them feature a non-zero learning curve and) Tj T* 0 Tw (a certain verbosity.) Tj T* ET
Q
Q
q
@@ -2664,10 +2712,10 @@ Q endstream
endobj
-% 'R147': class PDFStream
-147 0 obj
+% 'R150': class PDFStream
+150 0 obj
% page stream
-<< /Length 5433 >>
+<< /Length 5477 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -2698,7 +2746,7 @@ q 1 0 0 1 62.69291 558.0236 cm
Q
q
-1 0 0 1 62.69291 388.8236 cm
+1 0 0 1 62.69291 376.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2713,11 +2761,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 448.6898 168 re B*
+n -6 -6 448.6898 180 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 149.71 Tm /F4 10 Tf 12 TL (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) 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
+BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL (# example1.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) 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
@@ -2727,19 +2775,19 @@ q Q
Q
q
-1 0 0 1 62.69291 388.8236 cm
+1 0 0 1 62.69291 376.8236 cm
Q
q
-1 0 0 1 62.69291 286.8236 cm
+1 0 0 1 62.69291 274.8236 cm
q
BT 1 0 0 1 0 88.82 Tm .880651 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see the whole ) Tj /F4 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block \(nine lines\) is essentially boilerplate that) Tj T* 0 Tw 1.125318 Tw (should not exists. Actually I think the Python language should recognize the main function and perform) Tj T* 0 Tw 1.385984 Tw (trivial arguments parsing behind the scenes; 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 /F5 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 .482093 Tw (modules do not help for this use case, which may be trivial, but it is still incredibly common. Using ) Tj 0 0 .501961 rg (getopt) Tj T* 0 Tw .253735 Tw 0 0 0 rg (and ) Tj 0 0 .501961 rg (optparse ) Tj 0 0 0 rg (does not help, since they are intended to manage options and not positional arguments; the) Tj T* 0 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module helps a bit and it is able to reduce the boilerplate from nine lines to six lines:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 280.8236 cm
+1 0 0 1 62.69291 268.8236 cm
Q
q
-1 0 0 1 62.69291 147.6236 cm
+1 0 0 1 62.69291 123.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2754,11 +2802,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 448.6898 132 re B*
+n -6 -6 448.6898 144 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F4 10 Tf 12 TL (def main\(dsn\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn\)) 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
+BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (# example2.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something on the database") Tj T* ( print\(dsn\)) 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
@@ -2768,10 +2816,10 @@ q Q
Q
q
-1 0 0 1 62.69291 147.6236 cm
+1 0 0 1 62.69291 123.6236 cm
Q
q
-1 0 0 1 62.69291 105.6236 cm
+1 0 0 1 62.69291 81.62362 cm
q
0 0 0 rg
BT 1 0 0 1 0 28.82 Tm /F1 10 Tf 12 TL 1.644269 Tw (However saving three lines does not justify introducing the external dependency: most people will not) Tj T* 0 Tw .276303 Tw (switch Python 2.7, which at the time of this writing is just about to be released, for many years. Moreover,) Tj T* 0 Tw (it just feels too complex to instantiate a class and to define a parser by hand for such a trivial task.) Tj T* ET
@@ -2781,10 +2829,10 @@ Q endstream
endobj
-% 'R148': class PDFStream
-148 0 obj
+% 'R151': class PDFStream
+151 0 obj
% page stream
-<< /Length 4329 >>
+<< /Length 4343 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -2797,7 +2845,7 @@ q 1 0 0 1 62.69291 735.0236 cm
Q
q
-1 0 0 1 62.69291 649.8236 cm
+1 0 0 1 62.69291 637.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2812,11 +2860,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 448.6898 84 re B*
+n -6 -6 448.6898 96 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL (# example3.py) Tj T* (def main\(dsn\):) Tj T* ( "Do something with the database") Tj T* ( print\(dsn\)) Tj T* ( ) Tj T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
Q
Q
Q
@@ -2826,16 +2874,16 @@ q Q
Q
q
-1 0 0 1 62.69291 649.8236 cm
+1 0 0 1 62.69291 637.8236 cm
Q
q
-1 0 0 1 62.69291 619.8236 cm
+1 0 0 1 62.69291 607.8236 cm
q
BT 1 0 0 1 0 16.82 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 502.6236 cm
+1 0 0 1 62.69291 490.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -2856,29 +2904,29 @@ Q Q
Q
q
-1 0 0 1 62.69291 482.6236 cm
+1 0 0 1 62.69291 470.6236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (This is only the tip of 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 62.69291 449.6236 cm
+1 0 0 1 62.69291 437.6236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Positional default arguments) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 431.6236 cm
+1 0 0 1 62.69291 419.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (I have encountered this use case at work hundreds of times:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 425.6236 cm
+1 0 0 1 62.69291 413.6236 cm
Q
q
-1 0 0 1 62.69291 244.4236 cm
+1 0 0 1 62.69291 220.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -2893,10 +2941,10 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 448.6898 180 re B*
+n -6 -6 448.6898 192 re B*
Q
q
-BT 1 0 0 1 0 161.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (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
+BT 1 0 0 1 0 173.71 Tm 12 TL /F4 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
@@ -2906,16 +2954,16 @@ q Q
Q
q
-1 0 0 1 62.69291 244.4236 cm
+1 0 0 1 62.69291 220.4236 cm
Q
q
-1 0 0 1 62.69291 226.4236 cm
+1 0 0 1 62.69291 202.4236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (With ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (the entire ) Tj /F4 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 181.2236 cm
+1 0 0 1 62.69291 157.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -2936,7 +2984,7 @@ Q Q
Q
q
-1 0 0 1 62.69291 161.2236 cm
+1 0 0 1 62.69291 137.2236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (In other words, six lines of boilerplate have been removed, and I have the usage message for free:) Tj T* ET
@@ -2953,11 +3001,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
+n -6 -6 468.6898 48 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL (usage: example4_.py [-h] dsn [table] [today]) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( table) Tj T* ET
+BT 1 0 0 1 0 29.71 Tm /F4 10 Tf 12 TL (usage: example4_.py [-h] dsn [table] [today]) Tj T* T* (positional arguments:) Tj T* ET
Q
Q
Q
@@ -2967,14 +3015,14 @@ Q endstream
endobj
-% 'R149': class PDFStream
-149 0 obj
+% 'R152': class PDFStream
+152 0 obj
% page stream
-<< /Length 4867 >>
+<< /Length 4886 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 703.8236 cm
+1 0 0 1 62.69291 679.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -2984,27 +3032,27 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
+n -6 -6 468.6898 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ( today) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL ( dsn) Tj T* ( table) Tj T* ( today) 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 671.8236 cm
+1 0 0 1 62.69291 647.8236 cm
q
-BT 1 0 0 1 0 16.82 Tm .396235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (manages transparently even the case when you want to pass a variable number of arguments. Here) Tj T* 0 Tw (is an example, a script running on a database a series of ) Tj /F4 10 Tf (.sql ) Tj /F1 10 Tf (scripts:) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm .396235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (manages transparently even the case when you want to pass a variable number of arguments. Here) Tj T* 0 Tw (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 665.8236 cm
+1 0 0 1 62.69291 641.8236 cm
Q
q
-1 0 0 1 62.69291 508.6236 cm
+1 0 0 1 62.69291 472.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3019,10 +3067,10 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 442.6898 156 re B*
+n -6 -6 442.6898 168 re B*
Q
q
-BT 1 0 0 1 0 137.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (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* ( print\('executing %s' % script\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( if len\(sys.argv\) ) Tj (<) Tj ( 2:) Tj T* ( sys.exit\('usage: python %s dsn script.sql ...' % sys.argv[0]\)) Tj T* ( main\(sys.argv[1:]\)) Tj T* ET
+BT 1 0 0 1 0 149.71 Tm 12 TL /F4 10 Tf 0 0 0 rg (# example6.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* ( print\('executing %s' % script\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import sys) Tj T* ( if len\(sys.argv\) ) Tj (<) Tj ( 2:) Tj T* ( sys.exit\('usage: python %s dsn script.sql ...' % sys.argv[0]\)) Tj T* ( main\(sys.argv[1:]\)) Tj T* ET
Q
Q
Q
@@ -3032,16 +3080,16 @@ q Q
Q
q
-1 0 0 1 62.69291 508.6236 cm
+1 0 0 1 62.69291 472.6236 cm
Q
q
-1 0 0 1 62.69291 478.6236 cm
+1 0 0 1 62.69291 442.6236 cm
q
BT 1 0 0 1 0 16.82 Tm .563876 Tw 12 TL /F1 10 Tf 0 0 0 rg (Using ) Tj 0 0 .501961 rg (plac) Tj 0 0 0 rg (, you can just replace the ) Tj /F4 10 Tf (__main__ ) Tj /F1 10 Tf (block with the usual two lines \(I have defined an Emacs) Tj T* 0 Tw (keybinding for them\) and you get the following usage message:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 361.4236 cm
+1 0 0 1 62.69291 325.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3062,50 +3110,50 @@ Q Q
Q
q
-1 0 0 1 62.69291 317.4236 cm
+1 0 0 1 62.69291 281.4236 cm
q
BT 1 0 0 1 0 28.82 Tm .92881 Tw 12 TL /F1 10 Tf 0 0 0 rg (The examples here should have made clear that ) Tj /F5 10 Tf (plac is able to figure out the command line arguments) Tj T* 0 Tw .928488 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 my 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 284.4236 cm
+1 0 0 1 62.69291 248.4236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Options and flags) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 194.4236 cm
+1 0 0 1 62.69291 158.4236 cm
q
BT 1 0 0 1 0 76.82 Tm .046098 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.165984 Tw (a hundred\), compared to the number of scripts with positional arguments \(I certainly have written more) Tj T* 0 Tw 1.221163 Tw (than a thousand of them\). Still, this use case is quite common and cannot be neglected. The standard) Tj T* 0 Tw .446098 Tw (library modules \(all of them\) are quite verbose when it comes to specifying the options and frankly I have) Tj T* 0 Tw .732339 Tw (never used them directly. Instead, I have always relied on an old recipe of mine, the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw 1.32784 Tw (which provides a convenient wrapper over ) Tj 0 0 .501961 rg (optionparse) Tj 0 0 0 rg (. Alternatively, in the simplest cases, I have just) Tj T* 0 Tw (performed the parsing by hand, instead of manually building a suitable OptionParser.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 152.4236 cm
+1 0 0 1 62.69291 116.4236 cm
q
BT 1 0 0 1 0 28.82 Tm .476098 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (is inspired to the ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe, in the sense that it delivers the programmer from the burden of) Tj T* 0 Tw .011488 Tw (writing the parser, but is less of a hack: instead of extracting the parser from the docstring of the module, it) Tj T* 0 Tw (extracts it from the signature of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 122.4236 cm
+1 0 0 1 62.69291 86.42362 cm
q
BT 1 0 0 1 0 16.82 Tm .319987 Tw 12 TL /F1 10 Tf 0 0 0 rg (The idea comes from the ) Tj /F5 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 116.4236 cm
+1 0 0 1 62.69291 80.42362 cm
Q
endstream
endobj
-% 'R150': class PDFStream
-150 0 obj
+% 'R153': class PDFStream
+153 0 obj
% page stream
-<< /Length 4749 >>
+<< /Length 4772 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 667.8236 cm
+1 0 0 1 62.69291 655.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3120,11 +3168,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 442.6898 96 re B*
+n -6 -6 442.6898 108 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL (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
+BT 1 0 0 1 0 89.71 Tm /F4 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
@@ -3134,16 +3182,16 @@ q Q
Q
q
-1 0 0 1 62.69291 667.8236 cm
+1 0 0 1 62.69291 655.8236 cm
Q
q
-1 0 0 1 62.69291 601.8236 cm
+1 0 0 1 62.69291 589.8236 cm
q
BT 1 0 0 1 0 52.82 Tm .789983 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see, the argument ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (has been annotated with the tuple ) Tj /F4 10 Tf (\("SQL query", 'option',) Tj T* 0 Tw .593876 Tw ('c'\)) Tj /F1 10 Tf (: the first string is the help string which will appear in the usage message, whereas the second and) Tj T* 0 Tw .144988 Tw (third strings tell ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (that ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (is an option and that it can be abbreviated with the letter ) Tj /F4 10 Tf (c) Tj /F1 10 Tf (. Of course,) Tj T* 0 Tw 1.543735 Tw (it also possible to use the long option format, by prefixing the option with ) Tj /F4 10 Tf (--command=) Tj /F1 10 Tf (. The resulting) Tj T* 0 Tw (usage message is the following:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 460.6236 cm
+1 0 0 1 62.69291 448.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3164,14 +3212,14 @@ Q Q
Q
q
-1 0 0 1 62.69291 440.6236 cm
+1 0 0 1 62.69291 428.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here are two examples of usage:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 359.4236 cm
+1 0 0 1 62.69291 347.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3192,16 +3240,16 @@ Q Q
Q
q
-1 0 0 1 62.69291 327.4236 cm
+1 0 0 1 62.69291 315.4236 cm
q
BT 1 0 0 1 0 16.82 Tm 1.34104 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that if the option is not passed, the variable ) Tj /F4 10 Tf (command ) Tj /F1 10 Tf (will get the value ) Tj /F4 10 Tf (None) Tj /F1 10 Tf (. It is possible to) Tj T* 0 Tw (specify a non-trivial default for an option. Here is an example:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 321.4236 cm
+1 0 0 1 62.69291 309.4236 cm
Q
q
-1 0 0 1 62.6378 227.0167 cm
+1 0 0 1 62.6378 226.6719 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3216,11 +3264,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 462 96 re B*
+n -6 -6 462 84 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL (# example8_) Tj T* T* (def main\(dsn, command: \("SQL query", 'option', 'c'\)='select * from table'\):) Tj T* ( print\('executing %r on %s' % \(command, dsn\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 65.71 Tm /F4 10 Tf 12 TL (# example8_.py) Tj T* (def main\(dsn, command: \("SQL query", 'option', 'c'\)='select * from table'\):) Tj T* ( print\('executing %r on %s' % \(command, dsn\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
Q
Q
Q
@@ -3230,16 +3278,16 @@ q Q
Q
q
-1 0 0 1 62.69291 227.0167 cm
+1 0 0 1 62.69291 226.6719 cm
Q
q
-1 0 0 1 62.69291 209.0167 cm
+1 0 0 1 62.69291 208.6719 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Now if you do not pass the ) Tj /F4 10 Tf (command option) Tj /F1 10 Tf (, the default query will be executed:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 163.8167 cm
+1 0 0 1 62.69291 163.4719 cm
q
q
1 0 0 1 0 0 cm
@@ -3260,14 +3308,14 @@ Q Q
Q
q
-1 0 0 1 62.69291 143.8167 cm
+1 0 0 1 62.69291 143.4719 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Positional argument can be annotated too:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 86.6167 cm
+1 0 0 1 62.69291 86.27188 cm
q
q
1 0 0 1 0 0 cm
@@ -3291,8 +3339,8 @@ Q endstream
endobj
-% 'R151': class PDFStream
-151 0 obj
+% 'R154': class PDFStream
+154 0 obj
% page stream
<< /Length 5617 >>
stream
@@ -3475,10 +3523,10 @@ Q endstream
endobj
-% 'R152': class PDFStream
-152 0 obj
+% 'R155': class PDFStream
+155 0 obj
% page stream
-<< /Length 5596 >>
+<< /Length 5578 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -3628,7 +3676,7 @@ n -6 -6 480 144 re B* Q
q
0 0 0 rg
-BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (import plac) Tj T* T* (@plac.annotations\() Tj T* (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* ( op = getattr\(float, '__%s__' % operator\)) Tj T* ( result = dict\(add=0.0, mul=1.0\)[operator]) Tj T* ( for n in numbers:) Tj T* ( result = op\(result, n\)) Tj T* ET
+BT 1 0 0 1 0 125.71 Tm /F4 10 Tf 12 TL (# example10.py) Tj T* (import plac) Tj T* T* (@plac.annotations\() Tj T* (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* ( op = getattr\(float, '__%s__' % operator\)) Tj T* ( result = dict\(add=0.0, mul=1.0\)[operator]) Tj T* ( for n in numbers:) Tj T* ET
Q
Q
Q
@@ -3641,14 +3689,14 @@ Q endstream
endobj
-% 'R153': class PDFStream
-153 0 obj
+% 'R156': class PDFStream
+156 0 obj
% page stream
-<< /Length 5160 >>
+<< /Length 3966 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 703.8236 cm
+1 0 0 1 62.69291 691.8236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3663,11 +3711,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 442.6898 60 re B*
+n -6 -6 442.6898 72 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F4 10 Tf 12 TL ( print\(result\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 53.71 Tm /F4 10 Tf 12 TL ( result = op\(result, n\)) Tj T* ( print\(result\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( plac.call\(main\)) Tj T* ET
Q
Q
Q
@@ -3677,17 +3725,17 @@ q Q
Q
q
-1 0 0 1 62.69291 703.8236 cm
+1 0 0 1 62.69291 691.8236 cm
Q
q
-1 0 0 1 62.69291 685.8236 cm
+1 0 0 1 62.69291 673.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage for the script:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 544.6236 cm
+1 0 0 1 62.69291 532.6236 cm
q
q
1 0 0 1 0 0 cm
@@ -3708,13 +3756,13 @@ Q Q
Q
q
-1 0 0 1 62.69291 512.6236 cm
+1 0 0 1 62.69291 500.6236 cm
q
BT 1 0 0 1 0 16.82 Tm .15186 Tw 12 TL /F1 10 Tf 0 0 0 rg (Notice that the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function has been automatically added to the usage message. Here) Tj T* 0 Tw (are a couple of examples of use:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 419.2849 cm
+1 0 0 1 62.69291 407.2849 cm
q
q
.87797 0 0 .87797 0 0 cm
@@ -3735,31 +3783,145 @@ Q Q
Q
q
-1 0 0 1 62.69291 386.2849 cm
+1 0 0 1 62.69291 374.2849 cm
+q
+BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A more realistic example) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 332.2849 cm
+q
+BT 1 0 0 1 0 28.82 Tm 1.234488 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a more realistic script using most of the features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to run SQL queries on a database by) Tj T* 0 Tw .930697 Tw (relying on ) Tj 0 0 .501961 rg (SQLAlchemy) Tj 0 0 0 rg (. Notice the usage of the ) Tj /F4 10 Tf (type ) Tj /F1 10 Tf (feature to automagically convert a SQLAlchemy) Tj T* 0 Tw (connection string into a ) Tj 0 0 .501961 rg (SqlSoup ) Tj 0 0 0 rg (object:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 326.2849 cm
+Q
+q
+1 0 0 1 62.69291 76.86614 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 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 442.6898 240 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 221.71 Tm /F4 10 Tf 12 TL (# dbcli.py) Tj T* (import clap) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* T* (@clap.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* ( print\('Working on %s' % db.bind.url\)) Tj T* ( if sqlcmd:) Tj T* ( result = db.bind.execute\(sqlcmd\)) Tj T* ( if header: # print the header) Tj T* ( print\(delimiter.join\(result.keys\(\)\)\)) Tj T* ( for row in result: # print the rows) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+
+endstream
+
+endobj
+% 'R157': class PDFStream
+157 0 obj
+% page stream
+<< /Length 4959 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
+q
+1 0 0 1 62.69291 667.8236 cm
+0 0 0 rg
+BT /F3 10 Tf 12 TL ET
+BT 1 0 0 1 0 2 Tm T* ET
+q
+1 0 0 1 20 0 cm
+q
+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 442.6898 96 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 77.71 Tm /F4 10 Tf 12 TL ( print\(delimiter.join\(map\(str, row\)\)\)) Tj T* T* ( for script in scripts:) Tj T* ( db.bind.execute\(file\(script\).read\(\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( clap.call\(main\)) Tj T* ET
+Q
+Q
+Q
+Q
+Q
+q
+Q
+Q
+q
+1 0 0 1 62.69291 667.8236 cm
+Q
+q
+1 0 0 1 62.69291 649.8236 cm
+q
+0 0 0 rg
+BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
+Q
+Q
+q
+1 0 0 1 62.69291 460.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 180 re B*
+Q
+q
+0 0 0 rg
+BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL ($ python article/dbcli.py -h) Tj T* (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 427.6236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A few notes on the underlying implementation) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 356.2849 cm
+1 0 0 1 62.69291 397.6236 cm
q
BT 1 0 0 1 0 16.82 Tm .094988 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (plac ) Tj 0 0 0 rg (relies on a ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (for all of the heavy lifting work and it is possible to leverage on ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (features) Tj T* 0 Tw (directly or indirectly.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 314.2849 cm
+1 0 0 1 62.69291 355.6236 cm
q
-BT 1 0 0 1 0 28.82 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 11.11997 Tw ('==SUPPRESS==' as help string \(or ) Tj /F4 10 Tf (argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use) Tj T* 0 Tw /F4 10 Tf (argparse.FileType ) Tj /F1 10 Tf (directly.) Tj T* ET
+BT 1 0 0 1 0 28.82 Tm 5.575697 Tw 12 TL /F1 10 Tf 0 0 0 rg (For instance, you can make invisible an argument in the usage message simply by using) Tj T* 0 Tw 4.107752 Tw /F4 10 Tf ('==SUPPRESS==`'` as help string \(or ``argparse.SUPPRESS) Tj /F1 10 Tf (\). Similarly, you can use) Tj T* 0 Tw 0 0 .501961 rg (argparse.FileType ) Tj 0 0 0 rg (directly.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 260.2849 cm
+1 0 0 1 62.69291 301.6236 cm
q
BT 1 0 0 1 0 40.82 Tm 1.639213 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is also possible to pass options to the underlying ) Tj /F4 10 Tf (argparse.ArgumentParser ) Tj /F1 10 Tf (object \(currently it) Tj T* 0 Tw .285529 Tw (accepts the default arguments ) Tj /F4 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (epilog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (add_help) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (argument_default) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.439953 Tw /F4 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F4 10 Tf (formatter_class) Tj /F1 10 Tf (\). It) Tj T* 0 Tw (is enough to set such attributes on the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function. For instance) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 191.0849 cm
+1 0 0 1 62.69291 232.4236 cm
q
q
1 0 0 1 0 0 cm
@@ -3780,23 +3942,13 @@ Q Q
Q
q
-1 0 0 1 62.69291 111.0849 cm
+1 0 0 1 62.69291 152.4236 cm
q
BT 1 0 0 1 0 64.82 Tm 1.256457 Tw 12 TL /F1 10 Tf 0 0 0 rg (disable the recognition of the help flag ) Tj /F4 10 Tf (-h, --help) Tj /F1 10 Tf (. This is not particularly elegant, but I assume the) Tj T* 0 Tw .274751 Tw (typical user of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (will be happy with the default message and would not want to go at this level of detail;) Tj T* 0 Tw .230514 Tw (still it is possible if she wants to. For instance, by setting the ) Tj /F4 10 Tf (description ) Tj /F1 10 Tf (attribute, it is possible to add) Tj T* 0 Tw .04332 Tw (a comment to the usage message \(by default the docstring of the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function is used as description\). It) Tj T* 0 Tw .877765 Tw (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 lines:) Tj T* ET
Q
Q
-
-endstream
-
-endobj
-% 'R154': class PDFStream
-154 0 obj
-% page stream
-<< /Length 5176 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 727.8236 cm
+1 0 0 1 62.69291 107.2236 cm
q
q
1 0 0 1 0 0 cm
@@ -3816,20 +3968,30 @@ Q Q
Q
Q
+
+endstream
+
+endobj
+% 'R158': class PDFStream
+158 0 obj
+% page stream
+<< /Length 4919 >>
+stream
+1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
-1 0 0 1 62.69291 659.8236 cm
+1 0 0 1 62.69291 705.0236 cm
q
BT 1 0 0 1 0 52.82 Tm 3.694269 Tw 12 TL /F1 10 Tf 0 0 0 rg (The recognition of the ) Tj /F4 10 Tf (short_prefix ) Tj /F1 10 Tf (attribute is a ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (extension; there is also a companion) Tj T* 0 Tw 2.348314 Tw /F4 10 Tf (long_prefix ) Tj /F1 10 Tf (attribute with default value of ) Tj /F4 10 Tf (--) Tj /F1 10 Tf (. ) Tj /F4 10 Tf (prefix_chars ) Tj /F1 10 Tf (is an ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (feature. Interested) Tj T* 0 Tw 1.419984 Tw (readers should read the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to understand the meaning of the other options. If) Tj T* 0 Tw .098935 Tw (there is a set of options that you use very often, you may consider writing a decorator adding such options) Tj T* 0 Tw (to the ) Tj /F4 10 Tf (main ) Tj /F1 10 Tf (function for you. For simplicity, ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (does not perform any magic of that kind.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 629.8236 cm
+1 0 0 1 62.69291 675.0236 cm
q
BT 1 0 0 1 0 16.82 Tm 7.709147 Tw 12 TL /F1 10 Tf 0 0 0 rg (It is possible to access directly the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, by invoking the) Tj T* 0 Tw /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (utility function:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 512.6236 cm
+1 0 0 1 62.69291 557.8236 cm
q
q
1 0 0 1 0 0 cm
@@ -3849,35 +4011,35 @@ Q Q
Q
q
-1 0 0 1 62.69291 492.6236 cm
+1 0 0 1 62.69291 537.8236 cm
q
BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F4 10 Tf (plac.parser_from ) Tj /F1 10 Tf (in the unit tests of the module, but regular users should never need to use it.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 459.6236 cm
+1 0 0 1 62.69291 504.8236 cm
q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Custom annotation objects) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 429.6236 cm
+1 0 0 1 62.69291 474.8236 cm
q
BT 1 0 0 1 0 16.82 Tm .578651 Tw 12 TL /F1 10 Tf 0 0 0 rg (Internally ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (uses an ) Tj /F4 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the tuples in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F4 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 399.6236 cm
+1 0 0 1 62.69291 444.8236 cm
q
0 0 0 rg
BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL .083735 Tw (Advanced users can implement their own annotation objects. For instance, here is an example of how you) Tj T* 0 Tw (could implement annotations for positional arguments:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 393.6236 cm
+1 0 0 1 62.69291 438.8236 cm
Q
q
-1 0 0 1 62.69291 284.4236 cm
+1 0 0 1 62.69291 317.6236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3892,11 +4054,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 448.6898 108 re B*
+n -6 -6 448.6898 120 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F4 10 Tf 12 TL (class Positional\(object\):) Tj T* ( def __init__\(self, help='', type=None, choices=None, metavar=None\):) Tj T* ( self.help = help) Tj T* ( self.kind = 'positional') Tj T* ( self.abbrev = None) Tj T* ( self.type = type) Tj T* ( self.choices = choices) Tj T* ( self.metavar = metavar) Tj T* ET
+BT 1 0 0 1 0 101.71 Tm /F4 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
@@ -3906,20 +4068,20 @@ q Q
Q
q
-1 0 0 1 62.69291 284.4236 cm
+1 0 0 1 62.69291 317.6236 cm
Q
q
-1 0 0 1 62.69291 266.4236 cm
+1 0 0 1 62.69291 299.6236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 260.4236 cm
+1 0 0 1 62.69291 293.6236 cm
Q
q
-1 0 0 1 62.69291 103.2236 cm
+1 0 0 1 62.69291 124.4236 cm
0 0 0 rg
BT /F3 10 Tf 12 TL ET
BT 1 0 0 1 0 2 Tm T* ET
@@ -3934,11 +4096,11 @@ q .662745 .662745 .662745 RG
.5 w
.960784 .960784 .862745 rg
-n -6 -6 448.6898 156 re B*
+n -6 -6 448.6898 168 re B*
Q
q
0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F4 10 Tf 12 TL (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* ( n=Positional\("This is a float", float\),) Tj T* ( rest=Positional\("Other arguments"\)\)) Tj T* (def main\(i, n, *rest\):) Tj T* ( print\(i, n, rest\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
+BT 1 0 0 1 0 149.71 Tm /F4 10 Tf 12 TL (# example11.py) Tj T* (import plac) Tj T* (from annotations import Positional) Tj T* T* (@plac.annotations\() Tj T* ( i=Positional\("This is an int", int\),) Tj T* ( n=Positional\("This is a float", float\),) Tj T* ( rest=Positional\("Other arguments"\)\)) Tj T* (def main\(i, n, *rest\):) Tj T* ( print\(i, n, rest\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( import plac; plac.call\(main\)) Tj T* ET
Q
Q
Q
@@ -3948,10 +4110,10 @@ q Q
Q
q
-1 0 0 1 62.69291 103.2236 cm
+1 0 0 1 62.69291 124.4236 cm
Q
q
-1 0 0 1 62.69291 85.22362 cm
+1 0 0 1 62.69291 106.4236 cm
q
0 0 0 rg
BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message you get:) Tj T* ET
@@ -3961,10 +4123,10 @@ Q endstream
endobj
-% 'R155': class PDFStream
-155 0 obj
+% 'R159': class PDFStream
+159 0 obj
% page stream
-<< /Length 8028 >>
+<< /Length 8190 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -4177,125 +4339,11 @@ Q q
1 0 0 1 62.69291 167.8236 cm
q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (A final example) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 137.8236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .196655 Tw 12 TL /F1 10 Tf 0 0 0 rg (Here is a more realistic script using all of the features of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (to run SQL queries on a database by relying) Tj T* 0 Tw (on SQLAlchemy.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 131.8236 cm
-Q
-q
-1 0 0 1 62.69291 76.86614 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-BT 1 0 0 1 0 2 Tm T* ET
-q
-1 0 0 1 20 0 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 442.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F4 10 Tf 12 TL (import clap) Tj T* (from sqlalchemy.ext.sqlsoup import SqlSoup) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-
-endstream
-
-endobj
-% 'R156': class PDFStream
-156 0 obj
-% page stream
-<< /Length 3299 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 475.8236 cm
-0 0 0 rg
-BT /F3 10 Tf 12 TL ET
-BT 1 0 0 1 0 2 Tm T* ET
-q
-1 0 0 1 20 0 cm
-q
-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 442.6898 288 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 269.71 Tm /F4 10 Tf 12 TL T* (@clap.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* ( print\('Working on %s' % db.bind.url\)) Tj T* ( if sqlcmd:) Tj T* ( result = db.bind.execute\(sqlcmd\)) Tj T* ( if header: # print the header) Tj T* ( print\(delimiter.join\(result.keys\(\)\)\)) Tj T* ( for row in result: # print the rows) Tj T* ( print\(delimiter.join\(map\(str, row\)\)\)) Tj T* T* ( for script in scripts:) Tj T* ( db.execute\(file\(script\).read\(\)\)) Tj T* T* (if __name__ == '__main__':) Tj T* ( clap.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 475.8236 cm
-Q
-q
-1 0 0 1 62.69291 457.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 268.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 180 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 161.71 Tm /F4 10 Tf 12 TL ($ python article/dbcli.py -h) Tj T* (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 235.6236 cm
-q
BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The future) Tj T* ET
Q
Q
q
-1 0 0 1 62.69291 157.6236 cm
+1 0 0 1 62.69291 89.82362 cm
q
BT 1 0 0 1 0 64.82 Tm .444431 Tw 12 TL /F1 10 Tf 0 0 0 rg (Currently plac is below 100 lines of code, not counting blanks, comments and docstrings. I do not plan to) Tj T* 0 Tw .035444 Tw (extend it much in the future. The idea is to keep the module short: it is and it should remain a little wrapper) Tj T* 0 Tw 1.903318 Tw (over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually I have thought about contributing the code back to ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (if ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (becomes) Tj T* 0 Tw 4.105697 Tw (successfull and gains a reasonable number of users. For the moment it should be considered) Tj T* 0 Tw .351654 Tw (experimental: after all I wrote it in three days, including the tests, the documentation and the time to learn) Tj T* 0 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (.) Tj T* ET
Q
@@ -4304,10 +4352,10 @@ Q endstream
endobj
-% 'R157': class PDFStream
-157 0 obj
+% 'R160': class PDFStream
+160 0 obj
% page stream
-<< /Length 3429 >>
+<< /Length 3431 >>
stream
1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
q
@@ -4319,7 +4367,7 @@ Q q
1 0 0 1 62.69291 678.0236 cm
q
-BT 1 0 0 1 0 52.82 Tm .942651 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (project started very humble: I just wanted to make easy_installable my old ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw .565988 Tw (and to publish it on PyPI. The original name of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was optionparser and the idea behind it was to build) Tj T* 0 Tw .603735 Tw (an ) Tj 0 0 .501961 rg (OptionParser ) Tj 0 0 0 rg (object from the docstring of the module. However, before doing that, I decided to check) Tj T* 0 Tw .507356 Tw (out the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module, since I new it was going into Python 2.7 and Python 2.7 was coming out. Soon) Tj T* 0 Tw (enough I realized two things:) Tj T* ET
+BT 1 0 0 1 0 52.82 Tm .942651 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (project started very humble: I just wanted to make easy_installable my old ) Tj 0 0 .501961 rg (optionparse ) Tj 0 0 0 rg (recipe,) Tj T* 0 Tw .565988 Tw (and to publish it on PyPI. The original name of ) Tj 0 0 .501961 rg (plac ) Tj 0 0 0 rg (was optionparser and the idea behind it was to build) Tj T* 0 Tw .603735 Tw (an ) Tj 0 0 .501961 rg (OptionParser ) Tj 0 0 0 rg (object from the docstring of the module. However, before doing that, I decided to check) Tj T* 0 Tw .244198 Tw (out the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (module, since I knew it was going into Python 2.7 and Python 2.7 was coming out. Soon) Tj T* 0 Tw (enough I realized two things:) Tj T* ET
Q
Q
q
@@ -4390,122 +4438,122 @@ Q q
1 0 0 1 62.69291 528.0236 cm
q
-BT 1 0 0 1 0 16.82 Tm 1.093876 Tw 12 TL /F1 10 Tf 0 0 0 rg (I made a research on PyPI and the name clap \(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 (expletive deleted) Tj (>) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm 1.093876 Tw 12 TL /F1 10 Tf 0 0 0 rg (I made a research on PyPI and the name clap \(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 T* ET
Q
Q
q
1 0 0 1 62.69291 498.0236 cm
q
0 0 0 rg
-BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.123145 Tw (Having little fantasy, I decided to rename everything again to plac, as an anagram of clap: since it is a) Tj T* 0 Tw (non-existing English name, I hope nobody will stole it from me!) Tj T* ET
+BT 1 0 0 1 0 16.82 Tm /F1 10 Tf 12 TL 1.123145 Tw (Having little fantasy, I decided to rename everything again to plac, as 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
endstream
endobj
-% 'R158': class PDFPageLabels
-158 0 obj
+% 'R161': class PDFPageLabels
+161 0 obj
% Document Root
<< /Nums [ 0
- 159 0 R
+ 162 0 R
1
- 160 0 R
+ 163 0 R
2
- 161 0 R
+ 164 0 R
3
- 162 0 R
+ 165 0 R
4
- 163 0 R
+ 166 0 R
5
- 164 0 R
+ 167 0 R
6
- 165 0 R
+ 168 0 R
7
- 166 0 R
+ 169 0 R
8
- 167 0 R
+ 170 0 R
9
- 168 0 R
+ 171 0 R
10
- 169 0 R
+ 172 0 R
11
- 170 0 R ] >>
+ 173 0 R ] >>
endobj
-% 'R159': class PDFPageLabel
-159 0 obj
+% 'R162': class PDFPageLabel
+162 0 obj
% None
<< /S /D
/St 1 >>
endobj
-% 'R160': class PDFPageLabel
-160 0 obj
+% 'R163': class PDFPageLabel
+163 0 obj
% None
<< /S /D
/St 2 >>
endobj
-% 'R161': class PDFPageLabel
-161 0 obj
+% 'R164': class PDFPageLabel
+164 0 obj
% None
<< /S /D
/St 3 >>
endobj
-% 'R162': class PDFPageLabel
-162 0 obj
+% 'R165': class PDFPageLabel
+165 0 obj
% None
<< /S /D
/St 4 >>
endobj
-% 'R163': class PDFPageLabel
-163 0 obj
+% 'R166': class PDFPageLabel
+166 0 obj
% None
<< /S /D
/St 5 >>
endobj
-% 'R164': class PDFPageLabel
-164 0 obj
+% 'R167': class PDFPageLabel
+167 0 obj
% None
<< /S /D
/St 6 >>
endobj
-% 'R165': class PDFPageLabel
-165 0 obj
+% 'R168': class PDFPageLabel
+168 0 obj
% None
<< /S /D
/St 7 >>
endobj
-% 'R166': class PDFPageLabel
-166 0 obj
+% 'R169': class PDFPageLabel
+169 0 obj
% None
<< /S /D
/St 8 >>
endobj
-% 'R167': class PDFPageLabel
-167 0 obj
+% 'R170': class PDFPageLabel
+170 0 obj
% None
<< /S /D
/St 9 >>
endobj
-% 'R168': class PDFPageLabel
-168 0 obj
+% 'R171': class PDFPageLabel
+171 0 obj
% None
<< /S /D
/St 10 >>
endobj
-% 'R169': class PDFPageLabel
-169 0 obj
+% 'R172': class PDFPageLabel
+172 0 obj
% None
<< /S /D
/St 11 >>
endobj
-% 'R170': class PDFPageLabel
-170 0 obj
+% 'R173': class PDFPageLabel
+173 0 obj
% None
<< /S /D
/St 12 >>
endobj
xref
-0 171
+0 174
0000000000 65535 f
0000000113 00000 n
0000000258 00000 n
@@ -4531,160 +4579,163 @@ xref 0000004837 00000 n
0000005080 00000 n
0000005323 00000 n
-0000005567 00000 n
-0000005811 00000 n
-0000006055 00000 n
-0000006299 00000 n
-0000006543 00000 n
-0000006787 00000 n
-0000007031 00000 n
-0000007274 00000 n
-0000007536 00000 n
-0000007800 00000 n
-0000008050 00000 n
-0000008300 00000 n
-0000008552 00000 n
-0000008804 00000 n
-0000009056 00000 n
-0000009306 00000 n
-0000009543 00000 n
-0000010163 00000 n
-0000010429 00000 n
-0000010680 00000 n
-0000010917 00000 n
-0000011112 00000 n
-0000011374 00000 n
-0000011638 00000 n
-0000011873 00000 n
-0000012236 00000 n
-0000012488 00000 n
-0000012740 00000 n
-0000012991 00000 n
-0000013241 00000 n
-0000013493 00000 n
-0000013730 00000 n
-0000014093 00000 n
-0000014345 00000 n
-0000014597 00000 n
-0000014849 00000 n
-0000015137 00000 n
-0000015421 00000 n
-0000015673 00000 n
-0000015946 00000 n
-0000016318 00000 n
-0000016555 00000 n
-0000016873 00000 n
-0000017124 00000 n
-0000017376 00000 n
-0000017628 00000 n
-0000017865 00000 n
-0000018210 00000 n
-0000018462 00000 n
-0000018712 00000 n
-0000018962 00000 n
-0000019212 00000 n
-0000019464 00000 n
-0000019699 00000 n
-0000020062 00000 n
-0000020313 00000 n
-0000020563 00000 n
-0000020813 00000 n
-0000021050 00000 n
-0000021395 00000 n
-0000021647 00000 n
-0000021896 00000 n
-0000022146 00000 n
-0000022398 00000 n
-0000022685 00000 n
-0000022922 00000 n
-0000023285 00000 n
-0000023536 00000 n
-0000023786 00000 n
-0000024038 00000 n
-0000024302 00000 n
-0000024552 00000 n
-0000024816 00000 n
-0000025068 00000 n
-0000025318 00000 n
-0000025570 00000 n
-0000025821 00000 n
-0000026074 00000 n
-0000026325 00000 n
-0000026577 00000 n
-0000026830 00000 n
-0000027083 00000 n
-0000027336 00000 n
-0000027589 00000 n
-0000027840 00000 n
-0000028092 00000 n
-0000028380 00000 n
-0000028631 00000 n
-0000028870 00000 n
-0000029391 00000 n
-0000029642 00000 n
-0000029893 00000 n
-0000030147 00000 n
-0000030384 00000 n
-0000030735 00000 n
-0000030989 00000 n
-0000031276 00000 n
-0000031530 00000 n
-0000031841 00000 n
-0000032092 00000 n
-0000032344 00000 n
-0000032633 00000 n
-0000032885 00000 n
-0000033137 00000 n
-0000033380 00000 n
-0000033777 00000 n
-0000033941 00000 n
-0000034232 00000 n
-0000034361 00000 n
-0000034537 00000 n
-0000034747 00000 n
-0000034955 00000 n
-0000035152 00000 n
-0000035357 00000 n
-0000035550 00000 n
-0000035774 00000 n
-0000035979 00000 n
-0000036176 00000 n
-0000036373 00000 n
-0000036565 00000 n
-0000036748 00000 n
-0000036961 00000 n
-0000045452 00000 n
-0000050988 00000 n
-0000055420 00000 n
-0000060390 00000 n
-0000065242 00000 n
-0000070962 00000 n
-0000076661 00000 n
-0000081924 00000 n
-0000087203 00000 n
-0000095334 00000 n
-0000098736 00000 n
-0000102272 00000 n
-0000102524 00000 n
-0000102603 00000 n
-0000102682 00000 n
-0000102761 00000 n
-0000102840 00000 n
-0000102919 00000 n
-0000102998 00000 n
-0000103077 00000 n
-0000103156 00000 n
-0000103235 00000 n
-0000103315 00000 n
-0000103395 00000 n
+0000005566 00000 n
+0000005809 00000 n
+0000006053 00000 n
+0000006297 00000 n
+0000006541 00000 n
+0000006785 00000 n
+0000007029 00000 n
+0000007272 00000 n
+0000007534 00000 n
+0000007798 00000 n
+0000008048 00000 n
+0000008298 00000 n
+0000008550 00000 n
+0000008802 00000 n
+0000009054 00000 n
+0000009304 00000 n
+0000009541 00000 n
+0000010161 00000 n
+0000010427 00000 n
+0000010678 00000 n
+0000010915 00000 n
+0000011110 00000 n
+0000011372 00000 n
+0000011636 00000 n
+0000011871 00000 n
+0000012234 00000 n
+0000012486 00000 n
+0000012738 00000 n
+0000012989 00000 n
+0000013239 00000 n
+0000013491 00000 n
+0000013728 00000 n
+0000014091 00000 n
+0000014343 00000 n
+0000014595 00000 n
+0000014847 00000 n
+0000015135 00000 n
+0000015419 00000 n
+0000015671 00000 n
+0000015944 00000 n
+0000016316 00000 n
+0000016553 00000 n
+0000016871 00000 n
+0000017122 00000 n
+0000017374 00000 n
+0000017626 00000 n
+0000017863 00000 n
+0000018208 00000 n
+0000018460 00000 n
+0000018710 00000 n
+0000018960 00000 n
+0000019210 00000 n
+0000019462 00000 n
+0000019697 00000 n
+0000020060 00000 n
+0000020312 00000 n
+0000020558 00000 n
+0000020820 00000 n
+0000021156 00000 n
+0000021407 00000 n
+0000021657 00000 n
+0000021907 00000 n
+0000022223 00000 n
+0000022460 00000 n
+0000022814 00000 n
+0000023066 00000 n
+0000023315 00000 n
+0000023565 00000 n
+0000023817 00000 n
+0000024104 00000 n
+0000024342 00000 n
+0000024705 00000 n
+0000024956 00000 n
+0000025206 00000 n
+0000025458 00000 n
+0000025722 00000 n
+0000025973 00000 n
+0000026238 00000 n
+0000026491 00000 n
+0000026742 00000 n
+0000026995 00000 n
+0000027246 00000 n
+0000027499 00000 n
+0000027750 00000 n
+0000028002 00000 n
+0000028255 00000 n
+0000028508 00000 n
+0000028761 00000 n
+0000029014 00000 n
+0000029265 00000 n
+0000029517 00000 n
+0000029806 00000 n
+0000030058 00000 n
+0000030310 00000 n
+0000030561 00000 n
+0000030815 00000 n
+0000031052 00000 n
+0000031609 00000 n
+0000031863 00000 n
+0000032150 00000 n
+0000032404 00000 n
+0000032715 00000 n
+0000032967 00000 n
+0000033219 00000 n
+0000033508 00000 n
+0000033760 00000 n
+0000034012 00000 n
+0000034255 00000 n
+0000034652 00000 n
+0000034816 00000 n
+0000035107 00000 n
+0000035236 00000 n
+0000035412 00000 n
+0000035622 00000 n
+0000035830 00000 n
+0000036027 00000 n
+0000036232 00000 n
+0000036425 00000 n
+0000036629 00000 n
+0000036853 00000 n
+0000037058 00000 n
+0000037256 00000 n
+0000037448 00000 n
+0000037631 00000 n
+0000037843 00000 n
+0000046350 00000 n
+0000051930 00000 n
+0000056376 00000 n
+0000061365 00000 n
+0000066240 00000 n
+0000071960 00000 n
+0000077641 00000 n
+0000081710 00000 n
+0000086772 00000 n
+0000091794 00000 n
+0000100087 00000 n
+0000103625 00000 n
+0000103877 00000 n
+0000103956 00000 n
+0000104035 00000 n
+0000104114 00000 n
+0000104193 00000 n
+0000104272 00000 n
+0000104351 00000 n
+0000104430 00000 n
+0000104509 00000 n
+0000104588 00000 n
+0000104668 00000 n
+0000104748 00000 n
trailer
<< /ID
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\277\245\225\024\025\215\375JE\232\372/B\334iX) (\277\245\225\024\025\215\375JE\232\372/B\334iX)]
+ [(\235G:\345\242\327\277\256\202.\210\323e\200\340K) (\235G:\345\242\327\277\256\202.\210\323e\200\340K)]
- /Info 131 0 R
- /Root 130 0 R
- /Size 171 >>
+ /Info 134 0 R
+ /Root 133 0 R
+ /Size 174 >>
startxref
-103444
+104797
%%EOF
diff --git a/plac/doc/article.txt b/plac/doc/plac.txt index 7acd5c8..3432f1d 100644 --- a/plac/doc/article.txt +++ b/plac/doc/plac.txt @@ -13,7 +13,7 @@ Plac, the Easiest Command Line Arguments Parser in the World Introduction ------------------------------------------------ -There is no want of command line arguments parsers in Python +There is no want of command line arguments parsers in the Python world. The standard library alone contains three different modules for the parsing of command line options: getopt_ (from the stone age), optparse_ (from Python 2.3) and argparse_ (from Python 2.7). All of @@ -143,7 +143,7 @@ the usage message for free:: 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: +on a database a series of SQL scripts: .. include:: example6.py :literal: @@ -394,15 +394,44 @@ to the usage message. Here are a couple of examples of use:: usage: example10.py [-h] {add,mul} [n [n ...]] example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul') +A more 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: + +Here is the usage message:: + + $ python article/dbcli.py -h + 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 + A few notes on the underlying implementation ---------------------------------------------------- plac_ relies on a argparse_ for all of the heavy lifting work and it is possible to leverage on argparse_ features directly or indirectly. -For instance, 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. +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 @@ -433,13 +462,13 @@ for instance if your script must run under Windows and you want to use main.short_prefix = '/' The recognition of the ``short_prefix`` attribute is a plac_ -extension; there is also a companion ``long_prefix`` -attribute with default value of ``--``. ``prefix_chars`` is an argparse_ feature. -Interested readers should read the documentation of argparse_ to understand the -meaning of the other options. If there is a set of options that you -use very often, you may consider writing a decorator adding such -options to the ``main`` function for you. For simplicity, plac_ does -not perform any magic of that kind. +extension; there is also a companion ``long_prefix`` attribute with +default value of ``--``. ``prefix_chars`` is an argparse_ feature. +Interested readers should read the documentation of argparse_ to +understand the meaning of the other options. If there is a set of +options that you use very often, you may consider writing a decorator +adding such options to the ``main`` function for you. For simplicity, +plac_ does not perform any magic of that kind. It is possible to access directly the underlying ArgumentParser_ object, by invoking the ``plac.parser_from`` utility function: @@ -539,32 +568,6 @@ is then available to you: you can use ``add_argument``, ``add_subparsers()``, etc. In other words, while some features are not supported directly, *all* features are supported indirectly. -A final example ---------------------------------------- - -Here is a more realistic script using all of the features of plac_ to run SQL queries -on a database by relying on SQLAlchemy. - - .. include:: dbcli.py - :literal: - -Here is the usage message:: - - $ python article/dbcli.py -h - 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 - The future ------------------------------- @@ -585,7 +588,7 @@ 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 new it was going into Python 2.7 and Python 2.7 was coming out. +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 @@ -601,11 +604,11 @@ 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! <expletive deleted> +a Clap_ module appeared on PyPI! <expletives deleted> Having little fantasy, I decided to rename everything again to plac, as an anagram of clap: since it is a non-existing English name, I hope nobody -will stole it from me! +will steal it from me! .. _argparse: http://argparse.googlecode.com .. _optparse: http://docs.python.org/library/optparse.html @@ -614,5 +617,8 @@ will stole it from me! .. _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 diff --git a/plac/plac.py b/plac/plac.py index fdde35f..ef0bf75 100644 --- a/plac/plac.py +++ b/plac/plac.py @@ -29,7 +29,7 @@ See plac/doc.html for the documentation. """ # this module should be kept Python 2.3 compatible -__version__ = '0.2.0' +__version__ = '0.3.0' import re, sys, inspect, argparse @@ -73,9 +73,10 @@ def is_annotation(obj): class Annotation(object): def __init__(self, help="", kind="positional", abbrev=None, type=str, choices=None, metavar=None): + assert kind in ('positional', 'option', 'flag'), kind if kind == "positional": assert abbrev is None, abbrev - else: + else: # option, flag assert isinstance(abbrev, str) and len(abbrev) == 1, abbrev self.help = help self.kind = kind |