diff options
author | Michele Simionato <michele.simionato@gmail.com> | 2010-06-01 13:22:00 +0200 |
---|---|---|
committer | Michele Simionato <michele.simionato@gmail.com> | 2010-06-01 13:22:00 +0200 |
commit | 7ee9559aa9313824ea327111b7287b716172c286 (patch) | |
tree | def050dfc4dfc55848be93dcd5725b50a4fb85b3 | |
parent | 97dcd8ebb064319da3060d9795d4943b88870cd3 (diff) | |
download | micheles-7ee9559aa9313824ea327111b7287b716172c286.tar.gz |
Removed the clap project remnants
-rw-r--r-- | clap/article/annotations.py | 8 | ||||
-rw-r--r-- | clap/article/article.html | 410 | ||||
-rw-r--r-- | clap/article/article.pdf | 2418 | ||||
-rw-r--r-- | clap/article/article.txt | 442 | ||||
l--------- | clap/article/clap.py | 1 | ||||
-rw-r--r-- | clap/article/example1.py | 13 | ||||
-rw-r--r-- | clap/article/example10.py | 14 | ||||
-rw-r--r-- | clap/article/example11.py | 12 | ||||
-rw-r--r-- | clap/article/example2.py | 10 | ||||
-rw-r--r-- | clap/article/example3.py | 6 | ||||
-rw-r--r-- | clap/article/example4.py | 14 | ||||
-rw-r--r-- | clap/article/example4_.py | 8 | ||||
-rw-r--r-- | clap/article/example5.py | 8 | ||||
-rw-r--r-- | clap/article/example6.py | 12 | ||||
-rw-r--r-- | clap/article/example7.py | 9 | ||||
-rw-r--r-- | clap/article/example8.py | 7 | ||||
-rw-r--r-- | clap/article/example9.py | 7 | ||||
-rw-r--r-- | clap/doc.html | 187 | ||||
-rw-r--r-- | clap/doc.txt | 82 | ||||
-rw-r--r-- | clap/example.py | 20 | ||||
-rw-r--r-- | clap/example.txt | 31 | ||||
-rw-r--r-- | plac/README.txt (renamed from clap/README.txt) | 20 | ||||
-rw-r--r-- | plac/plac.py (renamed from clap/clap.py) | 46 | ||||
-rw-r--r-- | plac/setup.py | 29 | ||||
-rw-r--r-- | plac/test_plac.py (renamed from clap/test_clap.py) | 55 |
25 files changed, 98 insertions, 3771 deletions
diff --git a/clap/article/annotations.py b/clap/article/annotations.py deleted file mode 100644 index c84cc04..0000000 --- a/clap/article/annotations.py +++ /dev/null @@ -1,8 +0,0 @@ -class Positional(object): - def __init__(self, help='', type=None, choices=None, metavar=None): - self.help = help - self.kind = 'positional' - self.abbrev = None - self.type = type - self.choices = choices - self.metavar = metavar diff --git a/clap/article/article.html b/clap/article/article.html deleted file mode 100644 index 65d0c68..0000000 --- a/clap/article/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">clap</a>.</p> -<p><a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</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">clap</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">clap</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">clap</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">clap</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">clap</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">clap</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 clap; clap.call(main) - -</pre> -</blockquote> -<p>As an additional bonus she <a class="reference external" href="http://www.welton.it/articles/scalable_systems">clap</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">clap</a>, you can just replace the <tt class="docutils literal">__main__</tt> block with the usual -<tt class="docutils literal">import clap; clap.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">clap</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 clap; clap.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">clap</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">clap</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">clap</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="clap-is-also-for-people-not-using-python-3"> -<h1>clap 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">clap</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">clap.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">clap.annotations</tt> decorator.</p> -</div> -<div class="section" id="advanced-usage"> -<h1>Advanced usage</h1> -<p>One of the goals of clap 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">clap</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 clap - -@clap.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__': - clap.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/clap/article/article.pdf b/clap/article/article.pdf deleted file mode 100644 index cb3f3dc..0000000 --- a/clap/article/article.pdf +++ /dev/null @@ -1,2418 +0,0 @@ -%PDF-1.3
-%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
-% 'BasicFonts': class PDFDictionary
-1 0 obj
-% The standard fonts dictionary
-<< /F1 2 0 R
- /F2 3 0 R
- /F3 14 0 R
- /F4 16 0 R
- /F5 17 0 R >>
-endobj
-% 'F1': class PDFType1Font
-2 0 obj
-% Font Helvetica
-<< /BaseFont /Helvetica
- /Encoding /WinAnsiEncoding
- /Name /F1
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'F2': class PDFType1Font
-3 0 obj
-% Font Helvetica-Bold
-<< /BaseFont /Helvetica-Bold
- /Encoding /WinAnsiEncoding
- /Name /F2
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Annot.NUMBER1': class PDFDictionary
-4 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/getopt.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 361.1228
- 686.5936
- 392.9767
- 698.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER2': class PDFDictionary
-5 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 493.6727
- 686.5936
- 531.3087
- 698.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER3': class PDFDictionary
-6 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 164.0504
- 674.5936
- 206.7572
- 686.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER4': class PDFDictionary
-7 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 493.1227
- 674.5936
- 532.1158
- 686.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER5': class PDFDictionary
-8 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 95.71512
- 632.5936
- 114.0551
- 644.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER6': class PDFDictionary
-9 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 120.9573
- 632.5936
- 143.4195
- 644.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER7': class PDFDictionary
-10 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 227.1684
- 632.5936
- 320.7606
- 644.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER8': class PDFDictionary
-11 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 337.6822
- 620.5936
- 359.0965
- 632.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER9': class PDFDictionary
-12 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 490.3427
- 620.5936
- 529.8027
- 632.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER10': class PDFDictionary
-13 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 346.3284
- 608.5936
- 367.4652
- 620.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'F3': class PDFType1Font
-14 0 obj
-% Font Courier
-<< /BaseFont /Courier
- /Encoding /WinAnsiEncoding
- /Name /F3
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Annot.NUMBER11': class PDFDictionary
-15 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 471.0489
- 407.5936
- 529.8027
- 419.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'F4': class PDFType1Font
-16 0 obj
-% Font Helvetica-Oblique
-<< /BaseFont /Helvetica-Oblique
- /Encoding /WinAnsiEncoding
- /Name /F4
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'F5': class PDFType1Font
-17 0 obj
-% Font Times-Roman
-<< /BaseFont /Times-Roman
- /Encoding /WinAnsiEncoding
- /Name /F5
- /Subtype /Type1
- /Type /Font >>
-endobj
-% 'Page1': class PDFPage
-18 0 obj
-% Page dictionary
-<< /Annots [ 4 0 R
- 5 0 R
- 6 0 R
- 7 0 R
- 8 0 R
- 9 0 R
- 10 0 R
- 11 0 R
- 12 0 R
- 13 0 R
- 15 0 R ]
- /Contents 69 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER12': class PDFDictionary
-19 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/getopt.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 504.7827
- 756.5936
- 532.1006
- 768.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER13': class PDFDictionary
-20 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://docs.python.org/library/optparse.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 82.40665
- 744.5936
- 124.3504
- 756.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER14': class PDFDictionary
-21 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 732.5936
- 104.9329
- 744.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER15': class PDFDictionary
-22 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 83.82606
- 533.3936
- 106.0692
- 545.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER16': class PDFDictionary
-23 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 243.8829
- 521.3936
- 265.0029
- 533.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER17': class PDFDictionary
-24 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 83.6329
- 412.1936
- 105.6829
- 424.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER18': class PDFDictionary
-25 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 421.9727
- 412.1936
- 465.1427
- 424.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER19': class PDFDictionary
-26 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 211.6529
- 262.9936
- 232.7729
- 274.9936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page2': class PDFPage
-27 0 obj
-% Page dictionary
-<< /Annots [ 19 0 R
- 20 0 R
- 21 0 R
- 22 0 R
- 23 0 R
- 24 0 R
- 25 0 R
- 26 0 R ]
- /Contents 70 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER20': class PDFDictionary
-28 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 85.47291
- 665.3936
- 106.5929
- 677.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER21': class PDFDictionary
-29 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 450.9936
- 84.20915
- 462.9936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER22': class PDFDictionary
-30 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 97.95597
- 257.7936
- 116.296
- 269.7936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER23': class PDFDictionary
-31 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 446.6187
- 96.59362
- 464.9587
- 108.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page3': class PDFPage
-32 0 obj
-% Page dictionary
-<< /Annots [ 28 0 R
- 29 0 R
- 30 0 R
- 31 0 R ]
- /Contents 71 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER24': class PDFDictionary
-33 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 446.8103
- 681.5936
- 502.5727
- 693.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER25': class PDFDictionary
-34 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 260.18
- 669.5936
- 312.43
- 681.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER26': class PDFDictionary
-35 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 639.5936
- 84.28901
- 651.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER27': class PDFDictionary
-36 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://code.activestate.com/recipes/278844-parsing-the-command-line/) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 161.7834
- 639.5936
- 217.2895
- 651.5936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER28': class PDFDictionary
-37 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 133.1479
- 440.3936
- 154.4129
- 452.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page4': class PDFPage
-38 0 obj
-% Page dictionary
-<< /Annots [ 33 0 R
- 34 0 R
- 35 0 R
- 36 0 R
- 37 0 R ]
- /Contents 72 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER29': class PDFDictionary
-39 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 185.8554
- 711.3936
- 207.616
- 723.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER30': class PDFDictionary
-40 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 162.7329
- 556.9936
- 181.0729
- 568.9936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER31': class PDFDictionary
-41 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 479.7936
- 84.57878
- 491.7936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER32': class PDFDictionary
-42 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 185.1229
- 150.3936
- 208.3329
- 162.3936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page5': class PDFPage
-43 0 obj
-% Page dictionary
-<< /Annots [ 39 0 R
- 40 0 R
- 41 0 R
- 42 0 R ]
- /Contents 73 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER33': class PDFDictionary
-44 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 455.1936
- 102.1529
- 467.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER34': class PDFDictionary
-45 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 418.9683
- 455.1936
- 458.4283
- 467.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER35': class PDFDictionary
-46 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 443.1936
- 106.4622
- 455.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER36': class PDFDictionary
-47 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 188.85
- 443.1936
- 207.19
- 455.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER37': class PDFDictionary
-48 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 407.8229
- 263.1936
- 450.0629
- 275.1936 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page6': class PDFPage
-49 0 obj
-% Page dictionary
-<< /Annots [ 44 0 R
- 45 0 R
- 46 0 R
- 47 0 R
- 48 0 R ]
- /Contents 74 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Annot.NUMBER38': class PDFDictionary
-50 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 475.0549
- 84.7414
- 487.0549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER39': class PDFDictionary
-51 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 136.4369
- 475.0549
- 179.6054
- 487.0549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER40': class PDFDictionary
-52 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 463.0549
- 138.3111
- 475.0549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER41': class PDFDictionary
-53 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 127.9872
- 325.8549
- 149.3819
- 337.8549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER42': class PDFDictionary
-54 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 480.5388
- 313.8549
- 524.2427
- 325.8549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER43': class PDFDictionary
-55 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 225.5228
- 283.8549
- 301.2571
- 295.8549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Annot.NUMBER44': class PDFDictionary
-56 0 obj
-<< /A << /S /URI
- /Type /Action
- /URI (http://www.welton.it/articles/scalable_systems) >>
- /Border [ 0
- 0
- 0 ]
- /Rect [ 62.69291
- 116.6549
- 84.35423
- 128.6549 ]
- /Subtype /Link
- /Type /Annot >>
-endobj
-% 'Page7': class PDFPage
-57 0 obj
-% Page dictionary
-<< /Annots [ 50 0 R
- 51 0 R
- 52 0 R
- 53 0 R
- 54 0 R
- 55 0 R
- 56 0 R ]
- /Contents 75 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'Page8': class PDFPage
-58 0 obj
-% Page dictionary
-<< /Contents 76 0 R
- /MediaBox [ 0
- 0
- 595.2756
- 841.8898 ]
- /Parent 68 0 R
- /Resources << /Font 1 0 R
- /ProcSet [ /PDF
- /Text
- /ImageB
- /ImageC
- /ImageI ] >>
- /Rotate 0
- /Trans << >>
- /Type /Page >>
-endobj
-% 'R59': class PDFCatalog
-59 0 obj
-% Document Root
-<< /Outlines 61 0 R
- /PageLabels 77 0 R
- /PageMode /UseNone
- /Pages 68 0 R
- /Type /Catalog >>
-endobj
-% 'R60': class PDFInfo
-60 0 obj
-<< /Author ()
- /CreationDate (D:20100531105058-01'00')
- /Keywords ()
- /Producer (ReportLab http://www.reportlab.com)
- /Subject (\(unspecified\))
- /Title (The Easiest Command Line Arguments Parser in the World) >>
-endobj
-% 'R61': class PDFOutlines
-61 0 obj
-<< /Count 6
- /First 62 0 R
- /Last 67 0 R
- /Type /Outlines >>
-endobj
-% 'Outline.0': class OutlineEntryObject
-62 0 obj
-<< /Dest [ 18 0 R
- /XYZ
- 62.69291
- 443.0236
- 0 ]
- /Next 63 0 R
- /Parent 61 0 R
- /Title (The importance of scaling down) >>
-endobj
-% 'Outline.1': class OutlineEntryObject
-63 0 obj
-<< /Dest [ 27 0 R
- /XYZ
- 62.69291
- 247.4236
- 0 ]
- /Next 64 0 R
- /Parent 61 0 R
- /Prev 62 0 R
- /Title (Positional default arguments) >>
-endobj
-% 'Outline.2': class OutlineEntryObject
-64 0 obj
-<< /Dest [ 38 0 R
- /XYZ
- 62.69291
- 765.0236
- 0 ]
- /Next 65 0 R
- /Parent 61 0 R
- /Prev 63 0 R
- /Title (Options and flags) >>
-endobj
-% 'Outline.3': class OutlineEntryObject
-65 0 obj
-<< /Dest [ 43 0 R
- /XYZ
- 62.69291
- 209.8236
- 0 ]
- /Next 66 0 R
- /Parent 61 0 R
- /Prev 64 0 R
- /Title (clap for people not using Python 3) >>
-endobj
-% 'Outline.4': class OutlineEntryObject
-66 0 obj
-<< /Dest [ 49 0 R
- /XYZ
- 62.69291
- 502.6236
- 0 ]
- /Next 67 0 R
- /Parent 61 0 R
- /Prev 65 0 R
- /Title (Advanced usage) >>
-endobj
-% 'Outline.5': class OutlineEntryObject
-67 0 obj
-<< /Dest [ 57 0 R
- /XYZ
- 62.69291
- 510.4849
- 0 ]
- /Parent 61 0 R
- /Prev 66 0 R
- /Title (A few notes on the underlying implementation) >>
-endobj
-% 'R68': class PDFPages
-68 0 obj
-% page tree
-<< /Count 8
- /Kids [ 18 0 R
- 27 0 R
- 32 0 R
- 38 0 R
- 43 0 R
- 49 0 R
- 57 0 R
- 58 0 R ]
- /Type /Pages >>
-endobj
-% 'R69': class PDFStream
-69 0 obj
-% page stream
-<< /Length 6154 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 717.0236 cm
-q
-BT 1 0 0 1 0 33.64 Tm 3.214882 0 Td 24 TL /F2 20 Tf 0 0 0 rg (The Easiest Command Line Arguments Parser in) Tj T* 185.62 0 Td (the World) Tj T* -188.8349 0 Td ET
-Q
-Q
-q
-1 0 0 1 62.69291 647.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 .847485 Tw 0 0 0 rg (is an industrial strength solution; unfortunately, all of them have a non-zero learning curve and a certain) Tj T* 0 Tw (verbosity.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 557.0236 cm
-q
-BT 1 0 0 1 0 76.82 Tm 1.342209 Tw 12 TL /F1 10 Tf 0 0 0 rg (Enters ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (. ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is designed to be ) Tj 0 0 .501961 rg (downwardly scalable) Tj 0 0 0 rg (, i.e. to be trivially simple to use for trivial use) Tj T* 0 Tw .29436 Tw (cases, and to have a next-to-zero learning curve. Technically ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is just a simple wrapper over ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (,) Tj T* 0 Tw .01686 Tw (hiding most of the complexity while retaining most of the power. ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is surprisingly scalable upwards even) Tj T* 0 Tw .569398 Tw (for non-trivial use cases, but it is not intended to be an industrial strength command line parsing module.) Tj T* 0 Tw .07104 Tw (Its capabilities are limited by design. If you need more power, by all means use the parsing modules in the) Tj T* 0 Tw .51237 Tw (standard library. Still, I have been using Python for 8 years and never once I had to use the full power of) Tj T* 0 Tw (the standard library modules.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 455.0236 cm
-q
-BT 1 0 0 1 0 88.82 Tm .536457 Tw 12 TL /F1 10 Tf 0 0 0 rg (Actually I am pretty much convinced that features provided by ) Tj /F3 10 Tf (clap ) Tj /F1 10 Tf (are more than enough for 99.9% of) Tj T* 0 Tw .60811 Tw (the typical use cases of a scripter working in a Unix-like environment. I am targetting here programmers,) Tj T* 0 Tw .337126 Tw (sys-admins, scientists and in general people writing throw-away scripts for themselves, choosing to use a) Tj T* 0 Tw .242339 Tw (command line interface because it is the quick and simple. Such users are not interested in features, they) Tj T* 0 Tw 2.177882 Tw (just want to be able to write a simple command line tool from a simple specification, not to build a) Tj T* 0 Tw .014985 Tw (command line parser by hand. Unfortunately, the current modules in the standard library forces them to go) Tj T* 0 Tw 4.664269 Tw (the hard way. They are designed to implement power user tools for programmers or system) Tj T* 0 Tw (administrators, and they have a non-trivial learning curve.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 422.0236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (The importance of scaling down) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 320.0236 cm
-q
-BT 1 0 0 1 0 88.82 Tm .953735 Tw 12 TL /F1 10 Tf 0 0 0 rg (An ex-coworker of mine, David Welton, once wrote a nice article about the importance of ) Tj 0 0 .501961 rg (scaling down) Tj 0 0 0 rg (:) Tj T* 0 Tw 1.026457 Tw (most people are concerned with the possibility of scaling up, but we should also be concerned with the) Tj T* 0 Tw .754987 Tw (issue of scaling down: in other worlds, simple things should be kept simple. To be concrete, let me start) Tj T* 0 Tw .567765 Tw (with the simplest possible thing: a script that takes a single argument and does something to it. It cannot) Tj T* 0 Tw 1.314651 Tw (get more trivial than that \(discarding the possibility of a script without command line arguments, where) Tj T* 0 Tw .188935 Tw (there is nothing to parse\), nevertheless it is a use case ) Tj /F4 10 Tf (extremely common) Tj /F1 10 Tf (: I need to write scripts like that) Tj T* 0 Tw .539513 Tw (nearly every day, I wrote hundreds of them in the last few years and I have never been happy. Here is a) Tj T* 0 Tw (typical example of code I have been writing by hand for years:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 314.0236 cm
-Q
-q
-1 0 0 1 62.69291 144.8236 cm
-0 0 0 rg
-BT /F5 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 448.6898 168 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 149.71 Tm /F3 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
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 144.8236 cm
-Q
-q
-1 0 0 1 62.69291 78.82362 cm
-q
-BT 1 0 0 1 0 52.82 Tm .880651 Tw 12 TL /F1 10 Tf 0 0 0 rg (As you see the whole ) Tj /F3 10 Tf (if __name__ == '__main__' ) Tj /F1 10 Tf (block \(nine lines\) is essentially boilerplate that ) Tj T* 0 Tw 1.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 /F4 10 Tf (hate ) Tj /F1 10 Tf (it. The purpose of using a ) Tj T* 0 Tw 1.47229 Tw (scripting language is convenience and trivial things should be trivial. Unfortunately the standard library) Tj T* 0 Tw ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R70': class PDFStream
-70 0 obj
-% page stream
-<< /Length 4633 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 729.0236 cm
-q
-BT 1 0 0 1 0 28.82 Tm .482093 Tw 12 TL /F1 10 Tf 0 0 0 rg (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 723.0236 cm
-Q
-q
-1 0 0 1 62.69291 589.8236 cm
-0 0 0 rg
-BT /F5 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 448.6898 132 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F3 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
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 589.8236 cm
-Q
-q
-1 0 0 1 62.69291 547.8236 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
-Q
-Q
-q
-1 0 0 1 62.69291 517.8236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.123145 Tw 12 TL /F1 10 Tf 0 0 0 rg (The ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (module is designed to manage well such use cases, and it is able to reduce the original nine) Tj T* 0 Tw (lines of boiler plate to two lines. With the ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (module all you need to write is) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 511.8236 cm
-Q
-q
-1 0 0 1 62.69291 426.6236 cm
-0 0 0 rg
-BT /F5 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 448.6898 84 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 65.71 Tm /F3 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 clap; clap.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 426.6236 cm
-Q
-q
-1 0 0 1 62.69291 396.6236 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 (clap ) 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 279.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL ($ python example3.py -h) Tj T* (usage: example3.py [-h] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 259.4236 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 (clap ) 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 226.4236 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 208.4236 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 202.4236 cm
-Q
-q
-1 0 0 1 62.69291 76.86614 cm
-0 0 0 rg
-BT /F5 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 108 re B*
-Q
-q
-BT 1 0 0 1 0 89.71 Tm 12 TL /F3 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* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-
-endstream
-
-endobj
-% 'R71': class PDFStream
-71 0 obj
-% page stream
-<< /Length 4390 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 679.8236 cm
-0 0 0 rg
-BT /F5 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 84 re B*
-Q
-q
-BT 1 0 0 1 0 65.71 Tm 12 TL /F3 10 Tf 0 0 0 rg ( args = sys.argv[1:]) Tj T* ( if not args:) Tj T* ( sys.exit\('usage: python %s dsn' % sys.argv[0]\)) Tj T* ( elif len\(args\) ) Tj (>) Tj ( 2:) Tj T* ( sys.exit\('Unrecognized arguments: %s' % ' '.join\(argv[2:]\)\)) Tj T* ( main\(*args\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 679.8236 cm
-Q
-q
-1 0 0 1 62.69291 661.8236 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 (clap ) Tj 0 0 0 rg (the entire ) Tj /F3 10 Tf (__main__ ) Tj /F1 10 Tf (block reduces to the usual two lines:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 616.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (if __name__ == '__main__':) Tj T* ( import clap; clap.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 596.6236 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
-Q
-Q
-q
-1 0 0 1 62.69291 467.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL (usage: example4_.py [-h] dsn [table] [today]) Tj T* T* (positional arguments:) Tj T* ( 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 435.4236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .396235 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) 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 /F3 10 Tf (.sql ) Tj /F1 10 Tf (scripts:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 429.4236 cm
-Q
-q
-1 0 0 1 62.69291 272.2236 cm
-0 0 0 rg
-BT /F5 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 448.6898 156 re B*
-Q
-q
-BT 1 0 0 1 0 137.71 Tm 12 TL /F3 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
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 272.2236 cm
-Q
-q
-1 0 0 1 62.69291 242.2236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 6.923059 Tw 12 TL /F1 10 Tf 0 0 0 rg (Using ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (, you can just replace the ) Tj /F3 10 Tf (__main__ ) Tj /F1 10 Tf (block with the usual ) Tj /F3 10 Tf (import clap;) Tj T* 0 Tw (clap.call\(main\) ) Tj /F1 10 Tf (and you get the following usage message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 125.0236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL (usage: example7.py [-h] dsn [scripts [scripts ...]]) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* ( scripts) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 81.02362 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 /F4 10 Tf (clap 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 (clap) Tj 0 0 0 rg (: if my intent is) Tj T* 0 Tw (clear, let's the machine takes care of the details.) Tj T* ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R72': class PDFStream
-72 0 obj
-% page stream
-<< /Length 5415 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 744.0236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Options and flags) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 654.0236 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 612.0236 cm
-q
-BT 1 0 0 1 0 28.82 Tm .476098 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) 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 /F3 10 Tf (main ) Tj /F1 10 Tf (function.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 582.0236 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 /F4 10 Tf (function annotations ) Tj /F1 10 Tf (concept, a new feature of Python 3. An example is worth a) Tj T* 0 Tw (thousand words, so here it is:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 576.0236 cm
-Q
-q
-1 0 0 1 62.69291 478.8236 cm
-0 0 0 rg
-BT /F5 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 448.6898 96 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F3 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 clap; clap.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 478.8236 cm
-Q
-q
-1 0 0 1 62.69291 412.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 /F3 10 Tf (command ) Tj /F1 10 Tf (has been annotated with the tuple ) Tj /F3 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 (clap ) Tj 0 0 0 rg (that ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (is an option and that it can be abbreviated with the letter ) Tj /F3 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 /F3 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 271.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 132 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 113.71 Tm /F3 10 Tf 12 TL ($ python3 example8.py -h) Tj T* (usage: example8.py [-h] [-c COMMAND] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -c COMMAND, --command COMMAND) Tj T* ( SQL query) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 251.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 170.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 53.71 Tm /F3 10 Tf 12 TL ($ python3 example8.py -c"select * from table" dsn) Tj T* (executing select * from table on dsn) Tj T* T* ($ python3 example8.py --command="select * from table" dsn) Tj T* (executing select * from table on dsn) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 150.4236 cm
-q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Notice that if the option is not passed, the variable ) Tj /F3 10 Tf (command ) Tj /F1 10 Tf (will get the value ) Tj /F3 10 Tf (None) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 132.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Even positional argument can be annotated:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 87.22362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (def main\(command: \("SQL query", 'option', 'c'\),) Tj T* ( dsn: \("Database dsn", 'positional', None\)\):) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-
-endstream
-
-endobj
-% 'R73': class PDFStream
-73 0 obj
-% page stream
-<< /Length 5431 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 739.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 695.8236 cm
-q
-BT 1 0 0 1 0 28.82 Tm 3.203318 Tw 12 TL /F1 10 Tf 0 0 0 rg (Of course explicit is better than implicit, an no special cases are special enough, but sometimes) Tj T* 0 Tw .64061 Tw (practicality beats purity, so ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (is smart enough to convert help messages into tuples internally; in other) Tj T* 0 Tw (words, you can just write "Database dsn" instead of ) Tj /F3 10 Tf (\("Database dsn", 'positional', None\)) Tj /F1 10 Tf (:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 650.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (def main\(command: \("SQL query", 'option', 'c'\), dsn: "Database dsn"\):) Tj T* ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 618.6236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .171988 Tw 12 TL /F1 10 Tf 0 0 0 rg (In both cases the usage message will show a nice help string on the right hand side of the ) Tj /F3 10 Tf (dsn ) Tj /F1 10 Tf (positional) Tj T* 0 Tw (argument. varargs \(starred-arguments\) can also be annotated:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 573.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 553.4236 cm
-q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (is a valid signature for ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (, which will recognize the help strings for both ) Tj /F3 10 Tf (dsn ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (scripts) Tj /F1 10 Tf (:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 496.2236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 48 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 29.71 Tm /F3 10 Tf 12 TL (positional arguments:) Tj T* ( dsn Database dsn) Tj T* ( scripts SQL scripts) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 464.2236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .765868 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (also recognizes flags, i.e. boolean options which are ) Tj /F3 10 Tf (True ) Tj /F1 10 Tf (if they are passed to the command line) Tj T* 0 Tw (and ) Tj /F3 10 Tf (False ) Tj /F1 10 Tf (if they are absent. Here is an example:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 419.0236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 36 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 17.71 Tm /F3 10 Tf 12 TL ($ python3 example9.py -v dsn) Tj T* (connecting to dsn) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 289.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL ($ python3 example9.py -h) Tj T* (usage: example9.py [-h] [-v] dsn) Tj T* T* (positional arguments:) Tj T* ( dsn connection string) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ( -v, --verbose prints more info) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 221.8236 cm
-q
-BT 1 0 0 1 0 52.82 Tm 5.832651 Tw 12 TL /F1 10 Tf 0 0 0 rg (For consistency with the way the usage message is printed, I suggest you to follow the) Tj T* 0 Tw 1.686905 Tw (Flag-Option-Positional \(FOP\) convention: in the ) Tj /F3 10 Tf (main ) Tj /F1 10 Tf (function write first the flag arguments, then the) Tj T* 0 Tw .404692 Tw (option arguments and finally the positional arguments. This is just a convention and you are not forced to) Tj T* 0 Tw .478409 Tw (use it, but it makes sense to put the position arguments at the end, since they may be default arguments) Tj T* 0 Tw (and varargs.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 188.8236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (clap for people not using Python 3) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 122.8236 cm
-q
-BT 1 0 0 1 0 52.82 Tm .12999 Tw 12 TL /F1 10 Tf 0 0 0 rg (I do not use Python 3. At work we are just starting to think about migrating to Python 2.6. I think it will take) Tj T* 0 Tw 1.269988 Tw (years before we even think to migrate to Python 3. I am pretty much sure most Pythonistas are in the) Tj T* 0 Tw 2.089984 Tw (same situation. Therefore ) Tj 0 0 .501961 rg (clap ) Tj 0 0 0 rg (provides a way to work with function annotations even in Python 2.X) Tj T* 0 Tw 1.352339 Tw (\(including Python 2.3\). There is no magic involved; you just need to add the annotations by hand. For) Tj T* 0 Tw (instance) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 89.62362 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 24 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL (def main\(dsn: "Database dsn", *scripts: "SQL scripts"\):) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-
-endstream
-
-endobj
-% 'R74': class PDFStream
-74 0 obj
-% page stream
-<< /Length 5574 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 753.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (becomes:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 671.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 53.71 Tm /F3 10 Tf 12 TL (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* (main.__annotations__ = dict\() Tj T* (dsn="Database dsn",) Tj T* (scripts="SQL scripts"\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 627.8236 cm
-q
-BT 1 0 0 1 0 28.82 Tm .412765 Tw 12 TL /F1 10 Tf 0 0 0 rg (One should be careful to much the keys of the annotations dictionary with the names of the arguments in) Tj T* 0 Tw 3.347485 Tw (the annotated function; for lazy people with Python 2.4 available the simplest way is to use the) Tj T* 0 Tw /F3 10 Tf (clap.annotations ) Tj /F1 10 Tf (decorator that performs the check for you.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 546.6236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 72 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 53.71 Tm /F3 10 Tf 12 TL (@annotations\() Tj T* (dsn="Database dsn",) Tj T* (scripts="SQL scripts"\)) Tj T* (def main\(dsn, *scripts\):) Tj T* ( ...) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 514.6236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.422164 Tw 12 TL /F1 10 Tf 0 0 0 rg (In the rest of this article I will assume that you are using Python 2.X with ) Tj /F3 10 Tf (X >) Tj (= 4 ) Tj /F1 10 Tf (and I will use the) Tj T* 0 Tw /F3 10 Tf (clap.annotations ) Tj /F1 10 Tf (decorator.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 481.6236 cm
-q
-BT 1 0 0 1 0 8.435 Tm 21 TL /F2 17.5 Tf 0 0 0 rg (Advanced usage) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 427.6236 cm
-q
-BT 1 0 0 1 0 40.82 Tm .115703 Tw 12 TL /F1 10 Tf 0 0 0 rg (One of the goals of clap is to have a learning curve of ) Tj /F4 10 Tf (minutes) Tj /F1 10 Tf (, compared to the learning curve of ) Tj /F4 10 Tf (hours ) Tj /F1 10 Tf (of) Tj T* 0 Tw .196098 Tw 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. That does not mean that I have removed all the advanced features of ) Tj 0 0 .501961 rg (argparse) Tj 0 0 0 rg (. Actually a lot of) Tj T* 0 Tw 1.529269 Tw 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (power persists in ) Tj 0 0 .501961 rg (clap) Tj 0 0 0 rg (: in particular, the ) Tj /F3 10 Tf (type) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (choices ) Tj /F1 10 Tf (and ) Tj /F3 10 Tf (metavar ) Tj /F1 10 Tf (concepts are there.) Tj T* 0 Tw (Until now, I have only showed simple annotations, but in general an annotation is a 5-tuple of the form) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 421.6236 cm
-Q
-q
-1 0 0 1 62.69291 409.6236 cm
-0 0 0 rg
-BT /F5 10 Tf 12 TL ET
-BT 1 0 0 1 0 2 Tm T* ET
-q
-1 0 0 1 20 0 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 5.71 Tm /F3 10 Tf 12 TL (\(help, kind, abbrev, type, choices, metavar\)) Tj T* ET
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 409.6236 cm
-Q
-q
-1 0 0 1 62.69291 367.6236 cm
-q
-BT 1 0 0 1 0 28.82 Tm 3.38811 Tw 12 TL /F1 10 Tf 0 0 0 rg (where ) Tj /F3 10 Tf (help ) Tj /F1 10 Tf (is the help message, ) Tj /F3 10 Tf (kind ) Tj /F1 10 Tf (is one of {"flag", "option ", "positional"}, ) Tj /F3 10 Tf (abbrev ) Tj /F1 10 Tf (is a) Tj T* 0 Tw 2.203735 Tw (one-character string, ) Tj /F3 10 Tf (type ) Tj /F1 10 Tf (is callable taking a string in input, choices is a sequence of values and) Tj T* 0 Tw /F3 10 Tf (metavar ) Tj /F1 10 Tf (is a string.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 337.6236 cm
-q
-BT 1 0 0 1 0 16.82 Tm .006654 Tw 12 TL /F3 10 Tf 0 0 0 rg (type ) Tj /F1 10 Tf (is used to automagically convert the arguments from string to any Python type; by default there is no) Tj T* 0 Tw (convertion i.e. ) Tj /F3 10 Tf (type=None) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 307.6236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 2.904692 Tw 12 TL /F3 10 Tf 0 0 0 rg (choices ) Tj /F1 10 Tf (is used to restrict the number of the valid options; by default there is no restriction i.e.) Tj T* 0 Tw /F3 10 Tf (choices=None) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 277.6236 cm
-q
-BT 1 0 0 1 0 16.82 Tm 1.071751 Tw 12 TL /F3 10 Tf 0 0 0 rg (metavar ) Tj /F1 10 Tf (is used to change the argument name in the usage message \(and only there\); by default the) Tj T* 0 Tw (metavar is equal to the name of the argument.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 259.6236 cm
-q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (Here is an example showing all of such features \(shamelessly stolen from the ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (documentation\):) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 253.6236 cm
-Q
-q
-1 0 0 1 62.69291 84.21376 cm
-0 0 0 rg
-BT /F5 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
-.934933 0 0 .934933 0 0 cm
-q
-1 0 0 1 6.6 7.059329 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 480 180 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 161.71 Tm /F3 10 Tf 12 TL (import clap) Tj T* T* (@clap.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* ( 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* ( print\(result\)) 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 84.21376 cm
-Q
-
-endstream
-
-endobj
-% 'R75': class PDFStream
-75 0 obj
-% page stream
-<< /Length 5160 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 753.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage for the script:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 635.8236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F3 10 Tf 12 TL (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* T* (positional arguments:) Tj T* ( {add,mul} The name of an operator) Tj T* ( n A number) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 615.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here are a couple of examples of use:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 522.4849 cm
-q
-q
-.87797 0 0 .87797 0 0 cm
-q
-1 0 0 1 6.6 7.517338 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 534 96 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 77.71 Tm /F3 10 Tf 12 TL ($ python example10.py add 1 2 3 4) Tj T* (10.0) Tj T* ($ python example10.py mul 1 2 3 4) Tj T* (24.0) Tj T* ($ python example10.py ad 1 2 3 4 # a mispelling error) Tj T* (usage: example10.py [-h] {add,mul} [n [n ...]]) Tj T* (example10.py: error: argument operator: invalid choice: 'ad' \(choose from 'add', 'mul'\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 489.4849 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 423.4849 cm
-q
-BT 1 0 0 1 0 52.82 Tm .928488 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) 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. It is possible to pass options to the underlying) Tj T* 0 Tw .03816 Tw 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object \(currently it accepts the default arguments ) Tj /F3 10 Tf (prog) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (usage) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (description) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (epilog) Tj /F1 10 Tf (,) Tj T* 0 Tw 18.21744 Tw /F3 10 Tf (version) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (parents) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (formatter_class) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (prefix_chars) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (fromfile_prefix_chars) Tj /F1 10 Tf (,) Tj T* 0 Tw 1.035976 Tw /F3 10 Tf (argument_default) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (conflict_handler) Tj /F1 10 Tf (, ) Tj /F3 10 Tf (add_help) Tj /F1 10 Tf (\) simply by setting such attributes on the ) Tj /F3 10 Tf (main) Tj T* 0 Tw /F1 10 Tf (function. For instance) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 354.2849 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 60 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 41.71 Tm /F3 10 Tf 12 TL (def main\(...\):) Tj T* ( pass) Tj T* T* (main.add_help = False) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 298.2849 cm
-q
-BT 1 0 0 1 0 40.82 Tm 1.256457 Tw 12 TL /F1 10 Tf 0 0 0 rg (disable the recognition of the help flag ) Tj /F3 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 (clap ) 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 1.463876 Tw (still it is possible if she wants to. I redirect the interested readers to the documentation of ) Tj 0 0 .501961 rg (argparse ) Tj 0 0 0 rg (to) Tj T* 0 Tw (understand the meaning of the various options.) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 268.2849 cm
-q
-BT 1 0 0 1 0 16.82 Tm .154269 Tw 12 TL /F1 10 Tf 0 0 0 rg (If you want to access the underlying ) Tj 0 0 .501961 rg (ArgumentParser ) Tj 0 0 0 rg (object, you can use the ) Tj /F3 10 Tf (clap.parser_from ) Tj /F1 10 Tf (utility) Tj T* 0 Tw (function:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 151.0849 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 108 re B*
-Q
-q
-BT 1 0 0 1 0 89.71 Tm 12 TL /F3 10 Tf 0 0 0 rg (>) Tj (>) Tj (>) Tj ( import clap) Tj T* (>) Tj (>) Tj (>) Tj ( def main\(arg\):) Tj T* (... pass) Tj T* (...) Tj T* (>) Tj (>) Tj (>) Tj ( print clap.parser_from\(main\)) Tj T* (ArgumentParser\(prog='', usage=None, description=None, version=None,) Tj T* (formatter_class=) Tj (<) Tj (class 'argparse.HelpFormatter') Tj (>) Tj (, conflict_handler='error',) Tj T* (add_help=True\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 131.0849 cm
-q
-BT 1 0 0 1 0 4.82 Tm 12 TL /F1 10 Tf 0 0 0 rg (I use ) Tj /F3 10 Tf (clap.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 101.0849 cm
-q
-BT 1 0 0 1 0 16.82 Tm .541318 Tw 12 TL /F1 10 Tf 0 0 .501961 rg (clap ) Tj 0 0 0 rg (uses an ) Tj /F3 10 Tf (Annotation ) Tj /F1 10 Tf (class to convert the raw annotations in the function signature into annotation) Tj T* 0 Tw (objects, i.e. objects with six attributes ) Tj /F3 10 Tf (help, kind, short, type, choices, metavar) Tj /F1 10 Tf (.) Tj T* ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R76': class PDFStream
-76 0 obj
-% page stream
-<< /Length 3056 >>
-stream
-1 0 0 1 0 0 cm BT /F1 12 Tf 14.4 TL ET
-q
-1 0 0 1 62.69291 717.0236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 40.82 Tm /F1 10 Tf 12 TL 1.682126 Tw (Advanced users can implement their own annotation objects. Since the special case of no annotation) Tj T* 0 Tw 1.088735 Tw (must be taken care of, the annotation factory must return a suitable default annotation object where no) Tj T* 0 Tw .18811 Tw (arguments are passed in input. Here is an example of how you could implement annotations for positional) Tj T* 0 Tw (arguments:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 711.0236 cm
-Q
-q
-1 0 0 1 62.69291 601.8236 cm
-0 0 0 rg
-BT /F5 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 448.6898 108 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 89.71 Tm /F3 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
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 601.8236 cm
-Q
-q
-1 0 0 1 62.69291 583.8236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can use such annotations objects as follows:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 577.8236 cm
-Q
-q
-1 0 0 1 62.69291 420.6236 cm
-0 0 0 rg
-BT /F5 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 448.6898 156 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 137.71 Tm /F3 10 Tf 12 TL (import clap) Tj T* (from annotations import Positional) Tj T* T* (@clap.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 clap; clap.call\(main\)) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-Q
-Q
-q
-1 0 0 1 62.69291 420.6236 cm
-Q
-q
-1 0 0 1 62.69291 402.6236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (Here is the usage message:) Tj T* ET
-Q
-Q
-q
-1 0 0 1 62.69291 273.4236 cm
-q
-q
-1 0 0 1 0 0 cm
-q
-1 0 0 1 6.6 6.6 cm
-q
-.662745 .662745 .662745 RG
-.5 w
-.960784 .960784 .862745 rg
-n -6 -6 468.6898 120 re B*
-Q
-q
-0 0 0 rg
-BT 1 0 0 1 0 101.71 Tm /F3 10 Tf 12 TL (usage: example11.py [-h] i n [rest [rest ...]]) Tj T* T* (positional arguments:) Tj T* ( i This is an int) Tj T* ( n This is a float) Tj T* ( rest Other arguments) Tj T* T* (optional arguments:) Tj T* ( -h, --help show this help message and exit) Tj T* ET
-Q
-Q
-Q
-Q
-Q
-q
-1 0 0 1 62.69291 253.4236 cm
-q
-0 0 0 rg
-BT 1 0 0 1 0 4.82 Tm /F1 10 Tf 12 TL (You can go on and define Option and Flag classes, if you like.) Tj T* ET
-Q
-Q
-
-endstream
-
-endobj
-% 'R77': class PDFPageLabels
-77 0 obj
-% Document Root
-<< /Nums [ 0
- 78 0 R
- 1
- 79 0 R
- 2
- 80 0 R
- 3
- 81 0 R
- 4
- 82 0 R
- 5
- 83 0 R
- 6
- 84 0 R
- 7
- 85 0 R ] >>
-endobj
-% 'R78': class PDFPageLabel
-78 0 obj
-% None
-<< /S /D
- /St 1 >>
-endobj
-% 'R79': class PDFPageLabel
-79 0 obj
-% None
-<< /S /D
- /St 2 >>
-endobj
-% 'R80': class PDFPageLabel
-80 0 obj
-% None
-<< /S /D
- /St 3 >>
-endobj
-% 'R81': class PDFPageLabel
-81 0 obj
-% None
-<< /S /D
- /St 4 >>
-endobj
-% 'R82': class PDFPageLabel
-82 0 obj
-% None
-<< /S /D
- /St 5 >>
-endobj
-% 'R83': class PDFPageLabel
-83 0 obj
-% None
-<< /S /D
- /St 6 >>
-endobj
-% 'R84': class PDFPageLabel
-84 0 obj
-% None
-<< /S /D
- /St 7 >>
-endobj
-% 'R85': class PDFPageLabel
-85 0 obj
-% None
-<< /S /D
- /St 8 >>
-endobj
-xref
-0 86
-0000000000 65535 f
-0000000113 00000 n
-0000000260 00000 n
-0000000425 00000 n
-0000000612 00000 n
-0000000872 00000 n
-0000001134 00000 n
-0000001382 00000 n
-0000001630 00000 n
-0000001894 00000 n
-0000002158 00000 n
-0000002423 00000 n
-0000002688 00000 n
-0000002938 00000 n
-0000003191 00000 n
-0000003366 00000 n
-0000003619 00000 n
-0000003801 00000 n
-0000003969 00000 n
-0000004369 00000 n
-0000004631 00000 n
-0000004895 00000 n
-0000005145 00000 n
-0000005411 00000 n
-0000005677 00000 n
-0000005942 00000 n
-0000006192 00000 n
-0000006443 00000 n
-0000006822 00000 n
-0000007088 00000 n
-0000007354 00000 n
-0000007619 00000 n
-0000007870 00000 n
-0000008213 00000 n
-0000008501 00000 n
-0000008785 00000 n
-0000009051 00000 n
-0000009339 00000 n
-0000009590 00000 n
-0000009942 00000 n
-0000010207 00000 n
-0000010473 00000 n
-0000010739 00000 n
-0000010990 00000 n
-0000011333 00000 n
-0000011583 00000 n
-0000011833 00000 n
-0000012083 00000 n
-0000012345 00000 n
-0000012580 00000 n
-0000012932 00000 n
-0000013197 00000 n
-0000013447 00000 n
-0000013734 00000 n
-0000014000 00000 n
-0000014250 00000 n
-0000014537 00000 n
-0000014788 00000 n
-0000015143 00000 n
-0000015424 00000 n
-0000015583 00000 n
-0000015849 00000 n
-0000015974 00000 n
-0000016165 00000 n
-0000016369 00000 n
-0000016562 00000 n
-0000016772 00000 n
-0000016962 00000 n
-0000017151 00000 n
-0000017322 00000 n
-0000023577 00000 n
-0000028311 00000 n
-0000032802 00000 n
-0000038318 00000 n
-0000043850 00000 n
-0000049525 00000 n
-0000054786 00000 n
-0000057947 00000 n
-0000058131 00000 n
-0000058208 00000 n
-0000058285 00000 n
-0000058362 00000 n
-0000058439 00000 n
-0000058516 00000 n
-0000058593 00000 n
-0000058670 00000 n
-trailer
-<< /ID
- % ReportLab generated PDF document -- digest (http://www.reportlab.com)
- [(\261\274,t\312\324D[\217\346f\222\0103;\021) (\261\274,t\312\324D[\217\346f\222\0103;\021)]
-
- /Info 60 0 R
- /Root 59 0 R
- /Size 86 >>
-startxref
-58717
-%%EOF
diff --git a/clap/article/article.txt b/clap/article/article.txt deleted file mode 100644 index 79cb834..0000000 --- a/clap/article/article.txt +++ /dev/null @@ -1,442 +0,0 @@ -The Easiest Command Line Arguments Parser in the World -================================================================ - -There is no want of command line arguments parsers in 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 -them are quite powerful and especially argparse_ is an industrial -strength solution; unfortunately, all of them have a non-zero learning -curve and a certain verbosity. - -Enters clap_. clap_ 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 clap_ is just a simple -wrapper over argparse_, hiding most of the complexity while retaining -most of the power. clap_ 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. - -Actually I am pretty much convinced that features provided by ``clap`` -are 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, -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. - -The importance of scaling down ------------------------------------- - -An ex-coworker of mine, David Welton, once wrote a nice article about -the importance of `scaling down`_: 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 *extremely common*: -I need to write scripts like that nearly every day, I wrote hundreds -of them in the last few years and I have never been happy. Here is -a typical example of code I have been writing by hand for years: - - .. include:: example1.py - :literal: - -As you see the whole ``if __name__ == '__main__'`` block (nine lines) is -essentially boilerplate that should not 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 *hate* 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 getopt_ and optparse_ does not help, since they are -intended to manage options and not positional arguments; the argparse_ -module helps a bit and it is able to reduce the boilerplate from nine -lines to six lines: - - .. include:: example2.py - :literal: - -However 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. - -The clap_ 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 -clap_ module all you need to write is - - .. include:: example3.py - :literal: - -The clap_ module provides for free (actually the work is done by the -underlying argparse_ module) a nice usage message:: - - $ python example3.py -h - usage: example3.py [-h] dsn - - positional arguments: - dsn - - optional arguments: - -h, --help show this help message and exit - -This is only the tip of the iceberg: clap_ is able to do much more than that. - -Positional default arguments --------------------------------------------------- - -I have encountered this use case at work hundreds of times: - - .. include:: example4.py - :literal: - -With clap_ the entire ``__main__`` block reduces to the usual two lines:: - - if __name__ == '__main__': - import clap; clap.call(main) - -In other words, six lines of boilerplate have been removed, and I have -the usage message for free:: - - usage: example4_.py [-h] dsn [table] [today] - - positional arguments: - dsn - table - today - - optional arguments: - -h, --help show this help message and exit - -clap_ manages transparently even the case when you want to pass a -variable number of arguments. Here is an example, a script running -on a database a series of ``.sql`` scripts: - - .. include:: example6.py - :literal: - -Using clap_, you can just replace the ``__main__`` block with the usual -``import clap; clap.call(main)`` and you get the following usage message:: - - usage: example7.py [-h] dsn [scripts [scripts ...]] - - positional arguments: - dsn - scripts - - optional arguments: - -h, --help show this help message and exit - -The examples here should have made clear that *clap is able to figure out -the command line arguments parser to use from the signature of the main -function*. This is the whole idea behind clap_: if my intent is clear, -let's the machine takes care of the details. - -Options and flags ---------------------------------------- - -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 optionparse_ recipe, which provides a -convenient wrapper over optionparse_. Alternatively, in the simplest cases, -I have just performed the parsing by hand, instead of manually building -a suitable OptionParser. - -clap_ is inspired to the optionparse_ 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 ``main`` function. - -The idea comes from the `function annotations` concept, a new -feature of Python 3. An example is worth a thousand words, so here -it is: - - .. include:: example8.py - :literal: - -As you see, the argument ``command`` has been annotated with the -tuple ``("SQL query", 'option', 'c')``: the first string is the -help string which will appear in the usage message, whereas the -second and third strings tell clap_ that ``command`` is an option and that -it can be abbreviated with the letter ``c``. Of course, it also -possible to use the long option format, by prefixing the option -with ``--command=``. The resulting usage message is the following:: - - $ 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 - -Here are two examples of usage:: - - $ python3 example8.py -c"select * from table" dsn - executing select * from table on dsn - - $ python3 example8.py --command="select * from table" dsn - executing select * from table on dsn - -Notice that if the option is not passed, the variable ``command`` -will get the value ``None``. - -Even positional argument can be annotated:: - - def main(command: ("SQL query", 'option', 'c'), - dsn: ("Database dsn", 'positional', None)): - ... - -Of course explicit is better than implicit, an no special cases are -special enough, but sometimes practicality beats purity, so clap_ is -smart enough to convert help messages into tuples internally; in other -words, you can just write "Database dsn" instead of ``("Database dsn", -'positional', None)``:: - - - def main(command: ("SQL query", 'option', 'c'), dsn: "Database dsn"): - ... - -In both cases -the usage message will show a nice help string on the right hand side -of the ``dsn`` positional argument. varargs (starred-arguments) can also -be annotated:: - - def main(dsn: "Database dsn", *scripts: "SQL scripts"): - ... - -is a valid signature for clap_, which will recognize the help strings -for both ``dsn`` and ``scripts``:: - - positional arguments: - dsn Database dsn - scripts SQL scripts - -clap_ 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:: - - $ python3 example9.py -v dsn - connecting to dsn - -:: - - $ 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 - -For consistency with the way the usage message is printed, I suggest you to -follow the Flag-Option-Positional (FOP) convention: in the ``main`` -function write first the flag arguments, then the option arguments and -finally the positional arguments. This is just 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. - -clap for people not using Python 3 --------------------------------------------------- - -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 clap_ 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 - -:: - - def main(dsn: "Database dsn", *scripts: "SQL scripts"): - -becomes:: - - def main(dsn, *scripts): - ... - main.__annotations__ = dict( - dsn="Database dsn", - scripts="SQL scripts") - -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 -``clap.annotations`` decorator that performs the check for you. - -:: - - @annotations( - dsn="Database dsn", - scripts="SQL scripts") - def main(dsn, *scripts): - ... - -In the rest of this article I will assume that you are using Python 2.X with -``X >= 4`` and I will use the ``clap.annotations`` decorator. - -Advanced usage --------------------------------------------------- - -One of the goals of clap is to have a learning curve of *minutes*, compared -to the learning curve of *hours* of argparse_. That does not mean -that I have removed all the advanced features of argparse_. Actually -a lot of argparse_ power persists in clap_: in particular, the -``type``, ``choices`` and ``metavar`` concepts are there. -Until now, I have only showed simple annotations, but in general -an annotation is a 5-tuple of the form - - ``(help, kind, abbrev, type, choices, metavar)`` - -where ``help`` is the help message, ``kind`` is one of {"flag", -"option ", "positional"}, ``abbrev`` is a one-character string, -``type`` is callable taking a string in input, choices is a sequence -of values and ``metavar`` is a string. - -``type`` is used to automagically convert the arguments from string -to any Python type; by default there is no convertion i.e. ``type=None``. - -``choices`` is used to restrict the number of the valid -options; by default there is no restriction i.e. ``choices=None``. - -``metavar`` 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. - -Here is an example showing all of such features (shamelessly stolen -from the argparse_ documentation): - - .. include:: example10.py - :literal: - -Here is the usage for the script:: - - usage: example10.py [-h] {add,mul} [n [n ...]] - - positional arguments: - {add,mul} The name of an operator - n A number - - optional arguments: - -h, --help show this help message and exit - -Here are a couple of examples of use:: - - $ python example10.py add 1 2 3 4 - 10.0 - $ python example10.py mul 1 2 3 4 - 24.0 - $ python example10.py ad 1 2 3 4 # a mispelling error - usage: example10.py [-h] {add,mul} [n [n ...]] - example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul') - -A few notes on the underlying implementation ----------------------------------------------------- - -clap_ relies on a argparse_ for all of the heavy lifting work. It is possible -to pass options to the underlying ArgumentParser_ object (currently it -accepts the default arguments ``description``, ``epilog``, ``prog``, ``usage``, -``add_help``, ``argument_default``, ``parents``, ``prefix_chars``, -``conflict_handler``, ``formatter_class``) simply by setting such attributes -on the ``main`` function. -For instance - -:: - - def main(...): - pass - - main.add_help = False - -disable the recognition of the help flag ``-h, --help``. This is not -particularly elegant, but I assume the typical user of clap_ 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. I redirect the interested -readers to the documentation of argparse_ to understand the meaning of -the various options. - -If you want to access the underlying ArgumentParser_ object, you can -use the ``clap.parser_from`` utility function: - ->>> import clap ->>> def main(arg): -... pass -... ->>> print clap.parser_from(main) -ArgumentParser(prog='', usage=None, description=None, version=None, -formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', -add_help=True) - -I use ``clap.parser_from`` in the unit tests of the module, but regular -users should never need to use it. - -clap_ uses an ``Annotation`` class to convert the tuples -in the function signature into annotation objects, i.e. objects with -six attributes ``help, kind, short, type, choices, metavar``. - -Advanced users can implement their own annotation objects. -For instance, here is an example of how you could implement annotations for -positional arguments: - - .. include:: annotations.py - :literal: - -You can use such annotations objects as follows: - - .. include:: example11.py - :literal: - -Here is the usage message:: - - 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 - -You can go on and define ``Option`` and ``Flag`` classes, if you like. -Using custom annotation objects you could do advanced things like extracting the -annotations from a configuration file or from a database, but I expect such -use cases to be quite rare: the default mechanism should work -pretty well for most users. - -.. _argparse: http://argparse.googlecode.com -.. _optparse: http://docs.python.org/library/optparse.html -.. _getopt: http://docs.python.org/library/getopt.html -.. _optionparse: http://code.activestate.com/recipes/278844-parsing-the-command-line/ -.. _clap: -.. _scaling down: http://www.welton.it/articles/scalable_systems -.. _downwardly scalable: http://www.welton.it/articles/scalable_systems -.. _ArgumentParser: http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html diff --git a/clap/article/clap.py b/clap/article/clap.py deleted file mode 120000 index f85cf0d..0000000 --- a/clap/article/clap.py +++ /dev/null @@ -1 +0,0 @@ -../clap.py
\ No newline at end of file diff --git a/clap/article/example1.py b/clap/article/example1.py deleted file mode 100644 index 59d4ef1..0000000 --- a/clap/article/example1.py +++ /dev/null @@ -1,13 +0,0 @@ -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:])) diff --git a/clap/article/example10.py b/clap/article/example10.py deleted file mode 100644 index 3797c3f..0000000 --- a/clap/article/example10.py +++ /dev/null @@ -1,14 +0,0 @@ -import clap - -@clap.annotations( -operator=("The name of an operator", 'positional', None, str, ['add', 'mul']), -numbers=("A number", 'positional', 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__': - clap.call(main) diff --git a/clap/article/example11.py b/clap/article/example11.py deleted file mode 100644 index 9c078b7..0000000 --- a/clap/article/example11.py +++ /dev/null @@ -1,12 +0,0 @@ -import clap -from annotations import Positional - -@clap.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 clap; clap.call(main) diff --git a/clap/article/example2.py b/clap/article/example2.py deleted file mode 100644 index 69981ee..0000000 --- a/clap/article/example2.py +++ /dev/null @@ -1,10 +0,0 @@ -def main(dsn): - "Do something on the database" - print(dsn) - -if __name__ == '__main__': - import argparse - p = argparse.ArgumentParser() - p.add_argument('dsn') - arg = p.parse_args() - main(arg.dsn) diff --git a/clap/article/example3.py b/clap/article/example3.py deleted file mode 100644 index 94b756f..0000000 --- a/clap/article/example3.py +++ /dev/null @@ -1,6 +0,0 @@ -def main(dsn): - "Do something with the database" - print(dsn) - -if __name__ == '__main__': - import clap; clap.call(main) diff --git a/clap/article/example4.py b/clap/article/example4.py deleted file mode 100644 index 08a7dd2..0000000 --- a/clap/article/example4.py +++ /dev/null @@ -1,14 +0,0 @@ -from datetime import datetime - -def main(dsn, table='product', today=datetime.today()): - "Do something on the database" - print(dsn, table, today) - -if __name__ == '__main__': - import sys - args = sys.argv[1:] - if not args: - sys.exit('usage: python %s dsn' % sys.argv[0]) - elif len(args) > 2: - sys.exit('Unrecognized arguments: %s' % ' '.join(argv[2:])) - main(*args) diff --git a/clap/article/example4_.py b/clap/article/example4_.py deleted file mode 100644 index 9ae2d46..0000000 --- a/clap/article/example4_.py +++ /dev/null @@ -1,8 +0,0 @@ -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 clap; clap.call(main) diff --git a/clap/article/example5.py b/clap/article/example5.py deleted file mode 100644 index 59f88c7..0000000 --- a/clap/article/example5.py +++ /dev/null @@ -1,8 +0,0 @@ -from datetime import datetime - -def main(dsn, today=datetime.today()): - "Do something on the database" - print(dsn, today) - -if __name__ == '__main__': - import clap; clap.call(main) diff --git a/clap/article/example6.py b/clap/article/example6.py deleted file mode 100644 index 9c7b5fd..0000000 --- a/clap/article/example6.py +++ /dev/null @@ -1,12 +0,0 @@ -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:]) diff --git a/clap/article/example7.py b/clap/article/example7.py deleted file mode 100644 index ab3f2f7..0000000 --- a/clap/article/example7.py +++ /dev/null @@ -1,9 +0,0 @@ -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 clap; clap.call(main) diff --git a/clap/article/example8.py b/clap/article/example8.py deleted file mode 100644 index 2f675f5..0000000 --- a/clap/article/example8.py +++ /dev/null @@ -1,7 +0,0 @@ -def main(command: ("SQL query", 'option', 'c'), dsn): - if command: - print('executing %s on %s' % (command, dsn)) - # ... - -if __name__ == '__main__': - import clap; clap.call(main) diff --git a/clap/article/example9.py b/clap/article/example9.py deleted file mode 100644 index 6613345..0000000 --- a/clap/article/example9.py +++ /dev/null @@ -1,7 +0,0 @@ -def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'): - if verbose: - print('connecting to %s' % dsn) - # ... - -if __name__ == '__main__': - import clap; clap.call(main) diff --git a/clap/doc.html b/clap/doc.html deleted file mode 100644 index 15a033d..0000000 --- a/clap/doc.html +++ /dev/null @@ -1,187 +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>clap: a simplified interface to optparse</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" id="clap-a-simplified-interface-to-optparse"> -<h1 class="title">clap: a simplified interface to optparse</h1> - -<p>The Python standard library contains three different modules for the -parsing of command line options: <tt class="docutils literal">getopt</tt> (from the stone age), -<tt class="docutils literal">optparse</tt> (from Python 2.3) and <tt class="docutils literal">argparse</tt> (from Python 2.7). -There are therefore plenty of industrial strength ways of parsing -the command line in Python; nevertheless, in my opinion, we a <em>simple</em> -way is still missing.</p> -<p>I see two classes of users for command line scripts: on one side there -are 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; on the other side there are programmers -writing power user tools for other programmers or system administrators. -I strongly believe that 99.9% of the users of command line interfaces -fit in the first category. 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.</p> -<p><tt class="docutils literal">clap</tt> solves this problem. By design it is not intended to -be an industrial strength command line parsing module. Its capabilities -are limited <em>on purpose</em>. 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 <tt class="docutils literal">optparse</tt>: the -features provided by <tt class="docutils literal">clap</tt> have been more than enough for -me and I guess that the same could be said for most people.</p> -<p>The fundamental idea behind <tt class="docutils literal">clap</tt> is to extract the parser -from the usage docstring. In this way the user does not need to write -the parser by hand by duplicating the instructions which she has already -encoded implicitly in the usage docstring.</p> -<p>Here is a concrete example of a script using <tt class="docutils literal">clap</tt>:</p> -<pre class="literal-block"> -"""\ -usage: %prog [options] --c, --color=black: set default color --d, --delete=: delete the given file --a, --delete-all: delete all files -""" - -def print_(color, txt): - code = {'black': 30, 'red': 31}[color] - print '\x1b[%dm%s\x1b[0m' % (code, txt) - -if __name__=='__main__': - from clap import OptionParser - option, args = OptionParser(__doc__).parse_args() - color = option.color - if option.delete_all: - print_(color, "Delete all files") - elif option.delete: - print_(color, "Delete the file %s" % option.delete) - -</pre> -<p>Here are a few examples of usage:</p> -<pre class="literal-block"> -$ python example.py -<no output> - -$ python example.py -h -Usage: example.py [options] - -Options: - -h, --help show this help message and exit - -c COLOR, --color=COLOR - set default color - -d DELETE, --delete=DELETE - delete the given file - -a, --delete_all delete all files - -$ python example.py -d -Usage: example.py [options] - -example.py: error: -d option requires an argument - -$ python example.py --delete x.txt -Delete the file x.txt - -$ python example.py --delete-all -Delete all files - -$ python example.py -a --color=red -Delete all files # this is printed in red on most terminals - - -</pre> -<p>Here is some explanation. First of all, <tt class="docutils literal">clap</tt> recognizes -four classes of command line arguments:</p> -<ul class="simple"> -<li>positional arguments (none in this example)</li> -<li>options with non-trivial defaults (<tt class="docutils literal">color=black</tt> in this example)</li> -<li>options with null defaults (<tt class="docutils literal">delete=</tt> in this example)</li> -<li>flags (<tt class="docutils literal"><span class="pre">delete-all</span></tt> in this example)</li> -</ul> -<p>You can have a generic number of positional arguments, including no -arguments. An option with default can be set on the command line -(for instance you can pass <tt class="docutils literal"><span class="pre">--color=red</span></tt>) or not; if not, the default -value is used. An option with null default is a special case of -option with a default, where the default value is the empty string. -A flag is similar to an option with null default: if the -flag is not passed its value is False, otherwise it is True. -The difference with respect to a null default option is that -the flag value is a boolean, not a string, and that the syntax -is different (there is no <tt class="docutils literal">=</tt>).</p> -<p>The <tt class="docutils literal">OptionParser.parse_args</tt> method -parses the usage docstring by looking at the -characters ",", ":", "=", "\n", so be careful when using them. If -the docstring is not correctly formatted you will get a SyntaxError.</p> -<p>Options with dashes inside the name, such as -<tt class="docutils literal"><span class="pre">--delete-all</span></tt>, will be translated with underscore (<tt class="docutils literal">delete_all</tt>), since -<tt class="docutils literal"><span class="pre">delete-all</span></tt> is not a valid Python identifier.</p> -</div> -</body> -</html> diff --git a/clap/doc.txt b/clap/doc.txt deleted file mode 100644 index c84d8da..0000000 --- a/clap/doc.txt +++ /dev/null @@ -1,82 +0,0 @@ -clap: a simplified interface to optparse -------------------------------------------------- - -The Python standard library 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). -There are therefore plenty of industrial strength ways of parsing -the command line in Python; nevertheless, in my opinion, we a *simple* -way is still missing. - -I see two classes of users for command line scripts: on one side there -are 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; on the other side there are programmers -writing power user tools for other programmers or system administrators. -I strongly believe that 99.9% of the users of command line interfaces -fit in the first category. 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. - -``clap`` solves this problem. By design it is not intended to -be an industrial strength command line parsing module. Its capabilities -are limited *on purpose*. 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 ``optparse``: the -features provided by ``clap`` have been more than enough for -me and I guess that the same could be said for most people. - -The fundamental idea behind ``clap`` is to extract the parser -from the usage docstring. In this way the user does not need to write -the parser by hand by duplicating the instructions which she has already -encoded implicitly in the usage docstring. - -Here is a concrete example of a script using ``clap``: - -.. include:: example.py - :literal: - -Here are a few examples of usage: - -.. include:: example.txt - :literal: - -Here are some explanations. First of all, ``clap`` recognizes -four classes of command line arguments: - -- positional arguments (none in this example) -- options with non-trivial defaults (``color=black`` in this example) -- options with null defaults (``delete=`` in this example) -- flags (``delete-all`` in this example) - -You can have a generic number of positional arguments, including no -arguments. An option with default can be set on the command line -(for instance you can pass ``--color=red``) or not; if not, the default -value is used. An option with null default is a special case of -option with a default, where the default value is the empty string. -A flag is similar to an option with null default: if the -flag is not passed its value is False, otherwise it is True. -The difference with respect to a null default option is that -the flag value is a boolean, not a string, and that the syntax -is different (there is no ``=``). - -The ``OptionParser.parse_args`` method -parses the usage docstring by looking at the -characters ",", ":", "=", "\\n" (be careful when using them, if -the docstring is not correctly formatted you will get a ``SyntaxError``). - -Options with dashes inside the name, such as -``--delete-all``, are translated into with underscore (``delete_all``), since -``delete-all`` is not a valid Python identifier. - -Limitations ------------------------------------- - -The default values cannot contain a ':' (or it should be escaped). - - -.. 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: clap_. diff --git a/clap/example.py b/clap/example.py deleted file mode 100644 index 69cc981..0000000 --- a/clap/example.py +++ /dev/null @@ -1,20 +0,0 @@ -"""\ -usage: %prog [options] --c, --color=black: set default color --d, --delete=: delete the given file --a, --delete-all: delete all files -""" -from clap import OptionParser - -def print_(color, txt): - code = {'black': 30, 'red': 31}[color] - print '\x1b[%dm%s\x1b[0m' % (code, txt) - -if __name__=='__main__': - opt = OptionParser(__doc__).parse_args() - if not opt: - OptionParser.exit() - elif opt.delete_all: - print_(opt.color, "Delete all files") - elif opt.delete: - print_(opt.color, "Delete the file %s" % opt.delete) diff --git a/clap/example.txt b/clap/example.txt deleted file mode 100644 index 4cf83ff..0000000 --- a/clap/example.txt +++ /dev/null @@ -1,31 +0,0 @@ -$ python example.py -usage: example.py [options] --c, --color=black: set default color --d, --delete=: delete the given file --a, --delete-all: delete all files - -$ python example.py -h -Usage: example.py [options] - -Options: - -h, --help show this help message and exit - -c COLOR, --color=COLOR - set default color - -d DELETE, --delete=DELETE - delete the given file - -a, --delete_all delete all files - -$ python example.py -d -Usage: example.py [options] - -example.py: error: -d option requires an argument - -$ python example.py --delete x.txt -Delete the file x.txt - -$ python example.py --delete-all -Delete all files - -$ python example.py -a --color=red -Delete all files # this is printed in red on most terminals - diff --git a/clap/README.txt b/plac/README.txt index 06c9ce3..50ac30c 100644 --- a/clap/README.txt +++ b/plac/README.txt @@ -1,11 +1,11 @@ -clap, the easiest command line arguments parser in the world +plac, the easiest command line arguments parser in the world ============================================================ :Author: Michele Simionato :E-mail: michele.simionato@gmail.com :Requires: Python 2.3+ -:Download page: http://pypi.python.org/pypi/clap -:Installation: ``easy_install clap`` +:Download page: http://pypi.python.org/pypi/plac +:Installation: ``easy_install plac`` :License: BSD license Installation @@ -13,7 +13,7 @@ Installation If you are lazy, just perform -$ easy_install clap +$ easy_install plac which will install just the module on your system. Notice that Python 3 requires the easy_install version of the distribute_ project. @@ -25,7 +25,7 @@ $ python setup.py install in the main directory, possibly as superuser. -.. _tarball: http://pypi.python.org/pypi/clap +.. _tarball: http://pypi.python.org/pypi/plac .. _distribute: http://packages.python.org/distribute/ Testing @@ -33,20 +33,20 @@ Testing Run -$ python test_clap.py +$ python test_plac.py or -$ nosetests test_clap +$ nosetests test_plac or -$ py.test test_clap +$ py.test test_plac Documentation -------------- You can choose between the `HTML version`_ and the `PDF version`_ . -.. _HTML version: http://micheles.googlecode.com/hg/clap/documentation.html -.. _PDF version: http://micheles.googlecode.com/hg/clap/documentation.pdf +.. _HTML version: http://micheles.googlecode.com/hg/plac/doc/plac.html +.. _PDF version: http://micheles.googlecode.com/hg/plac/doc/plac.pdf diff --git a/clap/clap.py b/plac/plac.py index 625092b..fdde35f 100644 --- a/clap/clap.py +++ b/plac/plac.py @@ -24,9 +24,11 @@ ## DAMAGE. """ -clap, the easiest Command Line Arguments Parser in the world. -See clap/doc.html for the documentation. +plac, the easiest Command Line Arguments Parser in the world. +See plac/doc.html for the documentation. """ +# this module should be kept Python 2.3 compatible + __version__ = '0.2.0' import re, sys, inspect, argparse @@ -82,7 +84,6 @@ class Annotation(object): self.choices = choices self.metavar = metavar - @classmethod def from_(cls, obj): "Helper to convert an object into an annotation, if needed" if is_annotation(obj): @@ -90,14 +91,23 @@ class Annotation(object): elif hasattr(obj, '__iter__') and not isinstance(obj, str): return cls(*obj) return cls(obj) - + from_ = classmethod(from_) + NONE = object() # sentinel use to signal the absence of a default valid_attrs = getfullargspec(argparse.ArgumentParser.__init__).args[1:] def parser_from(func): - # extract the ArgumentParser arguments from the attributes of func - attrs = dict([(n, v) for n, v in vars(func).items() if n in valid_attrs]) + """ + Extract the arguments from the attributes of the passed function and + return an ArgumentParser instance. + """ + short_prefix = getattr(func, 'short_prefix', '-') + long_prefix = getattr(func, 'long_prefix', '--') + attrs = {'description': func.__doc__} + for n, v in vars(func).items(): + if n in valid_attrs: + attrs[n] = v p = argparse.ArgumentParser(**attrs) f = p.argspec = getfullargspec(func) defaults = f.defaults or () @@ -106,20 +116,22 @@ def parser_from(func): alldefaults = (NONE,) * (n_args - n_defaults) + defaults for name, default in zip(f.args, alldefaults): a = Annotation.from_(f.annotations.get(name, ())) + if default is NONE: + dflt, metavar = None, a.metavar + else: + dflt, metavar = default, a.metavar or str(default) if a.kind in ('option', 'flag'): - short = '-' + a.abbrev - long = '--' + name - elif default is NONE: # mandatory positional argument + short = short_prefix + a.abbrev + long = long_prefix + name + elif default is NONE: # required argument p.add_argument(name, help=a.help, type=a.type, choices=a.choices, - metavar=a.metavar) - else: # regular default argument - p.add_argument(name, nargs='?', help=a.help, default=default, - type=a.type, choices=a.choices, metavar=a.metavar) + metavar=metavar) + else: # default argument + p.add_argument(name, nargs='?', help=a.help, default=dflt, + type=a.type, choices=a.choices, metavar=metavar) if a.kind == 'option': - if default is not NONE: - raise TypeError('Option %r does not want a default' % name) - p.add_argument(short, long, help=a.help, type=a.type, - choices=a.choices, metavar=a.metavar) + p.add_argument(short, long, help=a.help, default=dflt, + type=a.type, choices=a.choices, metavar=metavar) elif a.kind == 'flag': if default is not NONE: raise TypeError('Flag %r does not want a default' % name) diff --git a/plac/setup.py b/plac/setup.py new file mode 100644 index 0000000..ca34c5b --- /dev/null +++ b/plac/setup.py @@ -0,0 +1,29 @@ +try: + from setuptools import setup +except ImportError: + from distutils.core import setup +import os.path +import plac + +if __name__ == '__main__': + setup(name=plac.__name__, + version=plac.__version__, + description='The easiest command line arguments parser in the world', + long_description=open('README.txt').read(), + author='Michele Simionato', + author_email='michele.simionato@gmail.com', + url='http://pypi.python.org/pypi/plac', + license="BSD License", + py_modules = ['plac'], + install_requires=['argparse>=1.1'], + keywords="command line arguments parser", + platforms=["All"], + classifiers=['Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Libraries', + 'Topic :: Utilities'], + zip_safe=False) diff --git a/clap/test_clap.py b/plac/test_plac.py index 04a313d..c961759 100644 --- a/clap/test_clap.py +++ b/plac/test_plac.py @@ -3,34 +3,36 @@ The tests are runnable with nose, with py.test, or even as standalone script """ import sys -import clap3 +import plac -def expect_exit(func, *args, **kw): +def expect(errclass, func, *args, **kw): try: func(*args, **kw) - except SystemExit: + except errclass: pass else: - raise RuntimeError('SystemExit expected, got none!') + raise RuntimeError('%s expected, got none!', errclass.__name__) -def f1(delete: "d: delete a file", args=()): - pass +def parser_from(f, **kw): + f.__annotations__ = kw + return plac.parser_from(f) + +p1 = parser_from(lambda delete, *args: None, + delete=('delete a file', 'option', 'd')) def test_p1(): - p1 = clap3.parser_from(f1) arg = p1.parse_args(['-d', 'foo', 'arg1', 'arg2']) assert arg.delete == 'foo' assert arg.args == ['arg1', 'arg2'] arg = p1.parse_args([]) assert arg.delete is None, arg.delete - assert arg.args is (), arg.args + assert arg.args == [], arg.args -def f2(arg1, delete: "d: delete a file", args=()): - pass +p2 = parser_from(lambda arg1, delete, *args: None, + delete=('delete a file', 'option', 'd')) def test_p2(): - p2 = clap3.parser_from(f2) arg = p2.parse_args(['-d', 'foo', 'arg1', 'arg2']) assert arg.delete == 'foo', arg.delete assert arg.arg1 == 'arg1', arg.arg1 @@ -38,30 +40,29 @@ def test_p2(): arg = p2.parse_args(['arg1']) assert arg.delete is None, arg.delete - assert arg.args is (), arg.args + assert arg.args == [], arg.args assert arg, arg - expect_exit(p2.parse_args, []) + expect(SystemExit, p2.parse_args, []) + -def f3(arg1, delete: "d: delete a file"): - pass +p3 = parser_from(lambda arg1, delete: None, + delete=('delete a file', 'option', 'd')) def test_p3(): - p3 = clap3.parser_from(f3) arg = p3.parse_args(['arg1']) assert arg.delete is None, arg.delete assert arg.arg1 == 'arg1', arg.args - expect_exit(p3.parse_args, ['arg1', 'arg2']) - expect_exit(p3.parse_args, []) + expect(SystemExit, p3.parse_args, ['arg1', 'arg2']) + expect(SystemExit, p3.parse_args, []) -def f4(delete: "d: delete a file", - delete_all: "a, delete all files", - color: "c: set default color"="black"): - pass +p4 = parser_from(lambda delete, delete_all, color="black": None, + delete=('delete a file', 'option', 'd'), + delete_all=('delete all files', 'flag', 'a'), + color=('color', 'option', 'c')) def test_p4(): - p4 = clap3.parser_from(f4) arg = p4.parse_args(['-a']) assert arg.delete_all is True, arg.delete_all @@ -73,11 +74,15 @@ def test_p4(): arg = p4.parse_args(['--color=red']) assert arg.color == 'red' +def test_flag_with_default(): + expect(TypeError, parser_from, lambda yes_or_no='no': None, + yes_or_no=('A yes/no flag', 'flag', 'f')) + if __name__ == '__main__': n = 0 - for name, test in list(globals().items()): + for name, test in sorted(globals().items()): if name.startswith('test_'): - print('Running', name) + print('Running ' + name) test() n +=1 print('Executed %d tests OK' % n) |