diff options
Diffstat (limited to 'plac/doc/plac.html')
-rw-r--r-- | plac/doc/plac.html | 573 |
1 files changed, 282 insertions, 291 deletions
diff --git a/plac/doc/plac.html b/plac/doc/plac.html index a973666..6339cc3 100644 --- a/plac/doc/plac.html +++ b/plac/doc/plac.html @@ -3,7 +3,7 @@ <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/" /> +<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" /> <title>Plac: Parsing the Command Line the Easy Way</title> <meta name="author" content="Michele Simionato" /> <style type="text/css"> @@ -428,7 +428,7 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { </tr> <tr class="field"><th class="docinfo-name">Project page:</th><td class="field-body"><a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">http://micheles.googlecode.com/hg/plac/doc/plac.html</a></td> </tr> -<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install plac</tt></td> +<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal"><span class="pre">easy_install</span> <span class="pre">-U</span> <span class="pre">plac</span></tt></td> </tr> <tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td> </tr> @@ -440,18 +440,16 @@ h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { <li><a class="reference internal" href="#the-importance-of-scaling-down" id="id1">The importance of scaling down</a></li> <li><a class="reference internal" href="#scripts-with-required-arguments" id="id2">Scripts with required arguments</a></li> <li><a class="reference internal" href="#scripts-with-default-arguments" id="id3">Scripts with default arguments</a></li> -<li><a class="reference internal" href="#scripts-with-options" id="id4">Scripts with options</a></li> +<li><a class="reference internal" href="#scripts-with-options-and-smart-options" id="id4">Scripts with options (and smart options)</a></li> <li><a class="reference internal" href="#scripts-with-flags" id="id5">Scripts with flags</a></li> <li><a class="reference internal" href="#plac-for-python-2-x-users" id="id6">plac for Python 2.X users</a></li> <li><a class="reference internal" href="#more-features" id="id7">More features</a></li> -<li><a class="reference internal" href="#keyword-arguments" id="id8">Keyword arguments</a></li> -<li><a class="reference internal" href="#a-realistic-example" id="id9">A realistic example</a></li> -<li><a class="reference internal" href="#advanced-usage" id="id10">Advanced usage</a></li> -<li><a class="reference internal" href="#custom-annotation-objects" id="id11">Custom annotation objects</a></li> -<li><a class="reference internal" href="#plac-vs-argparse" id="id12">plac vs argparse</a></li> -<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id13">plac vs the rest of the world</a></li> -<li><a class="reference internal" href="#the-future" id="id14">The future</a></li> -<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id15">Trivia: the story behind the name</a></li> +<li><a class="reference internal" href="#a-realistic-example" id="id8">A realistic example</a></li> +<li><a class="reference internal" href="#keyword-arguments" id="id9">Keyword arguments</a></li> +<li><a class="reference internal" href="#plac-vs-argparse" id="id10">plac vs argparse</a></li> +<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id11">plac vs the rest of the world</a></li> +<li><a class="reference internal" href="#the-future" id="id12">The future</a></li> +<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id13">Trivia: the story behind the name</a></li> </ul> </div> <div class="section" id="the-importance-of-scaling-down"> @@ -523,7 +521,7 @@ if __name__ == '__main__': sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv[2:])) </pre> -<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (nine lines) +<p>As you see the whole <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__'</span></tt> block (nine lines) is essentially boilerplate that should not exists. Actually I think the language should recognize the main function and pass to it the command line arguments automatically; unfortunaly this is unlikely to @@ -573,13 +571,18 @@ if __name__ == '__main__': underlying <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module) a nice usage message:</p> <pre class="literal-block"> $ python example3.py -h +</pre> +<pre class="literal-block"> usage: example3.py [-h] dsn +Do something with the database + positional arguments: dsn optional arguments: -h, --help show this help message and exit + </pre> <p>This is only the tip of the iceberg: <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to do much more than that.</p> </div> @@ -607,12 +610,12 @@ if __name__ == '__main__': </pre> <p>Here I want to perform a query on a database table, by extracting the -today's data: it makes sense for <tt class="docutils literal">today</tt> to be a default argument. -If there is a most used table (in this example a table called <tt class="docutils literal">'product'</tt>) +today's data: it makes sense for <tt class="docutils literal"><span class="pre">today</span></tt> to be a default argument. +If there is a most used table (in this example a table called <tt class="docutils literal"><span class="pre">'product'</span></tt>) it also makes sense to make it a default argument. Performing the parsing of the command lines arguments by hand takes 8 ugly lines of boilerplate (using <a class="reference external" href="http://argparse.googlecode.com">argparse</a> would require about the same number of lines). -With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal">__main__</tt> block reduces to the usual two lines:</p> +With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal"><span class="pre">__main__</span></tt> block reduces to the usual two lines:</p> <pre class="literal-block"> if __name__ == '__main__': import plac; plac.call(main) @@ -637,7 +640,7 @@ optional arguments: variable number of arguments. Here is an example, a script running on a database a series of SQL scripts:</p> <pre class="literal-block"> -# example6.py +# example7.py from datetime import datetime def main(dsn, *scripts): @@ -647,15 +650,10 @@ def main(dsn, *scripts): # ... 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:]) + import plac; plac.call(main) </pre> -<p>Using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, you can just replace the <tt class="docutils literal">__main__</tt> block with the -usual two lines (I have defined an Emacs keybinding for them) -and then you get the following nice usage message:</p> +<p>Here is the usage message:</p> <pre class="literal-block"> usage: example7.py [-h] dsn [scripts [scripts ...]] @@ -673,9 +671,48 @@ optional arguments: the command line arguments parser to use from the signature of the main function</em>. This is the whole idea behind <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: if the intent is clear, let's the machine take care of the details.</p> +<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, in the sense that it +delivers the programmer from the burden of writing the parser, but is +less of a hack: instead of extracting the parser from the docstring of +the module, it extracts it from the signature of the <tt class="docutils literal"><span class="pre">main</span></tt> +function.</p> +<p>The idea comes from the <cite>function annotations</cite> concept, a new +feature of Python 3. An example is worth a thousand words, so here +it is:</p> +<pre class="literal-block"> +# example7_.py +from datetime import datetime + +def main(dsn: "Database dsn", *scripts: "SQL scripts"): + "Run the given scripts on the database" + for script in scripts: + print('executing %s' % script) + # ... + +if __name__ == '__main__': + import plac; plac.call(main) + +</pre> +<p>Here the arguments of the <tt class="docutils literal"><span class="pre">main</span></tt> function have been annotated with +strings which are intented to be used in the help message:</p> +<pre class="literal-block"> +usage: example7_.py [-h] dsn [scripts [scripts ...]] + +Run the given scripts on the database + +positional arguments: + dsn Database dsn + scripts SQL scripts + +optional arguments: + -h, --help show this help message and exit + +</pre> +<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize much more complex annotations, as +I will show in the next paragraphs.</p> </div> -<div class="section" id="scripts-with-options"> -<h1><a class="toc-backref" href="#id4">Scripts with options</a></h1> +<div class="section" id="scripts-with-options-and-smart-options"> +<h1><a class="toc-backref" href="#id4">Scripts with options (and smart options)</a></h1> <p>It is surprising how few command line scripts with options I have written over the years (probably less than a hundred), compared to the number of scripts with positional arguments I wrote (certainly more @@ -685,15 +722,8 @@ comes to specifying the options and frankly I have never used them directly. Instead, I have always relied on an old recipe of mine, the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, which provides a convenient wrapper over <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a>. Alternatively, in the simplest cases, I have just -performed the parsing by hand.</p> -<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to the <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, in the sense that it -delivers the programmer from the burden of writing the parser, but is -less of a hack: instead of extracting the parser from the docstring of -the module, it extracts it from the signature of the <tt class="docutils literal">main</tt> -function.</p> -<p>The idea comes from the <cite>function annotations</cite> concept, a new -feature of Python 3. An example is worth a thousand words, so here -it is:</p> +performed the parsing by hand. In <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the parser is inferred by the +function annotations. Here is an example:</p> <pre class="literal-block"> # example8.py def main(command: ("SQL query", 'option', 'c'), dsn): @@ -705,13 +735,12 @@ if __name__ == '__main__': import plac; plac.call(main) </pre> -<p>As you see, the argument <tt class="docutils literal">command</tt> has been annotated with the tuple -<tt class="docutils literal">("SQL query", 'option', 'c')</tt>: the first string is the help string -which will appear in the usage message, the second string tell <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> -that <tt class="docutils literal">command</tt> is an option and the third string that it can be -abbreviated with the letter <tt class="docutils literal">c</tt>. Of course, the long option format -(<tt class="docutils literal"><span class="pre">--command=</span></tt>) comes from the argument name. The resulting usage -message is the following:</p> +<p>Here the argument <tt class="docutils literal"><span class="pre">command</span></tt> has been annotated with the tuple +<tt class="docutils literal"><span class="pre">("SQL</span> <span class="pre">query",</span> <span class="pre">'option',</span> <span class="pre">'c')</span></tt>: the first string is the help string +which will appear in the usage message, the second string tells <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> +that <tt class="docutils literal"><span class="pre">command</span></tt> is an option and the third string that there is also +a short form of the option <tt class="docutils literal"><span class="pre">-c</span></tt>, the long form being <tt class="docutils literal"><span class="pre">--command=</span></tt>. +The usage message is the following:</p> <pre class="literal-block"> usage: example8.py [-h] [-c COMMAND] dsn @@ -720,7 +749,7 @@ positional arguments: optional arguments: -h, --help show this help message and exit - -c, --command COMMAND + -c COMMAND, --command COMMAND SQL query </pre> @@ -732,62 +761,84 @@ 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>. It is possible to specify a non-trivial -default for an option. Here is an example:</p> +<p>The third argument in the function annotation can be omitted: in such +case it will be assumed to be <tt class="docutils literal"><span class="pre">None</span></tt>. The consequence is that +the usual dichotomy between long and short options (GNU-style options) +disappears: we get <em>smart options</em>, which have the single character prefix +of short options and behave like both long and short options, since +they can be abbreviated. Here is an example featuring smart options:</p> <pre class="literal-block"> -# example8_.py -def main(dsn, command: ("SQL query", 'option', 'c')='select * from table'): +# example6.py +def main(dsn, command: ("SQL query", 'option')): print('executing %r on %s' % (command, dsn)) if __name__ == '__main__': import plac; plac.call(main) </pre> -<p>Now if you do not pass the <tt class="docutils literal">command option</tt>, the -default query will be executed:</p> <pre class="literal-block"> -$ python3 example8_.py dsn -executing 'select * from table' on dsn +usage: example6.py [-h] [-command COMMAND] dsn + +positional arguments: + dsn + +optional arguments: + -h, --help show this help message and exit + -command COMMAND SQL query + </pre> -<p>Positional argument can be annotated too:</p> +<p>The following are all valid invocations ot the script:</p> <pre class="literal-block"> -def main(command: ("SQL query", 'option', 'c'), - dsn: ("Database dsn", 'positional', None)): - ... +$ python3 example6.py -c "select" dsn +executing 'select' on dsn +$ python3 example6.py -com "select" dsn +executing 'select' on dsn +$ python3 example6.py -command="select" dsn +executing 'select' on dsn </pre> -<p>Of course explicit is better than implicit, an no special cases are -special enough, but sometimes practicality beats purity, so <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is -able to use smart defaults; in particular you can omit the third argument -and write:</p> +<p>Notice that the form <tt class="docutils literal"><span class="pre">-command=SQL</span></tt> is recognized only for the full +option, not for its abbreviations:</p> <pre class="literal-block"> -def main(command: ("SQL query", 'option'), - dsn: ("Database dsn", 'positional')): - ... +$ python3 example6.py -com="select" dsn +usage: example6.py [-h] [-command COMMAND] dsn +example6.py: error: unrecognized arguments: -com=select </pre> -<p>When omitted, the third argument is assumed to be the first letter of -the variable name for options and flags, and <tt class="docutils literal">None</tt> for positional -arguments. Moreover, smart enough to convert help messages into tuples; -in other words, you can just write <tt class="docutils literal">"Database dsn"</tt> instead of -<tt class="docutils literal">("Database dsn", 'positional')</tt>.</p> -<p>I should notice that varargs (starred-arguments) can be annotated too; -here is an example:</p> +<p>If the option is not passed, the variable <tt class="docutils literal"><span class="pre">command</span></tt> +will get the value <tt class="docutils literal"><span class="pre">None</span></tt>. However, it is possible to specify a non-trivial +default. Here is an example:</p> <pre class="literal-block"> -def main(dsn: "Database dsn", *scripts: "SQL scripts"): - ... +# example8_.py +def main(dsn, command: ("SQL query", 'option')='select * from table'): + print('executing %r on %s' % (command, dsn)) + +if __name__ == '__main__': + import plac; plac.call(main) + </pre> -<p>This is a valid signature for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>, which will recognize the help strings -for both <tt class="docutils literal">dsn</tt> and <tt class="docutils literal">scripts</tt>:</p> +<p>Notice that the default value appears in the help message:</p> <pre class="literal-block"> +usage: example8_.py [-h] [-command select * from table] dsn + positional arguments: - dsn Database dsn - scripts SQL scripts + dsn + +optional arguments: + -h, --help show this help message and exit + -command select * from table + SQL query + +</pre> +<p>When you run the script and you do not pass the <tt class="docutils literal"><span class="pre">-command</span></tt> option, the +default query will be executed:</p> +<pre class="literal-block"> +$ python3 example8_.py dsn +executing 'select * from table' on dsn </pre> </div> <div class="section" id="scripts-with-flags"> <h1><a class="toc-backref" href="#id5">Scripts with flags</a></h1> -<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> also recognizes flags, i.e. boolean options which are -<tt class="docutils literal">True</tt> if they are passed to the command line and <tt class="docutils literal">False</tt> +<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize flags, i.e. boolean options which are +<tt class="docutils literal"><span class="pre">True</span></tt> if they are passed to the command line and <tt class="docutils literal"><span class="pre">False</span></tt> if they are absent. Here is an example:</p> <pre class="literal-block"> # example9.py @@ -817,12 +868,12 @@ $ python3 example9.py -v dsn connecting to dsn </pre> <p>Notice that it is an error trying to specify a default for flags: the -default value for a flag is always <tt class="docutils literal">False</tt>. If you feel the need to +default value for a flag is always <tt class="docutils literal"><span class="pre">False</span></tt>. If you feel the need to implement non-boolean flags, you should use an option with two choices, as explained in the "more features" section.</p> <p>For consistency with the way the usage message is printed, I suggest you to follow the Flag-Option-Required-Default (FORD) convention: in -the <tt class="docutils literal">main</tt> function write first the flag arguments, then the option +the <tt class="docutils literal"><span class="pre">main</span></tt> function write first the flag arguments, then the option arguments, then the required arguments and finally the default arguments. This is just a convention and you are not forced to use it, except for the default arguments (including the varargs) which must @@ -852,7 +903,7 @@ main.__annotations__ = dict( <p>One should be careful to match the keys of the annotation dictionary with the names of the arguments in the annotated function; for lazy people with Python 2.4 available the simplest way is to use the -<tt class="docutils literal">plac.annotations</tt> decorator that performs the check for you:</p> +<tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator that performs the check for you:</p> <pre class="literal-block"> @plac.annotations( dsn="Database dsn", @@ -861,30 +912,30 @@ def main(dsn, *scripts): ... </pre> <p>In the rest of this article I will assume that you are using Python 2.X with -<tt class="docutils literal">X >= 4</tt> and I will use the <tt class="docutils literal">plac.annotations</tt> decorator. Notice however -that the tests for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> runs even on Python 2.3.</p> +X >= 4 and I will use the <tt class="docutils literal"><span class="pre">plac.annotations</span></tt> decorator. Notice however +that <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> runs even on Python 2.3.</p> </div> <div class="section" id="more-features"> <h1><a class="toc-backref" href="#id7">More features</a></h1> -<p>Even if one of the goals of plac is to have a learning curve of -<em>minutes</em>, compared to the learning curve of <em>hours</em> of -<a class="reference external" href="http://argparse.googlecode.com">argparse</a>, it does not mean that I have removed all the features of -<a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually a lot of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> power persists in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Until -now, I have only showed simple annotations, but in general an -annotation is a 5-tuple of the form</p> +<p>One of the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is to have a learning curve of <em>minutes</em> for +its core features, compared to the learning curve of <em>hours</em> of +<a class="reference external" href="http://argparse.googlecode.com">argparse</a>. In order to reach this goal, I have <em>not</em> sacrificed all +the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually a lot of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> power persists +in <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Until now, I have only showed simple annotations, but in +general an annotation is a 6-tuple of the form</p> <blockquote> -<tt class="docutils literal">(help, kind, abbrev, type, choices, metavar)</tt></blockquote> -<p>where <tt class="docutils literal">help</tt> is the help message, <tt class="docutils literal">kind</tt> is a string in the set { -<tt class="docutils literal">"flag"</tt>, <tt class="docutils literal">"option"</tt>, <tt class="docutils literal">"positional"</tt>}, <tt class="docutils literal">abbrev</tt> is a -one-character string, <tt class="docutils literal">type</tt> is a callable taking a string in input, -<tt class="docutils literal">choices</tt> is a discrete sequence of values and <tt class="docutils literal">metavar</tt> is a string.</p> -<p><tt class="docutils literal">type</tt> is used to automagically convert the command line arguments +<tt class="docutils literal"><span class="pre">(help,</span> <span class="pre">kind,</span> <span class="pre">abbrev,</span> <span class="pre">type,</span> <span class="pre">choices,</span> <span class="pre">metavar)</span></tt></blockquote> +<p>where <tt class="docutils literal"><span class="pre">help</span></tt> is the help message, <tt class="docutils literal"><span class="pre">kind</span></tt> is a string in the set { +<tt class="docutils literal"><span class="pre">"flag"</span></tt>, <tt class="docutils literal"><span class="pre">"option"</span></tt>, <tt class="docutils literal"><span class="pre">"positional"</span></tt>}, <tt class="docutils literal"><span class="pre">abbrev</span></tt> is a +one-character string, <tt class="docutils literal"><span class="pre">type</span></tt> is a callable taking a string in input, +<tt class="docutils literal"><span class="pre">choices</span></tt> is a discrete sequence of values and <tt class="docutils literal"><span class="pre">metavar</span></tt> is a string.</p> +<p><tt class="docutils literal"><span class="pre">type</span></tt> is used to automagically convert the command line arguments from the string type to any Python type; by default there is no -convertion and <tt class="docutils literal">type=None</tt>.</p> -<p><tt class="docutils literal">choices</tt> is used to restrict the number of the valid -options; by default there is no restriction i.e. <tt class="docutils literal">choices=None</tt>.</p> -<p><tt class="docutils literal">metavar</tt> is used to change the argument name in the usage message -(and only there); by default the metavar is <tt class="docutils literal">None</tt>: this means that +conversion and <tt class="docutils literal"><span class="pre">type=None</span></tt>.</p> +<p><tt class="docutils literal"><span class="pre">choices</span></tt> is used to restrict the number of the valid +options; by default there is no restriction i.e. <tt class="docutils literal"><span class="pre">choices=None</span></tt>.</p> +<p><tt class="docutils literal"><span class="pre">metavar</span></tt> is used to change the argument name in the usage message +(and only there); by default the metavar is <tt class="docutils literal"><span class="pre">None</span></tt>: this means that the name in the usage message is the same as the argument name, unless the argument has a default and in such a case is equal to the stringified form of the default.</p> @@ -903,12 +954,19 @@ def main(operator, *numbers): result = dict(add=0.0, mul=1.0)[operator] for n in numbers: result = op(result, n) - print(result) + return result if __name__ == '__main__': - plac.call(main) + print(plac.call(main)[0]) </pre> +<p>Usually the main function of a script works by side effects and returns +<tt class="docutils literal"><span class="pre">None</span></tt>; in this example instead I choose to return the number and to +print it in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block.</p> +<p>Notice that <em>plac.call returns a list of strings</em>: in particular, it +returns a single-element list if the main function returns a single +non-None element (as in this example) or an empty list if the main +function returns <tt class="docutils literal"><span class="pre">None</span></tt>.</p> <p>Here is the usage:</p> <pre class="literal-block"> usage: example10.py [-h] {add,mul} [n [n ...]] @@ -923,7 +981,7 @@ optional arguments: -h, --help show this help message and exit </pre> -<p>Notice that the docstring of the <tt class="docutils literal">main</tt> function has been automatically added +<p>Notice that the docstring of the <tt class="docutils literal"><span class="pre">main</span></tt> function has been automatically added to the usage message. Here are a couple of examples of use:</p> <pre class="literal-block"> $ python example10.py add 1 2 3 4 @@ -934,63 +992,38 @@ $ python example10.py ad 1 2 3 4 # a mispelling error usage: example10.py [-h] {add,mul} [n [n ...]] example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul') </pre> -</div> -<div class="section" id="keyword-arguments"> -<h1><a class="toc-backref" href="#id8">Keyword arguments</a></h1> -<p>Starting from release 0.4, if your main function has keyword arguments, -<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> recognizes arguments of the form <tt class="docutils literal">"name=value"</tt> in the command line. -Here is an example:</p> -<pre class="literal-block"> -# example12.py -import plac - -@plac.annotations( - opt=('some option', 'option'), - args='default arguments', - kw='keyword arguments') -def main(opt, *args, **kw): - print(opt, args, kw) - -if __name__ == '__main__': - plac.call(main) - -</pre> -<p>Here is the generated usage message:</p> -<pre class="literal-block"> -usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]] - -positional arguments: - args default arguments - kw keyword arguments - -optional arguments: - -h, --help show this help message and exit - -o, --opt OPT some option - -</pre> -<p>Here is how you call the script:</p> -<pre class="literal-block"> -$ python example12.py 1 2 kw1=1 kw2=2 --opt=0 -('0', ('1', '2'), {'kw1': '1', 'kw2': '2'}) +<p>If the main function returns a generic number of elements, +the elements returned by <tt class="docutils literal"><span class="pre">plac.call</span></tt> are stringified by invoking +<tt class="docutils literal"><span class="pre">str</span></tt> on each of them. +The reason is to simplify testing: a plac-based +command-line interface can be tested by simply comparing lists of +strings in input and lists of strings in output. +For instance a doctest may look like this:</p> +<pre class="doctest-block"> +>>> import example10 +>>> plac.call(example10.main, ['add', '1', '2']) +['3.0'] </pre> -<p>When using keyword arguments, one must be careful to use names which -are not alreay taken; for instance in this examples the name <tt class="docutils literal">opt</tt> -is taken:</p> -<pre class="literal-block"> -$ python example12.py 1 2 kw1=1 kw2=2 opt=0 -usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]] -example12.py: error: colliding keyword arguments: opt +<p><tt class="docutils literal"><span class="pre">plac.call</span></tt> works for generators too:</p> +<pre class="doctest-block"> +>>> def main(n): +... for i in range(int(n)): +... yield i +>>> plac.call(main, ['3']) +['0', '1', '2'] </pre> -<p>The names taken are the names of the flags, of the options, and of the -positional arguments, excepted varargs and keywords. This limitation -is a consequence of the way the argument names are managed in function calls -by the Python language.</p> +<p>However, you should notice that <tt class="docutils literal"><span class="pre">plac.call</span></tt> is <em>eager</em>, not lazy: +the generator is exhausted by the call. This behavior avoids mistakes like +forgetting of applying <tt class="docutils literal"><span class="pre">list(result)</span></tt> to the result of a <tt class="docutils literal"><span class="pre">plac.call</span></tt>.</p> +<p>This behavior makes testing easier and supports the <em>yield-is-print</em> +pattern: just replace the occurrences of <tt class="docutils literal"><span class="pre">print</span></tt> with <tt class="docutils literal"><span class="pre">yield</span></tt> in +the main function and you will get an easy to test interface.</p> </div> <div class="section" id="a-realistic-example"> -<h1><a class="toc-backref" href="#id9">A realistic example</a></h1> +<h1><a class="toc-backref" href="#id8">A realistic example</a></h1> <p>Here is a more realistic script using most of the features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to run SQL queries on a database by relying on <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a>. Notice the usage -of the <tt class="docutils literal">type</tt> feature to automagically convert a SQLAlchemy connection +of the <tt class="docutils literal"><span class="pre">type</span></tt> feature to automagically convert a SQLAlchemy connection string into a <a class="reference external" href="http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html">SqlSoup</a> object:</p> <pre class="literal-block"> # dbcli.py @@ -1006,21 +1039,31 @@ from sqlalchemy.ext.sqlsoup import SqlSoup ) def main(db, header, sqlcmd, delimiter="|", *scripts): "A script to run queries and SQL scripts on a database" - print('Working on %s' % db.bind.url) + yield 'Working on %s' % db.bind.url + if sqlcmd: result = db.bind.execute(sqlcmd) if header: # print the header - print(delimiter.join(result.keys())) + yield delimiter.join(result.keys()) for row in result: # print the rows - print(delimiter.join(map(str, row))) + yield delimiter.join(map(str, row)) for script in scripts: db.bind.execute(file(script).read()) + yield 'executed %s' % script if __name__ == '__main__': - plac.call(main) + for output in plac.call(main): + print(output) </pre> +<p>You can see the <em>yield-is-print</em> pattern here: instead of using +<tt class="docutils literal"><span class="pre">print</span></tt> in the main function, we use <tt class="docutils literal"><span class="pre">yield</span></tt>, and we perform the +print in the <tt class="docutils literal"><span class="pre">__main__</span></tt> block. The advantage of the pattern is that +the test becomes trivial: had we performed the printing in the main +function, tje test would have involved redirecting <tt class="docutils literal"><span class="pre">sys.stdout</span></tt> to a +<tt class="docutils literal"><span class="pre">StringIO</span></tt> object and we know that redirecting <tt class="docutils literal"><span class="pre">sys.stdout</span></tt> is +always ugly, especially in multithreaded situations.</p> <p>Here is the usage message:</p> <pre class="literal-block"> usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]] @@ -1028,146 +1071,90 @@ usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]] A script to run queries and SQL scripts on a database positional arguments: - db Connection string - scripts SQL scripts + db Connection string + scripts SQL scripts optional arguments: - -h, --help show this help message and exit - -H, --header Header - -c, --sqlcmd SQL SQL command - -d, --delimiter | Column separator + -h, --help show this help message and exit + -H, --header Header + -c SQL, --sqlcmd SQL SQL command + -d |, --delimiter | Column separator </pre> +<p>I leave as an exercise for the reader to write doctests for this +example.</p> </div> -<div class="section" id="advanced-usage"> -<h1><a class="toc-backref" href="#id10">Advanced usage</a></h1> -<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> relies on a <a class="reference external" href="http://argparse.googlecode.com">argparse</a> for all of the heavy lifting work and it is -possible to leverage on <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features directly or indirectly.</p> -<p>For instance, you can make invisible an argument in the usage message -simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or -<tt class="docutils literal">argparse.SUPPRESS</tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a> -directly.</p> -<p>It is also possible to pass options to the underlying -<tt class="docutils literal">argparse.ArgumentParser</tt> object (currently it accepts the default -arguments <tt class="docutils literal">description</tt>, <tt class="docutils literal">epilog</tt>, <tt class="docutils literal">prog</tt>, <tt class="docutils literal">usage</tt>, -<tt class="docutils literal">add_help</tt>, <tt class="docutils literal">argument_default</tt>, <tt class="docutils literal">parents</tt>, <tt class="docutils literal">prefix_chars</tt>, -<tt class="docutils literal">fromfile_prefix_chars</tt>, <tt class="docutils literal">conflict_handler</tt>, <tt class="docutils literal">formatter_class</tt>). -It is enough to set such attributes on the <tt class="docutils literal">main</tt> function. For -instance</p> -<pre class="literal-block"> -def main(...): - pass - -main.add_help = False -</pre> -<p>disable the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This is not -particularly elegant, but I assume the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be -happy with the defaults and would not want to change them; still it is -possible if she wants to. For instance, by setting the <tt class="docutils literal">description</tt> -attribute, it is possible to add a comment to the usage message (by -default the docstring of the <tt class="docutils literal">main</tt> function is used as -description). It is also possible to change the option prefix; for -instance if your script must run under Windows and you want to use "/" -as option prefix you can add the lines:</p> -<pre class="literal-block"> -main.prefix_chars='-/' -main.short_prefix = '/' -</pre> -<p>The recognition of the <tt class="docutils literal">short_prefix</tt> attribute is a <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> -extension; there is also a companion <tt class="docutils literal">long_prefix</tt> attribute with -default value of <tt class="docutils literal"><span class="pre">"--"</span></tt>. <tt class="docutils literal">prefix_chars</tt> is an <a class="reference external" href="http://argparse.googlecode.com">argparse</a> feature. -Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> to -understand the meaning of the other options. If there is a set of -options that you use very often, you may consider writing a decorator -adding such options to the <tt class="docutils literal">main</tt> function for you. For simplicity, -<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic of that kind.</p> -<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by -invoking the <tt class="docutils literal">plac.parser_from</tt> utility function:</p> -<pre class="doctest-block"> ->>> import plac ->>> def main(arg): -... pass -... ->>> print plac.parser_from(main) -ArgumentParser(prog='', usage=None, description=None, version=None, -formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', -add_help=True) -</pre> -<p>I use <tt class="docutils literal">plac.parser_from</tt> in the unit tests of the module, but regular -users should never need to use it.</p> -</div> -<div class="section" id="custom-annotation-objects"> -<h1><a class="toc-backref" href="#id11">Custom annotation objects</a></h1> -<p>Internally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> uses an <tt class="docutils literal">Annotation</tt> class to convert the tuples -in the function signature into annotation objects, i.e. objects with -six attributes <tt class="docutils literal">help, kind, short, type, choices, metavar</tt>.</p> -<p>Advanced users can implement their own annotation objects. -For instance, here is an example of how you could implement annotations for -positional arguments:</p> -<pre class="literal-block"> -# annotations.py -class Positional(object): - def __init__(self, help='', type=None, choices=None, metavar=None): - self.help = help - self.kind = 'positional' - self.abbrev = None - self.type = type - self.choices = choices - self.metavar = metavar - -</pre> -<p>You can use such annotations objects as follows:</p> +<div class="section" id="keyword-arguments"> +<h1><a class="toc-backref" href="#id9">Keyword arguments</a></h1> +<p>Starting from release 0.4, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports keyword arguments. In +practice that means that if your main function has keyword arguments, +<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> treats specially arguments of the form <tt class="docutils literal"><span class="pre">"name=value"</span></tt> in the +command line. Here is an example:</p> <pre class="literal-block"> -# example11.py +# example12.py import plac -from annotations import Positional @plac.annotations( - i=Positional("This is an int", int), - n=Positional("This is a float", float), - rest=Positional("Other arguments")) -def main(i, n, *rest): - print(i, n, rest) + opt=('some option', 'option'), + args='default arguments', + kw='keyword arguments') +def main(opt, *args, **kw): + if opt: + yield 'opt=%s' % opt + if args: + yield 'args=%s' % str(args) + if kw: + yield 'kw=%s' % kw if __name__ == '__main__': - import plac; plac.call(main) + for output in plac.call(main): + print(output) </pre> -<p>Here is the usage message you get:</p> +<p>Here is the generated usage message:</p> <pre class="literal-block"> -usage: example11.py [-h] i n [rest [rest ...]] +usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]] positional arguments: - i This is an int - n This is a float - rest Other arguments + args default arguments + kw keyword arguments optional arguments: -h, --help show this help message and exit + -opt OPT some option </pre> -<p>You can go on and define <tt class="docutils literal">Option</tt> and <tt class="docutils literal">Flag</tt> classes, if you like. -Using custom annotation objects you could do advanced things like extracting the -annotations from a configuration file or from a database, but I expect such -use cases to be quite rare: the default mechanism should work -pretty well for most users.</p> +<p>Here is how you call the script:</p> +<pre class="literal-block"> +$ python example12.py -o X a1 a2 name=value +opt=X +args=('a1', 'a2') +kw={'name': 'value'} +</pre> +<p>When using keyword arguments, one must be careful to use names which +are not alreay taken; for instance in this examples the name <tt class="docutils literal"><span class="pre">opt</span></tt> +is taken:</p> +<pre class="literal-block"> +$ python example12.py 1 2 kw1=1 kw2=2 opt=0 +usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]] +example12.py: error: colliding keyword arguments: opt +</pre> +<p>The names taken are the names of the flags, of the options, and of the +positional arguments, excepted varargs and keywords. This limitation +is a consequence of the way the argument names are managed in function calls +by the Python language.</p> </div> <div class="section" id="plac-vs-argparse"> -<h1><a class="toc-backref" href="#id12">plac vs argparse</a></h1> +<h1><a class="toc-backref" href="#id10">plac vs argparse</a></h1> <p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is opinionated and by design it does not try to make available all of the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> in an easy way. In particular you should be aware of the following limitations/differences (the following assumes knowledge of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>):</p> <ul class="simple"> -<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> automatically defines both a long and short form for each options, -just like <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a>. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> allows you to define only a long form, -or only a short form, if you like. However, since I have always been -happy with the behavior of <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a>, which I feel is pretty much -consistent, I have decided not to support this feature.</li> <li>plac does not support the destination concept: the destination coincides with the name of the argument, always. This restriction has some drawbacks. For instance, suppose you want to define a long -option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal">yield</tt>, +option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal"><span class="pre">yield</span></tt>, which is a Python keyword, and since you cannot introduce an argument with that name in a function definition, it is impossible to implement it. Your choices are to change the name of the long @@ -1177,32 +1164,32 @@ documentation puts it: <em>Required options are generally considered bad form - normal users expect options to be optional. You should avoid the use of required options whenever possible.</em></li> <li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to -define generalized two-value flags with values different from <tt class="docutils literal">True</tt> -and <tt class="docutils literal">False</tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but +define generalized two-value flags with values different from <tt class="docutils literal"><span class="pre">True</span></tt> +and <tt class="docutils literal"><span class="pre">False</span></tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but since you can use options with two choices instead, and in any case -the conversion from <tt class="docutils literal">{True, False}</tt> to any couple of values +the conversion from <tt class="docutils literal"><span class="pre">{True,</span> <span class="pre">False}</span></tt> to any couple of values can be trivially implemented with a ternary operator -(<tt class="docutils literal">value1 if flag else value2</tt>), I have removed it (KISS rules!).</li> -<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal">nargs</tt> options directly (it uses them internally, +(<tt class="docutils literal"><span class="pre">value1</span> <span class="pre">if</span> <span class="pre">flag</span> <span class="pre">else</span> <span class="pre">value2</span></tt>), I have removed it (KISS rules!).</li> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal"><span class="pre">nargs</span></tt> options directly (it uses them internally, though, to implement flag recognition). The reason it that all the use cases of interest to me are covered by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and did not feel the need -to increase the learning curve by adding direct support for <tt class="docutils literal">nargs</tt>.</li> -<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support subparsers directly. For the moment, this -looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</li> +to increase the learning curve by adding direct support for <tt class="docutils literal"><span class="pre">nargs</span></tt>.</li> +<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does support subparsers, but you must read the <a class="reference external" href="in-writing">advanced usage +document</a> to see how it works.</li> <li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support actions directly. This also looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Notice however -that the ability to define your own annotation objects may mitigate the -need for custom actions.</li> +that the ability to define your own annotation objects (again, see +the <a class="reference external" href="in-writing">advanced usage document</a>) may mitigate the need for custom actions.</li> </ul> -<p>I should stress again that if you want to access all of the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features -from <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you can use <tt class="docutils literal">plac.parser_from</tt> and you will get +<p>I should stress that if you want to access all of the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features +from <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> you can use <tt class="docutils literal"><span class="pre">plac.parser_from</span></tt> and you will get the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object. The the full power of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> -is then available to you: you can use <tt class="docutils literal">add_argument</tt>, <tt class="docutils literal">add_subparsers()</tt>, +is then available to you: you can use <tt class="docutils literal"><span class="pre">add_argument</span></tt>, <tt class="docutils literal"><span class="pre">add_subparsers()</span></tt>, etc. In other words, while some features are not supported directly, <em>all</em> features are supported indirectly.</p> </div> <div class="section" id="plac-vs-the-rest-of-the-world"> -<h1><a class="toc-backref" href="#id13">plac vs the rest of the world</a></h1> +<h1><a class="toc-backref" href="#id11">plac vs the rest of the world</a></h1> <p>Originally <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> boasted about being "the easiest command-line arguments parser in the world". Since then, people started pointing out to me various projects which are based on the same idea @@ -1221,19 +1208,23 @@ just the day before <a class="reference external" href="http://pypi.python.org/p easier than <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p> </div> <div class="section" id="the-future"> -<h1><a class="toc-backref" href="#id14">The future</a></h1> -<p>Currently plac is around 140 lines of code, not counting blanks, -comments and docstrings. I do not plan to extend it much in the +<h1><a class="toc-backref" href="#id12">The future</a></h1> +<p>Currently <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is around 170 lines of code, not counting blanks, +comments and docstrings. I do not plan to extend the core much in the future. The idea is to keep the module short: it is and it should remain a little wrapper over <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually I have thought about -contributing the code back to <a class="reference external" href="http://argparse.googlecode.com">argparse</a> if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> becomes successfull +contributing the core back to <a class="reference external" href="http://argparse.googlecode.com">argparse</a> if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> becomes successfull and gains a reasonable number of users. For the moment it should be -considered experimental: after all I wrote the first version of it in +considered in alpha status: after all I wrote the first version of it in three days, including the tests, the documentation and the time to -learn <a class="reference external" href="http://argparse.googlecode.com">argparse</a>.</p> +learn <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. At the moment I have already written some powerful +extensions for <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and I plan to release them in the 0.5 release, once +I finish writing <a class="reference external" href="in-writing">advanced usage document</a> . +The extensions will be released together with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> but in a separated +module.</p> </div> <div class="section" id="trivia-the-story-behind-the-name"> -<h1><a class="toc-backref" href="#id15">Trivia: the story behind the name</a></h1> +<h1><a class="toc-backref" href="#id13">Trivia: the story behind the name</a></h1> <p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> project started very humble: I just wanted to make easy_installable my old <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, and to publish it on PyPI. The original name of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was optionparser and the idea behind it was @@ -1249,16 +1240,16 @@ of functions annotations in Python 3.</li> </ol> <p>Putting together these two observations with the original idea of inferring the parser I decided to build an <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object from function -annotations. The <tt class="docutils literal">optionparser</tt> name was ruled out, since I was -now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal">argparse_plus</tt> was also ruled out, +annotations. The <tt class="docutils literal"><span class="pre">optionparser</span></tt> name was ruled out, since I was +now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal"><span class="pre">argparse_plus</span></tt> was also ruled out, since the typical usage was completely different from the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> usage.</p> -<p>I made a research on PyPI and the name clap (Command Line Arguments Parser) +<p>I made a research on PyPI and the name <em>clap</em> (Command Line Arguments Parser) was not taken, so I renamed everything to clap. After two days a <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> module appeared on PyPI <expletives deleted>!</p> <p>Having little imagination, I decided to rename everything again to plac, an anagram of clap: since it is a non-existing English name, I hope nobody will steal it from me!</p> -<p>That's all, I hope you will enjoy working with plac!</p> +<p>That's all, I hope you will enjoy working with <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>!</p> </div> </div> </body> |