mirror of
https://github.com/kennethreitz-archive/plac.git
synced 2026-06-05 07:36:12 +00:00
1355 lines
61 KiB
HTML
1355 lines
61 KiB
HTML
<?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>Plac: Parsing the Command Line the Easy Way</title>
|
|
<meta name="author" content="Michele Simionato" />
|
|
<meta name="date" content="July 2010" />
|
|
<style type="text/css">
|
|
|
|
/*
|
|
:Author: David Goodger (goodger@python.org)
|
|
:Id: $Id: html4css1.css 5951 2009-05-18 18:03:10Z milde $
|
|
:Copyright: This stylesheet has been placed in the public domain.
|
|
|
|
Default cascading style sheet for the HTML output of Docutils.
|
|
|
|
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
|
customize this style sheet.
|
|
*/
|
|
|
|
/* used to remove borders from tables and images */
|
|
.borderless, table.borderless td, table.borderless th {
|
|
border: 0 }
|
|
|
|
table.borderless td, table.borderless th {
|
|
/* Override padding for "table.docutils td" with "! important".
|
|
The right padding separates the table cells. */
|
|
padding: 0 0.5em 0 0 ! important }
|
|
|
|
.first {
|
|
/* Override more specific margin styles with "! important". */
|
|
margin-top: 0 ! important }
|
|
|
|
.last, .with-subtitle {
|
|
margin-bottom: 0 ! important }
|
|
|
|
.hidden {
|
|
display: none }
|
|
|
|
a.toc-backref {
|
|
text-decoration: none ;
|
|
color: black }
|
|
|
|
blockquote.epigraph {
|
|
margin: 2em 5em ; }
|
|
|
|
dl.docutils dd {
|
|
margin-bottom: 0.5em }
|
|
|
|
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
|
dl.docutils dt {
|
|
font-weight: bold }
|
|
*/
|
|
|
|
div.abstract {
|
|
margin: 2em 5em }
|
|
|
|
div.abstract p.topic-title {
|
|
font-weight: bold ;
|
|
text-align: center }
|
|
|
|
div.admonition, div.attention, div.caution, div.danger, div.error,
|
|
div.hint, div.important, div.note, div.tip, div.warning {
|
|
margin: 2em ;
|
|
border: medium outset ;
|
|
padding: 1em }
|
|
|
|
div.admonition p.admonition-title, div.hint p.admonition-title,
|
|
div.important p.admonition-title, div.note p.admonition-title,
|
|
div.tip p.admonition-title {
|
|
font-weight: bold ;
|
|
font-family: sans-serif }
|
|
|
|
div.attention p.admonition-title, div.caution p.admonition-title,
|
|
div.danger p.admonition-title, div.error p.admonition-title,
|
|
div.warning p.admonition-title {
|
|
color: red ;
|
|
font-weight: bold ;
|
|
font-family: sans-serif }
|
|
|
|
/* Uncomment (and remove this text!) to get reduced vertical space in
|
|
compound paragraphs.
|
|
div.compound .compound-first, div.compound .compound-middle {
|
|
margin-bottom: 0.5em }
|
|
|
|
div.compound .compound-last, div.compound .compound-middle {
|
|
margin-top: 0.5em }
|
|
*/
|
|
|
|
div.dedication {
|
|
margin: 2em 5em ;
|
|
text-align: center ;
|
|
font-style: italic }
|
|
|
|
div.dedication p.topic-title {
|
|
font-weight: bold ;
|
|
font-style: normal }
|
|
|
|
div.figure {
|
|
margin-left: 2em ;
|
|
margin-right: 2em }
|
|
|
|
div.footer, div.header {
|
|
clear: both;
|
|
font-size: smaller }
|
|
|
|
div.line-block {
|
|
display: block ;
|
|
margin-top: 1em ;
|
|
margin-bottom: 1em }
|
|
|
|
div.line-block div.line-block {
|
|
margin-top: 0 ;
|
|
margin-bottom: 0 ;
|
|
margin-left: 1.5em }
|
|
|
|
div.sidebar {
|
|
margin: 0 0 0.5em 1em ;
|
|
border: medium outset ;
|
|
padding: 1em ;
|
|
background-color: #ffffee ;
|
|
width: 40% ;
|
|
float: right ;
|
|
clear: right }
|
|
|
|
div.sidebar p.rubric {
|
|
font-family: sans-serif ;
|
|
font-size: medium }
|
|
|
|
div.system-messages {
|
|
margin: 5em }
|
|
|
|
div.system-messages h1 {
|
|
color: red }
|
|
|
|
div.system-message {
|
|
border: medium outset ;
|
|
padding: 1em }
|
|
|
|
div.system-message p.system-message-title {
|
|
color: red ;
|
|
font-weight: bold }
|
|
|
|
div.topic {
|
|
margin: 2em }
|
|
|
|
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
|
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
|
margin-top: 0.4em }
|
|
|
|
h1.title {
|
|
text-align: center }
|
|
|
|
h2.subtitle {
|
|
text-align: center }
|
|
|
|
hr.docutils {
|
|
width: 75% }
|
|
|
|
img.align-left, .figure.align-left{
|
|
clear: left ;
|
|
float: left ;
|
|
margin-right: 1em }
|
|
|
|
img.align-right, .figure.align-right {
|
|
clear: right ;
|
|
float: right ;
|
|
margin-left: 1em }
|
|
|
|
.align-left {
|
|
text-align: left }
|
|
|
|
.align-center {
|
|
clear: both ;
|
|
text-align: center }
|
|
|
|
.align-right {
|
|
text-align: right }
|
|
|
|
/* reset inner alignment in figures */
|
|
div.align-right {
|
|
text-align: left }
|
|
|
|
/* div.align-center * { */
|
|
/* text-align: left } */
|
|
|
|
ol.simple, ul.simple {
|
|
margin-bottom: 1em }
|
|
|
|
ol.arabic {
|
|
list-style: decimal }
|
|
|
|
ol.loweralpha {
|
|
list-style: lower-alpha }
|
|
|
|
ol.upperalpha {
|
|
list-style: upper-alpha }
|
|
|
|
ol.lowerroman {
|
|
list-style: lower-roman }
|
|
|
|
ol.upperroman {
|
|
list-style: upper-roman }
|
|
|
|
p.attribution {
|
|
text-align: right ;
|
|
margin-left: 50% }
|
|
|
|
p.caption {
|
|
font-style: italic }
|
|
|
|
p.credits {
|
|
font-style: italic ;
|
|
font-size: smaller }
|
|
|
|
p.label {
|
|
white-space: nowrap }
|
|
|
|
p.rubric {
|
|
font-weight: bold ;
|
|
font-size: larger ;
|
|
color: maroon ;
|
|
text-align: center }
|
|
|
|
p.sidebar-title {
|
|
font-family: sans-serif ;
|
|
font-weight: bold ;
|
|
font-size: larger }
|
|
|
|
p.sidebar-subtitle {
|
|
font-family: sans-serif ;
|
|
font-weight: bold }
|
|
|
|
p.topic-title {
|
|
font-weight: bold }
|
|
|
|
pre.address {
|
|
margin-bottom: 0 ;
|
|
margin-top: 0 ;
|
|
font: inherit }
|
|
|
|
pre.literal-block, pre.doctest-block {
|
|
margin-left: 2em ;
|
|
margin-right: 2em }
|
|
|
|
span.classifier {
|
|
font-family: sans-serif ;
|
|
font-style: oblique }
|
|
|
|
span.classifier-delimiter {
|
|
font-family: sans-serif ;
|
|
font-weight: bold }
|
|
|
|
span.interpreted {
|
|
font-family: sans-serif }
|
|
|
|
span.option {
|
|
white-space: nowrap }
|
|
|
|
span.pre {
|
|
white-space: pre }
|
|
|
|
span.problematic {
|
|
color: red }
|
|
|
|
span.section-subtitle {
|
|
/* font-size relative to parent (h1..h6 element) */
|
|
font-size: 80% }
|
|
|
|
table.citation {
|
|
border-left: solid 1px gray;
|
|
margin-left: 1px }
|
|
|
|
table.docinfo {
|
|
margin: 2em 4em }
|
|
|
|
table.docutils {
|
|
margin-top: 0.5em ;
|
|
margin-bottom: 0.5em }
|
|
|
|
table.footnote {
|
|
border-left: solid 1px black;
|
|
margin-left: 1px }
|
|
|
|
table.docutils td, table.docutils th,
|
|
table.docinfo td, table.docinfo th {
|
|
padding-left: 0.5em ;
|
|
padding-right: 0.5em ;
|
|
vertical-align: top }
|
|
|
|
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
|
font-weight: bold ;
|
|
text-align: left ;
|
|
white-space: nowrap ;
|
|
padding-left: 0 }
|
|
|
|
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
|
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
|
font-size: 100% }
|
|
|
|
ul.auto-toc {
|
|
list-style-type: none }
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="document" id="plac-parsing-the-command-line-the-easy-way">
|
|
<h1 class="title">Plac: Parsing the Command Line the Easy Way</h1>
|
|
<table class="docinfo" frame="void" rules="none">
|
|
<col class="docinfo-name" />
|
|
<col class="docinfo-content" />
|
|
<tbody valign="top">
|
|
<tr><th class="docinfo-name">Author:</th>
|
|
<td>Michele Simionato</td></tr>
|
|
<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato@gmail.com">michele.simionato@gmail.com</a></td>
|
|
</tr>
|
|
<tr><th class="docinfo-name">Date:</th>
|
|
<td>July 2010</td></tr>
|
|
<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/plac">http://pypi.python.org/pypi/plac</a></td>
|
|
</tr>
|
|
<tr class="field"><th class="docinfo-name">Project page:</th><td class="field-body"><a class="reference external" href="http://micheles.googlecode.com/hg/plac/doc/plac.html">http://micheles.googlecode.com/hg/plac/doc/plac.html</a></td>
|
|
</tr>
|
|
<tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.3+</td>
|
|
</tr>
|
|
<tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install <span class="pre">-U</span> plac</tt></td>
|
|
</tr>
|
|
<tr class="field"><th class="docinfo-name">License:</th><td class="field-body">BSD license</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="contents topic" id="contents">
|
|
<p class="topic-title first">Contents</p>
|
|
<ul class="simple">
|
|
<li><a class="reference internal" href="#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-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="#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="#final-example-a-shelve-interface" id="id10">Final example: a shelve interface</a></li>
|
|
<li><a class="reference internal" href="#plac-vs-argparse" id="id11">plac vs argparse</a></li>
|
|
<li><a class="reference internal" href="#plac-vs-the-rest-of-the-world" id="id12">plac vs the rest of the world</a></li>
|
|
<li><a class="reference internal" href="#the-future" id="id13">The future</a></li>
|
|
<li><a class="reference internal" href="#trivia-the-story-behind-the-name" id="id14">Trivia: the story behind the name</a></li>
|
|
</ul>
|
|
</div>
|
|
<div class="section" id="the-importance-of-scaling-down">
|
|
<h1><a class="toc-backref" href="#id1">The importance of scaling down</a></h1>
|
|
<p>There is no want of command line arguments parsers in the Python
|
|
world. The standard library alone contains three different modules:
|
|
<a class="reference external" href="http://docs.python.org/library/getopt.html">getopt</a> (from the stone age),
|
|
<a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> (from Python 2.3) and <a class="reference external" href="http://argparse.googlecode.com">argparse</a> (from Python 2.7). All of
|
|
them are quite powerful and especially <a class="reference external" href="http://argparse.googlecode.com">argparse</a> is an industrial
|
|
strength solution; unfortunately, all of them feature a non-zero learning
|
|
curve and a certain verbosity. They do not scale down well, at
|
|
least in my opinion.</p>
|
|
<p>It should not be necessary to stress the importance <a class="reference external" href="http://www.welton.it/articles/scalable_systems">scaling down</a>;
|
|
nevertheless, a lot of people are obsessed with features and concerned with
|
|
the possibility of scaling up, forgetting the equally important
|
|
issue of scaling down. This is an old meme in
|
|
the computing world: programs should address the common cases simply and
|
|
simple things should be kept simple, while at the same keeping
|
|
difficult things possible. <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> adhere as much as possible to this
|
|
philosophy and it is designed to handle well the simple cases, while
|
|
retaining the ability to handle complex cases by relying on the
|
|
underlying power of <a class="reference external" href="http://argparse.googlecode.com">argparse</a>.</p>
|
|
<p>Technically <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is just a simple wrapper over <a class="reference external" href="http://argparse.googlecode.com">argparse</a> which hides
|
|
most of its complexity by using a declarative interface: the argument
|
|
parser is inferred rather than written down by imperatively. Still, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is
|
|
surprisingly scalable upwards, even without using the underlying
|
|
<a class="reference external" href="http://argparse.googlecode.com">argparse</a>. I have been using Python for 8 years and in my experience
|
|
it is extremely unlikely that you will ever need to go beyond the
|
|
features provided by the declarative interface of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: they should
|
|
be more than enough for 99.9% of the use cases.</p>
|
|
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is targetting especially unsophisticated users,
|
|
programmers, sys-admins, scientists and in general people writing
|
|
throw-away scripts for themselves, choosing the command line
|
|
interface because it is the quick and simple. Such users are not
|
|
interested in features, they are interested in a small learning curve:
|
|
they just want to be able to write a simple command line tool from a
|
|
simple specification, not to build a command-line parser by
|
|
hand. Unfortunately, the modules in the standard library forces them
|
|
to go the hard way. They are designed to implement power user tools
|
|
and they have a non-trivial learning curve. On the contrary, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
|
|
is designed to be simple to use and extremely concise, as the examples
|
|
below will show.</p>
|
|
</div>
|
|
<div class="section" id="scripts-with-required-arguments">
|
|
<h1><a class="toc-backref" href="#id2">Scripts with required arguments</a></h1>
|
|
<p>Let me start with the simplest possible thing: a script that takes a
|
|
single argument and does something to it. It cannot get simpler
|
|
than that, unless you consider a script without command-line
|
|
arguments, where there is nothing to parse. Still, it is a use
|
|
case <em>extremely common</em>: I need to write scripts like that nearly
|
|
every day, I wrote hundreds of them in the last few years and I have
|
|
never been happy. Here is a typical example of code I have been
|
|
writing by hand for years:</p>
|
|
<pre class="literal-block">
|
|
# example1.py
|
|
def main(dsn):
|
|
"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>
|
|
<p>As you see the whole <tt class="docutils literal">if __name__ == '__main__'</tt> block (nine lines)
|
|
is essentially boilerplate that should not exist. Actually I think
|
|
the language should recognize the main function and pass to it the
|
|
command-line arguments automatically; unfortunaly this is unlikely to
|
|
happen. I have been writing boilerplate like this in hundreds of
|
|
scripts for years, and every time I <em>hate</em> it. The purpose of using a
|
|
scripting language is convenience and trivial things should be
|
|
trivial. Unfortunately the standard library does not help for this
|
|
incredibly common use case. Using <a class="reference external" href="http://docs.python.org/library/getopt.html">getopt</a> and <a class="reference external" href="http://docs.python.org/library/optparse.html">optparse</a> does not help,
|
|
since they are intended to manage options and not positional
|
|
arguments; the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module helps a bit and it is able to reduce
|
|
the boilerplate from nine lines to six lines:</p>
|
|
<pre class="literal-block">
|
|
# example2.py
|
|
def main(dsn):
|
|
"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>
|
|
<p>However saving three lines does not justify introducing the external
|
|
dependency: most people will not switch to Python 2.7, which at the time of
|
|
this writing is just about to be released, for many years.
|
|
Moreover, it just feels too complex to instantiate a class and to
|
|
define a parser by hand for such a trivial task.</p>
|
|
<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> module is designed to manage well such use cases, and it is able
|
|
to reduce the original nine lines of boiler plate to two lines. With the
|
|
<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> module all you need to write is</p>
|
|
<pre class="literal-block">
|
|
# example3.py
|
|
def main(dsn):
|
|
"Do something with the database"
|
|
print(dsn)
|
|
# ...
|
|
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
|
|
</pre>
|
|
<p>The <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> module provides for free (actually the work is done by the
|
|
underlying <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module) a nice usage message:</p>
|
|
<pre class="literal-block">
|
|
$ python example3.py -h
|
|
</pre>
|
|
<pre class="literal-block">
|
|
usage: example3.py [-h] dsn
|
|
|
|
Do something with the database
|
|
|
|
positional arguments:
|
|
dsn
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
|
|
</pre>
|
|
<p>Moreover <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> manages the case of missing arguments and of too many arguments.
|
|
This is only the tip of the iceberg: <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to do much more than that.</p>
|
|
</div>
|
|
<div class="section" id="scripts-with-default-arguments">
|
|
<h1><a class="toc-backref" href="#id3">Scripts with default arguments</a></h1>
|
|
<p>The need to have suitable defaults for command-line scripts is quite
|
|
common. For instance I have encountered this use case at work hundreds
|
|
of times:</p>
|
|
<pre class="literal-block">
|
|
# example4.py
|
|
from datetime import datetime
|
|
|
|
def main(dsn, table='product', today=datetime.today()):
|
|
"Do something on the database"
|
|
print(dsn, table, today)
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
args = sys.argv[1:]
|
|
if not args:
|
|
sys.exit('usage: python %s dsn' % sys.argv[0])
|
|
elif len(args) > 2:
|
|
sys.exit('Unrecognized arguments: %s' % ' '.join(argv[2:]))
|
|
main(*args)
|
|
|
|
</pre>
|
|
<p>Here I want to perform a query on a database table, by extracting the
|
|
most recent data: it makes sense for <tt class="docutils literal">today</tt> to be a default argument.
|
|
If there is a most used table (in this example a table called <tt class="docutils literal">'product'</tt>)
|
|
it also makes sense to make it a default argument. Performing the parsing
|
|
of the command-line arguments by hand takes 8 ugly lines of boilerplate
|
|
(using <a class="reference external" href="http://argparse.googlecode.com">argparse</a> would require about the same number of lines).
|
|
With <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the entire <tt class="docutils literal">__main__</tt> block reduces to the usual two lines:</p>
|
|
<pre class="literal-block">
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
</pre>
|
|
<p>In other words, six lines of boilerplate have been removed, and we get
|
|
the usage message for free:</p>
|
|
<pre class="literal-block">
|
|
usage: example5.py [-h] dsn [table] [today]
|
|
|
|
Do something on the database
|
|
|
|
positional arguments:
|
|
dsn
|
|
table
|
|
today
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
|
|
</pre>
|
|
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> manages transparently even the case when you want to pass a
|
|
variable number of arguments. Here is an example, a script running
|
|
on a database a series of SQL scripts:</p>
|
|
<pre class="literal-block">
|
|
# example7.py
|
|
from datetime import datetime
|
|
|
|
def main(dsn, *scripts):
|
|
"Run the given scripts on the database"
|
|
for script in scripts:
|
|
print('executing %s' % script)
|
|
# ...
|
|
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
|
|
</pre>
|
|
<p>Here is the usage message:</p>
|
|
<pre class="literal-block">
|
|
usage: example7.py [-h] dsn [scripts [scripts ...]]
|
|
|
|
Run the given scripts on the database
|
|
|
|
positional arguments:
|
|
dsn
|
|
scripts
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
|
|
</pre>
|
|
<p>The examples here should have made clear that <em>plac is able to figure out
|
|
the command-line arguments parser to use from the signature of the main
|
|
function</em>. This is the whole idea behind <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>: if the intent is clear,
|
|
let's the machine take care of the details.</p>
|
|
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is inspired to an old Python Cookbook recipe (<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a>), in
|
|
the sense that it delivers the programmer from the burden of writing
|
|
the parser, but is less of a hack: instead of extracting the parser
|
|
from the docstring of the module, it extracts it from the signature of
|
|
the <tt class="docutils literal">main</tt> function.</p>
|
|
<p>The idea comes from the <cite>function annotations</cite> concept, a new
|
|
feature of Python 3. An example is worth a thousand words, so here
|
|
it is:</p>
|
|
<pre class="literal-block">
|
|
# example7_.py
|
|
from datetime import datetime
|
|
|
|
def main(dsn: "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">main</tt> function have been annotated with
|
|
strings which are intented to be used in the help message:</p>
|
|
<pre class="literal-block">
|
|
usage: example7_.py [-h] dsn [scripts [scripts ...]]
|
|
|
|
Run the given scripts on the database
|
|
|
|
positional arguments:
|
|
dsn Database dsn
|
|
scripts SQL scripts
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
|
|
</pre>
|
|
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is able to recognize much more complex annotations, as
|
|
I will show in the next paragraphs.</p>
|
|
</div>
|
|
<div class="section" id="scripts-with-options-and-smart-options">
|
|
<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
|
|
than a thousand of them). Still, this use case cannot be neglected.
|
|
The standard library modules (all of them) are quite verbose when it
|
|
comes to specifying the options and frankly I have never used them
|
|
directly. Instead, I have always relied on the
|
|
<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, which provides a convenient wrapper over
|
|
<a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a>. Alternatively, in the simplest cases, I have just
|
|
performed the parsing by hand. In <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> the parser is inferred by the
|
|
function annotations. Here is an example:</p>
|
|
<pre class="literal-block">
|
|
# example8.py
|
|
def main(command: ("SQL query", 'option', 'c'), dsn):
|
|
if command:
|
|
print('executing %s on %s' % (command, dsn))
|
|
# ...
|
|
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
|
|
</pre>
|
|
<p>Here the argument <tt class="docutils literal">command</tt> has been annotated with the tuple
|
|
<tt class="docutils literal">("SQL query", 'option', 'c')</tt>: the first string is the help string
|
|
which will appear in the usage message, the second string tells <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>
|
|
that <tt class="docutils literal">command</tt> is an option and the third string that there is also
|
|
a short form of the option <tt class="docutils literal"><span class="pre">-c</span></tt>, the long form being <tt class="docutils literal"><span class="pre">--command</span></tt>.
|
|
The usage message is the following:</p>
|
|
<pre class="literal-block">
|
|
usage: example8.py [-h] [-c COMMAND] dsn
|
|
|
|
positional arguments:
|
|
dsn
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-c COMMAND, --command COMMAND
|
|
SQL query
|
|
|
|
</pre>
|
|
<p>Here are two examples of usage:</p>
|
|
<pre class="literal-block">
|
|
$ python3 example8.py -c"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>The third argument in the function annotation can be omitted: in such
|
|
case it will be assumed to be <tt class="docutils literal">None</tt>. The consequence is that
|
|
the usual dichotomy between long and short options (GNU-style options)
|
|
disappears: we get <em>smart options</em>, which have the single character prefix
|
|
of short options and behave like both long and short options, since
|
|
they can be abbreviated. Here is an example featuring smart options:</p>
|
|
<pre class="literal-block">
|
|
# example6.py
|
|
def main(dsn, command: ("SQL query", 'option')):
|
|
print('executing %r on %s' % (command, dsn))
|
|
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
|
|
</pre>
|
|
<pre class="literal-block">
|
|
usage: example6.py [-h] [-command COMMAND] dsn
|
|
|
|
positional arguments:
|
|
dsn
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-command COMMAND SQL query
|
|
|
|
</pre>
|
|
<p>The following are all valid invocations ot the script:</p>
|
|
<pre class="literal-block">
|
|
$ python3 example6.py -c "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>Notice that the form <tt class="docutils literal"><span class="pre">-command=SQL</span></tt> is recognized only for the full
|
|
option, not for its abbreviations:</p>
|
|
<pre class="literal-block">
|
|
$ python3 example6.py -com="select" dsn
|
|
usage: example6.py [-h] [-command COMMAND] dsn
|
|
example6.py: error: unrecognized arguments: -com=select
|
|
</pre>
|
|
<p>If the option is not passed, the variable <tt class="docutils literal">command</tt>
|
|
will get the value <tt class="docutils literal">None</tt>. However, it is possible to specify a non-trivial
|
|
default. Here is an example:</p>
|
|
<pre class="literal-block">
|
|
# example8_.py
|
|
def main(dsn, command: ("SQL query", 'option')='select * from table'):
|
|
print('executing %r on %s' % (command, dsn))
|
|
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
|
|
</pre>
|
|
<p>Notice that the default value appears in the help message:</p>
|
|
<pre class="literal-block">
|
|
usage: example8_.py [-h] [-command select * from table] dsn
|
|
|
|
positional arguments:
|
|
dsn
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-command select * from table
|
|
SQL query
|
|
|
|
</pre>
|
|
<p>When you run the script and you do not pass the <tt class="docutils literal"><span class="pre">-command</span></tt> option, the
|
|
default query will be executed:</p>
|
|
<pre class="literal-block">
|
|
$ python3 example8_.py dsn
|
|
executing 'select * from table' on dsn
|
|
</pre>
|
|
</div>
|
|
<div class="section" id="scripts-with-flags">
|
|
<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> is able to recognize flags, i.e. boolean options which are
|
|
<tt class="docutils literal">True</tt> if they are passed to the command line and <tt class="docutils literal">False</tt>
|
|
if they are absent. Here is an example:</p>
|
|
<pre class="literal-block">
|
|
# example9.py
|
|
|
|
def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'):
|
|
if verbose:
|
|
print('connecting to %s' % dsn)
|
|
# ...
|
|
|
|
if __name__ == '__main__':
|
|
import plac; plac.call(main)
|
|
|
|
</pre>
|
|
<pre class="literal-block">
|
|
usage: example9.py [-h] [-v] dsn
|
|
|
|
positional arguments:
|
|
dsn connection string
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-v, --verbose prints more info
|
|
|
|
</pre>
|
|
<pre class="literal-block">
|
|
$ python3 example9.py -v dsn
|
|
connecting to dsn
|
|
</pre>
|
|
<p>Notice that it is an error trying to specify a default for flags: the
|
|
default value for a flag is always <tt class="docutils literal">False</tt>. If you feel the need to
|
|
implement non-boolean flags, you should use an option with two
|
|
choices, as explained in the "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
|
|
arguments, then the required arguments and finally the default
|
|
arguments. This is just a convention and you are not forced to use it,
|
|
except for the default arguments (including the varargs) which must
|
|
stay at the end as it is required by the Python syntax.</p>
|
|
<p>I also suggests to specify a one-character abbreviation for flags: in
|
|
this way you can use the GNU-style composition of flags (i.e. <tt class="docutils literal"><span class="pre">-zxvf</span></tt>
|
|
is an abbreviation of <tt class="docutils literal"><span class="pre">-z</span> <span class="pre">-x</span> <span class="pre">-v</span> <span class="pre">-f</span></tt>). I usually do not provide
|
|
the one-character abbreviation for options, since it does not make sense
|
|
to compose them.</p>
|
|
</div>
|
|
<div class="section" id="plac-for-python-2-x-users">
|
|
<h1><a class="toc-backref" href="#id6">plac for Python 2.X users</a></h1>
|
|
<p>I do not use Python 3. At work we are just starting to think about
|
|
migrating to Python 2.6. It will take years before we
|
|
think to migrate to Python 3. I am pretty much sure most Pythonistas
|
|
are in the same situation. Therefore <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> provides a way to work
|
|
with function annotations even in Python 2.X (including Python 2.3).
|
|
There is no magic involved; you just need to add the annotations
|
|
by hand. For instance the annotated function declaration</p>
|
|
<pre class="literal-block">
|
|
def main(dsn: "Database dsn", *scripts: "SQL scripts"):
|
|
...
|
|
</pre>
|
|
<p>is equivalent to the following code:</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 match the keys of the annotation dictionary
|
|
with the names of the arguments in the annotated function; for lazy
|
|
people with Python 2.4 available the simplest way is to use the
|
|
<tt class="docutils literal">plac.annotations</tt> decorator that performs the check for you:</p>
|
|
<pre class="literal-block">
|
|
@plac.annotations(
|
|
dsn="Database dsn",
|
|
scripts="SQL scripts")
|
|
def main(dsn, *scripts):
|
|
...
|
|
</pre>
|
|
<p>In the rest of this article I will assume that you are using Python 2.X with
|
|
X >= 4 and I will use the <tt class="docutils literal">plac.annotations</tt> decorator. Notice however
|
|
that the core features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> run even on Python 2.3.</p>
|
|
</div>
|
|
<div class="section" id="more-features">
|
|
<h1><a class="toc-backref" href="#id7">More features</a></h1>
|
|
<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 or <tt class="docutils literal">None</tt>, <tt class="docutils literal">type</tt> is a callable taking a
|
|
string in input,
|
|
<tt class="docutils literal">choices</tt> is a discrete sequence of values and <tt class="docutils literal">metavar</tt> is a string.</p>
|
|
<p><tt class="docutils literal">type</tt> is used to automagically convert the command line arguments
|
|
from the string type to any Python type; by default there is no
|
|
conversion and <tt class="docutils literal">type=None</tt>.</p>
|
|
<p><tt class="docutils literal">choices</tt> is used to restrict the number of the valid
|
|
options; by default there is no restriction i.e. <tt class="docutils literal">choices=None</tt>.</p>
|
|
<p><tt class="docutils literal">metavar</tt> 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
|
|
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>
|
|
<p>Here is an example showing many of the features (copied from the
|
|
<a class="reference external" href="http://argparse.googlecode.com">argparse</a> documentation):</p>
|
|
<pre class="literal-block">
|
|
# example10.py
|
|
import plac
|
|
|
|
@plac.annotations(
|
|
operator=("The name of an operator", 'positional', None, str, ['add', 'mul']),
|
|
numbers=("A number", 'positional', None, float, None, "n"))
|
|
def main(operator, *numbers):
|
|
"A script to add and multiply numbers"
|
|
if operator == 'mul':
|
|
op = float.__mul__
|
|
result = 1.0
|
|
else: # operator == 'add'
|
|
op = float.__add__
|
|
result = 0.0
|
|
for n in numbers:
|
|
result = op(result, n)
|
|
return result
|
|
|
|
if __name__ == '__main__':
|
|
print(plac.call(main))
|
|
|
|
</pre>
|
|
<p>Here is the usage:</p>
|
|
<pre class="literal-block">
|
|
usage: example10.py [-h] {add,mul} [n [n ...]]
|
|
|
|
A script to add and multiply numbers
|
|
|
|
positional arguments:
|
|
{add,mul} The name of an operator
|
|
n A number
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
|
|
</pre>
|
|
<p>Notice that the docstring of the <tt class="docutils literal">main</tt> function has been automatically added
|
|
to the usage message. Here are a couple of examples of use:</p>
|
|
<pre class="literal-block">
|
|
$ python example10.py add 1 2 3 4
|
|
10.0
|
|
$ python example10.py mul 1 2 3 4
|
|
24.0
|
|
$ python example10.py ad 1 2 3 4 # a mispelling error
|
|
usage: example10.py [-h] {add,mul} [n [n ...]]
|
|
example10.py: error: argument operator: invalid choice: 'ad' (choose from 'add', 'mul')
|
|
</pre>
|
|
<p><tt class="docutils literal">plac.call</tt> can also be used in doctests like this:</p>
|
|
<pre class="doctest-block">
|
|
>>> import plac, example10
|
|
>>> plac.call(example10.main, ['add', '1', '2'])
|
|
3.0
|
|
</pre>
|
|
<p><tt class="docutils literal">plac.call</tt> works for generators too:</p>
|
|
<pre class="doctest-block">
|
|
>>> def main(n):
|
|
... for i in range(int(n)):
|
|
... yield i
|
|
>>> plac.call(main, ['3'])
|
|
[0, 1, 2]
|
|
</pre>
|
|
<p>Internally <tt class="docutils literal">plac.call</tt> tries to convert the output of the main function
|
|
into a list, if possible. If the output is not iterable or it is a
|
|
string, it is left unchanged, but if it is iterable it is converted.
|
|
In particular, generator objects are exhausted by <tt class="docutils literal">plac.call</tt>.</p>
|
|
<p>This behavior avoids mistakes like forgetting of applying
|
|
<tt class="docutils literal">list(result)</tt> to the result of <tt class="docutils literal">plac.call</tt>; moreover it makes
|
|
errors visible early, and avoids mistakes in code like the following:</p>
|
|
<pre class="literal-block">
|
|
try:
|
|
result = plac.call(main, args)
|
|
except:
|
|
# do something
|
|
</pre>
|
|
<p>Without the "listify" functionality, a main function returning a
|
|
generator object would not raise any exception until the generator
|
|
is iterated over.</p>
|
|
<p>If you are a fan of lazyness, you can still have it by setting the <tt class="docutils literal">eager</tt>
|
|
flag to <tt class="docutils literal">False</tt>, as in the following example:</p>
|
|
<pre class="literal-block">
|
|
for line in plac.call(main, args, eager=False):
|
|
print(line)
|
|
</pre>
|
|
<p>If <tt class="docutils literal">main</tt> returns a generator object this example will print each
|
|
line as soon as available, whereas the default behaviour is to print
|
|
all the lines together and the end of the computation.</p>
|
|
</div>
|
|
<div class="section" id="a-realistic-example">
|
|
<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
|
|
string into a <a class="reference external" href="http://www.sqlalchemy.org/docs/reference/ext/sqlsoup.html">SqlSoup</a> object:</p>
|
|
<pre class="literal-block">
|
|
# dbcli.py
|
|
import plac
|
|
from sqlalchemy.ext.sqlsoup import SqlSoup
|
|
|
|
@plac.annotations(
|
|
db=("Connection string", 'positional', None, SqlSoup),
|
|
header=("Header", 'flag', 'H'),
|
|
sqlcmd=("SQL command", 'option', 'c', str, None, "SQL"),
|
|
delimiter=("Column separator", 'option', 'd'),
|
|
scripts="SQL scripts",
|
|
)
|
|
def main(db, header, sqlcmd, delimiter="|", *scripts):
|
|
"A script to run queries and SQL scripts on a database"
|
|
yield 'Working on %s' % db.bind.url
|
|
|
|
if sqlcmd:
|
|
result = db.bind.execute(sqlcmd)
|
|
if header: # print the header
|
|
yield delimiter.join(result.keys())
|
|
for row in result: # print the rows
|
|
yield delimiter.join(map(str, row))
|
|
|
|
for script in scripts:
|
|
db.bind.execute(file(script).read())
|
|
yield 'executed %s' % script
|
|
|
|
if __name__ == '__main__':
|
|
for output in plac.call(main):
|
|
print(output)
|
|
|
|
</pre>
|
|
<p>You can see the <em>yield-is-print</em> pattern here: instead of using
|
|
<tt class="docutils literal">print</tt> in the main function, I use <tt class="docutils literal">yield</tt>, and I perform the
|
|
print in the <tt class="docutils literal">__main__</tt> block. The advantage of the pattern is that
|
|
tests invoking <tt class="docutils literal">plac.call</tt> and checking the result become trivial:
|
|
had I performed the printing in the main function, the test would have
|
|
involved an ugly hack like redirecting <tt class="docutils literal">sys.stdout</tt> to a
|
|
<tt class="docutils literal">StringIO</tt> object.</p>
|
|
<p>Here is the usage message:</p>
|
|
<pre class="literal-block">
|
|
usage: dbcli.py [-h] [-H] [-c SQL] [-d |] db [scripts [scripts ...]]
|
|
|
|
A script to run queries and SQL scripts on a database
|
|
|
|
positional arguments:
|
|
db Connection string
|
|
scripts SQL scripts
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-H, --header Header
|
|
-c SQL, --sqlcmd SQL SQL command
|
|
-d |, --delimiter | Column separator
|
|
|
|
</pre>
|
|
<p>You can check for yourself that the script works.</p>
|
|
</div>
|
|
<div class="section" id="keyword-arguments">
|
|
<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">"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):
|
|
if opt:
|
|
yield 'opt=%s' % opt
|
|
if args:
|
|
yield 'args=%s' % str(args)
|
|
if kw:
|
|
yield 'kw=%s' % kw
|
|
|
|
if __name__ == '__main__':
|
|
for output in plac.call(main):
|
|
print(output)
|
|
|
|
</pre>
|
|
<p>Here is the generated usage message:</p>
|
|
<pre class="literal-block">
|
|
usage: example12.py [-h] [-opt OPT] [args [args ...]] [kw [kw ...]]
|
|
|
|
positional arguments:
|
|
args default arguments
|
|
kw keyword arguments
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-opt OPT some option
|
|
|
|
</pre>
|
|
<p>Here is how you call the script:</p>
|
|
<pre class="literal-block">
|
|
$ python example12.py -o X a1 a2 name=value
|
|
opt=X
|
|
args=('a1', 'a2')
|
|
kw={'name': 'value'}
|
|
</pre>
|
|
<p>When using keyword arguments, one must be careful to use names which
|
|
are not alreay taken; for instance in this examples the name <tt class="docutils literal">opt</tt>
|
|
is taken:</p>
|
|
<pre class="literal-block">
|
|
$ python example12.py 1 2 kw1=1 kw2=2 opt=0
|
|
usage: example12.py [-h] [-o OPT] [args [args ...]] [kw [kw ...]]
|
|
example12.py: error: colliding keyword arguments: opt
|
|
</pre>
|
|
<p>The names taken are the names of the flags, of the options, and of the
|
|
positional arguments, excepted varargs and keywords. This limitation
|
|
is a consequence of the way the argument names are managed in function calls
|
|
by the Python language.</p>
|
|
</div>
|
|
<div class="section" id="final-example-a-shelve-interface">
|
|
<h1><a class="toc-backref" href="#id10">Final example: a shelve interface</a></h1>
|
|
<p>Here is a less trivial example for the keyword arguments feature.
|
|
The use case is the following: suppose we have stored the
|
|
configuration parameters of a given application into a Python shelve
|
|
and we need a command-line tool to edit the shelve.
|
|
A possible implementation using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> could be the following:</p>
|
|
<pre class="literal-block">
|
|
# ishelve.py
|
|
import os, shelve, plac
|
|
|
|
DEFAULT_SHELVE = os.path.expanduser('~/conf.shelve')
|
|
|
|
@plac.annotations(
|
|
help=('show help', 'flag'),
|
|
showall=('show all parameters in the shelve', 'flag'),
|
|
clear=('clear the shelve', 'flag'),
|
|
delete=('delete an element', 'option'),
|
|
filename=('filename of the shelve', 'option'),
|
|
params='names of the parameters in the shelve',
|
|
setters='setters param=value')
|
|
def main(help, showall, clear, delete, filename=DEFAULT_SHELVE,
|
|
*params, **setters):
|
|
"A simple interface to a shelve. Use .help to see the available commands."
|
|
sh = shelve.open(filename)
|
|
try:
|
|
if not any([help, showall, clear, delete, params, setters]):
|
|
yield 'no arguments passed, use .help to see the available commands'
|
|
elif help: # custom help
|
|
yield 'Commands: .help, .showall, .clear, .delete'
|
|
yield '<param> ...'
|
|
yield '<param=value> ...'
|
|
elif showall:
|
|
for param, name in sh.items():
|
|
yield '%s=%s' % (param, name)
|
|
elif clear:
|
|
sh.clear()
|
|
yield 'cleared the shelve'
|
|
elif delete:
|
|
try:
|
|
del sh[delete]
|
|
except KeyError:
|
|
yield '%s: not found' % delete
|
|
else:
|
|
yield 'deleted %s' % delete
|
|
for param in params:
|
|
try:
|
|
yield sh[param]
|
|
except KeyError:
|
|
yield '%s: not found' % param
|
|
for param, value in setters.items():
|
|
sh[param] = value
|
|
yield 'setting %s=%s' % (param, value)
|
|
finally:
|
|
sh.close()
|
|
|
|
main.add_help = False # there is a custom help, remove the default one
|
|
main.prefix_chars = '.' # use dot-prefixed commands
|
|
|
|
if __name__ == '__main__':
|
|
for output in plac.call(main):
|
|
print(output)
|
|
|
|
</pre>
|
|
<p>A few notes are in order:</p>
|
|
<ol class="arabic simple">
|
|
<li>I have disabled the ordinary help provided by <a class="reference external" href="http://argparse.googlecode.com">argparse</a> and I have
|
|
implemented a custom help command.</li>
|
|
<li>I have changed the prefix character used to recognize the options
|
|
to a dot.</li>
|
|
<li>Keyword arguments recognition (in the <tt class="docutils literal">**setters</tt>) is used to make it
|
|
possible to store a value in the shelve with the syntax
|
|
<tt class="docutils literal">param_name=param_value</tt>.</li>
|
|
<li><tt class="docutils literal">*params</tt> are used to retrieve parameters from the shelve and some
|
|
error checking is performed in the case of missing parameters</li>
|
|
<li>A command to clear the shelve is implemented as a flag (<tt class="docutils literal">.clear</tt>).</li>
|
|
<li>A command to delete a given parameter is implemented as an option
|
|
(<tt class="docutils literal">.delete</tt>).</li>
|
|
<li>There is an option with default (<tt class="docutils literal">.filename=conf.shelve</tt>) to store
|
|
the filename of the shelve.</li>
|
|
<li>All things considered, the code looks like a poor man object oriented
|
|
interface implemented with a chain of elifs instead of methods. Of course,
|
|
<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can do better than that, but let me start from a low-level approach
|
|
first.</li>
|
|
</ol>
|
|
<p>If you run <tt class="docutils literal">ishelve.py</tt> without arguments you get the following
|
|
message:</p>
|
|
<pre class="literal-block">
|
|
$ python ishelve.py
|
|
no arguments passed, use .help to see the available commands
|
|
</pre>
|
|
<p>If you run <tt class="docutils literal">ishelve.py</tt> with the option <tt class="docutils literal">.h</tt> (or any abbreviation
|
|
of <tt class="docutils literal">.help</tt>) you get:</p>
|
|
<pre class="literal-block">
|
|
$ python ishelve.py .h
|
|
Commands: .help, .showall, .clear, .delete
|
|
<param> ...
|
|
<param=value> ...
|
|
</pre>
|
|
<p>You can check by hand that the tool work:</p>
|
|
<pre class="literal-block">
|
|
$ python ishelve.py .clear # start from an empty shelve
|
|
cleared the shelve
|
|
$ python ishelve.py a=1 b=2
|
|
setting a=1
|
|
setting b=2
|
|
$ python ishelve.py .showall
|
|
b=2
|
|
a=1
|
|
$ python ishelve.py .del b # abbreviation for .delete
|
|
deleted b
|
|
$ python ishelve.py a
|
|
1
|
|
$ python ishelve.py b
|
|
b: not found
|
|
$ python ishelve.py .cler # mispelled command
|
|
usage: ishelve.py [.help] [.showall] [.clear] [.delete DELETE]
|
|
[.filename /home/micheles/conf.shelve]
|
|
[params [params ...]] [setters [setters ...]]
|
|
ishelve.py: error: unrecognized arguments: .cler
|
|
</pre>
|
|
</div>
|
|
<div class="section" id="plac-vs-argparse">
|
|
<h1><a class="toc-backref" href="#id11">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>plac does not support the destination concept: the destination
|
|
coincides with the name of the argument, always. This restriction
|
|
has some drawbacks. For instance, suppose you want to define a long
|
|
option called <tt class="docutils literal"><span class="pre">--yield</span></tt>. In this case the destination would be <tt class="docutils literal">yield</tt>,
|
|
which is a Python keyword, and since you cannot introduce an
|
|
argument with that name in a function definition, it is impossible
|
|
to implement it. Your choices are to change the name of the long
|
|
option, or to use <a class="reference external" href="http://argparse.googlecode.com">argparse</a> with a suitable destination.</li>
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support "required options". As the <a class="reference external" href="http://argparse.googlecode.com">argparse</a>
|
|
documentation puts it: <em>Required options are generally considered bad
|
|
form - normal users expect options to be optional. You should avoid
|
|
the use of required options whenever possible.</em> Notice that since
|
|
<a class="reference external" href="http://argparse.googlecode.com">argparse</a> supports them, <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can manage them too, but not directly.</li>
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> supports only regular boolean flags. <a class="reference external" href="http://argparse.googlecode.com">argparse</a> has the ability to
|
|
define generalized two-value flags with values different from <tt class="docutils literal">True</tt>
|
|
and <tt class="docutils literal">False</tt>. An earlier version of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> had this feature too, but
|
|
since you can use options with two choices instead, and in any case
|
|
the conversion from <tt class="docutils literal">{True, False}</tt> to any couple of values
|
|
can be trivially implemented with a ternary operator
|
|
(<tt class="docutils literal">value1 if flag else value2</tt>), I have removed it (KISS rules!).</li>
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support <tt class="docutils literal">nargs</tt> options directly (it uses them internally,
|
|
though, to implement flag recognition). The reason it that all the use
|
|
cases of interest to me are covered by <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> and did not feel the need
|
|
to increase the learning curve by adding direct support for <tt class="docutils literal">nargs</tt>.</li>
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does support subparsers, but you must read the <a class="reference external" href="in-writing">advanced usage
|
|
document</a> to see how it works.</li>
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not support actions directly. This also
|
|
looks like a feature too advanced for the goals of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>. Notice however
|
|
that the ability to define your own annotation objects (again, see
|
|
the <a class="reference external" href="in-writing">advanced usage document</a>) may mitigate the need for custom actions.</li>
|
|
</ul>
|
|
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can leverage directly on many <a class="reference external" href="http://argparse.googlecode.com">argparse</a> features.</p>
|
|
<p>For instance, you can make invisible an argument in the usage message
|
|
simply by using <tt class="docutils literal"><span class="pre">'==SUPPRESS=='</span></tt> as help string (or
|
|
<tt class="docutils literal">argparse.SUPPRESS</tt>). Similarly, you can use <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/other-utilities.html?highlight=filetype#FileType">argparse.FileType</a>
|
|
directly.</p>
|
|
<p>It is also possible to pass options to the underlying
|
|
<tt class="docutils literal">argparse.ArgumentParser</tt> object (currently it accepts the default
|
|
arguments <tt class="docutils literal">description</tt>, <tt class="docutils literal">epilog</tt>, <tt class="docutils literal">prog</tt>, <tt class="docutils literal">usage</tt>,
|
|
<tt class="docutils literal">add_help</tt>, <tt class="docutils literal">argument_default</tt>, <tt class="docutils literal">parents</tt>, <tt class="docutils literal">prefix_chars</tt>,
|
|
<tt class="docutils literal">fromfile_prefix_chars</tt>, <tt class="docutils literal">conflict_handler</tt>, <tt class="docutils literal">formatter_class</tt>).
|
|
It is enough to set such attributes on the <tt class="docutils literal">main</tt> function. For
|
|
instance</p>
|
|
<pre class="literal-block">
|
|
def main(...):
|
|
pass
|
|
|
|
main.add_help = False
|
|
</pre>
|
|
<p>disables the recognition of the help flag <tt class="docutils literal"><span class="pre">-h,</span> <span class="pre">--help</span></tt>. This
|
|
mechanism does not look particularly elegant, but it works well
|
|
enough. I assume that the typical user of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> will be happy with
|
|
the defaults and would not want to change them; still it is possible
|
|
if she wants to.</p>
|
|
<p>For instance, by setting the <tt class="docutils literal">description</tt> attribute, it is possible
|
|
to add a comment to the usage message (by default the docstring of the
|
|
<tt class="docutils literal">main</tt> function is used as description).</p>
|
|
<p>It is also possible to change the option prefix; for
|
|
instance if your script must run under Windows and you want to use "/"
|
|
as option prefix you can add the line:</p>
|
|
<pre class="literal-block">
|
|
main.prefix_chars='/-'
|
|
</pre>
|
|
<p>The first prefix char (<tt class="docutils literal">/</tt>) is used
|
|
as the default for the recognition of options and flags;
|
|
the second prefix char (<tt class="docutils literal">-</tt>) is kept to keep the <tt class="docutils literal"><span class="pre">-h/--help</span></tt> option
|
|
working: however you can disable it and reimplement it, if you like,
|
|
as seen in the <tt class="docutils literal">ishelve</tt> example.</p>
|
|
<p>It is possible to access directly the underlying <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object, by
|
|
invoking the <tt class="docutils literal">plac.parser_from</tt> utility function:</p>
|
|
<pre class="doctest-block">
|
|
>>> import plac
|
|
>>> def main(arg):
|
|
... pass
|
|
...
|
|
>>> print(plac.parser_from(main)) #doctest: +ELLIPSIS
|
|
ArgumentParser(prog=...)
|
|
</pre>
|
|
<p>Internally <tt class="docutils literal">plac.call</tt> uses <tt class="docutils literal">plac.parser_from</tt> and adds the parser
|
|
to the main function as an attribute. When <tt class="docutils literal">plac.call(func)</tt> is
|
|
invoked multiple time, the parser is re-used and not rebuilt from scratch again.</p>
|
|
<p>I use <tt class="docutils literal">plac.parser_from</tt> in the unit tests of the module, but regular
|
|
users should not need to use it, unless they want to access <em>all</em>
|
|
of the features of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> directly without calling the main function.</p>
|
|
<p>Interested readers should read the documentation of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> to
|
|
understand the meaning of the other options. If there is a set of
|
|
options that you use very often, you may consider writing a decorator
|
|
adding such options to the <tt class="docutils literal">main</tt> function for you. For simplicity,
|
|
<a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> does not perform any magic except the addition of the <tt class="docutils literal">.p</tt>
|
|
attribute.</p>
|
|
</div>
|
|
<div class="section" id="plac-vs-the-rest-of-the-world">
|
|
<h1><a class="toc-backref" href="#id12">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
|
|
(extracting the parser from the main function signature) and are
|
|
arguably even easier than <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>:</p>
|
|
<ul class="simple">
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/opterator">opterator</a> by Dusty Phillips</li>
|
|
<li><a class="reference external" href="http://pypi.python.org/pypi/CLIArgs">CLIArgs</a> by Pavel Panchekha</li>
|
|
</ul>
|
|
<p>Luckily for me none of such projects had the idea of using
|
|
function annotations and <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; as a consequence, they are
|
|
no match for the capabilities of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
|
|
<p>Of course, there are tons of other libraries to parse the command
|
|
line. For instance <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> by Matthew Frazier which appeared on PyPI
|
|
just the day before <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>; <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> is fine but it is certainly not
|
|
easier than <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
|
|
<p><a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> can also be used as a replacement of the <a class="reference external" href="http://docs.python.org/library/cmd.html">cmd</a> module in the standard
|
|
library and as such it shares many features with the module <a class="reference external" href="http://packages.python.org/cmd2/">cmd2</a> by
|
|
Catherine Devlin. However, this is completely coincidental, since I became
|
|
aware of the <a class="reference external" href="http://packages.python.org/cmd2/">cmd2</a> module only after writing <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>.</p>
|
|
</div>
|
|
<div class="section" id="the-future">
|
|
<h1><a class="toc-backref" href="#id13">The future</a></h1>
|
|
<p>Currently the core of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> is around 200 lines of code, not counting blanks,
|
|
comments and docstrings. I do not plan to extend the core much in the
|
|
future. The idea is to keep the module short: it is and it should
|
|
remain a little wrapper over <a class="reference external" href="http://argparse.googlecode.com">argparse</a>. Actually I have thought about
|
|
contributing the core back to <a class="reference external" href="http://argparse.googlecode.com">argparse</a> if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> becomes successfull
|
|
and gains a reasonable number of users. For the moment it should be
|
|
considered in alpha status.</p>
|
|
<p>Notice that even if <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> has been designed to be simple to use for
|
|
simple stuff, its power should not be underestimated; it is actually a
|
|
quite advanced tool with a domain of applicability which far exceeds
|
|
the realm of command-line arguments parsers.</p>
|
|
<p>Version 0.5 of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> doubled the code base and the documentation: it is
|
|
based on the idea of using <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> to implement command-line interpreters,
|
|
i.e. something akin to the <tt class="docutils literal">cmd</tt> module in the standard library, only better.
|
|
The new features of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> are described in the <a class="reference external" href="in-writing">advanced usage document</a> .
|
|
They are implemented in a separated module (<tt class="docutils literal">plac_ext.py</tt>), since
|
|
they require Python 2.5 to work, whereas <tt class="docutils literal">plac_core.py</tt> only requires
|
|
Python 2.3.</p>
|
|
</div>
|
|
<div class="section" id="trivia-the-story-behind-the-name">
|
|
<h1><a class="toc-backref" href="#id14">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 humbly: I just wanted to make
|
|
easy_installable my old <a class="reference external" href="http://code.activestate.com/recipes/278844-parsing-the-command-line/">optionparse</a> recipe, and to publish it on PyPI.
|
|
The original name of <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a> was optionparser and the idea behind it was
|
|
to build an <a class="reference external" href="http://docs.python.org/library/optparse.html?highlight=optionparser#optparse.OptionParser">OptionParser</a> object from the docstring of the module.
|
|
However, before doing that, I decided to check out the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> module,
|
|
since I knew it was going into Python 2.7 and Python 2.7 was coming out.
|
|
Soon enough I realized two things:</p>
|
|
<ol class="arabic simple">
|
|
<li>the single greatest idea of <a class="reference external" href="http://argparse.googlecode.com">argparse</a> was unifying the positional arguments
|
|
and the options in a single namespace object;</li>
|
|
<li>parsing the docstring was so old-fashioned, considering the existence
|
|
of functions annotations in Python 3.</li>
|
|
</ol>
|
|
<p>Putting together these two observations with the original idea of inferring the
|
|
parser I decided to build an <a class="reference external" href="http://argparse.googlecode.com/svn/tags/r11/doc/ArgumentParser.html">ArgumentParser</a> object from function
|
|
annotations. The <tt class="docutils literal">optionparser</tt> name was ruled out, since I was
|
|
now using <a class="reference external" href="http://argparse.googlecode.com">argparse</a>; a name like <tt class="docutils literal">argparse_plus</tt> was also ruled out,
|
|
since the typical usage was completely different from the <a class="reference external" href="http://argparse.googlecode.com">argparse</a> usage.</p>
|
|
<p>I made a research on PyPI and the name <em>clap</em> (Command Line Arguments Parser)
|
|
was not taken, so I renamed everything to clap. After two days
|
|
a <a class="reference external" href="http://pypi.python.org/pypi/Clap/0.7">Clap</a> module appeared on PyPI <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 <a class="reference external" href="http://pypi.python.org/pypi/plac">plac</a>!</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|