<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><title>This is my weblog.</title><link href="http://andialbrecht.de/" rel="alternate" /><id>http://andialbrecht.de/</id><updated>2013-01-05T00:00:00+01:00</updated><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/andialbrecht" /><feedburner:info uri="andialbrecht" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><title>Custom TreeModel in Gtk3</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/dAKi_VRvcUI/custom-treemodel-in-gtk3.html" rel="alternate" /><updated>2013-01-05T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2013-01-05:/blog/2013/01/05/custom-treemodel-in-gtk3.html</id><summary type="html">&lt;p&gt;While porting &lt;a class="reference external" href="http://crunchyfrog.googlecode.com"&gt;CrunchyFrog&lt;/a&gt; to Gtk3 I came across a tricky issue. Porting the
&lt;tt class="docutils literal"&gt;GenericTreeModel&lt;/tt&gt; I use to display query results wasn't easy.&lt;/p&gt;
&lt;p&gt;The first problem was that &lt;tt class="docutils literal"&gt;GenericTreeModel&lt;/tt&gt; doesn't exist in the Python
bindings for Gtk3 anymore. This class was specific to PyGTK 2. In Gtk3 you'll
have to implement the TreeModel interface (and implementing an existing interface
looks much cleaner).&lt;/p&gt;
&lt;p&gt;But the I came across a &lt;a class="reference external" href="https://bugzilla.gnome.org/show_bug.cgi?id=680016"&gt;bunch&lt;/a&gt; of &lt;a class="reference external" href="https://bugzilla.gnome.org/show_bug.cgi?id=680812"&gt;issues&lt;/a&gt;. After asking on the &lt;a class="reference external" href="http://www.daa.com.au/pipermail/pygtk/2012-December/020510.html"&gt;mailing
list&lt;/a&gt; I came up with the following solution:&lt;/p&gt;
&lt;script src="https://gist.github.com/4463278.js"&gt;&lt;/script&gt;&lt;p&gt;The important thing is that &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python-gi&lt;/span&gt;&lt;/tt&gt; (&lt;tt class="docutils literal"&gt;pygobject&lt;/tt&gt;) &amp;gt;= 3.4 is required to
make this work. On Debian Wheezy you'll have to install it from experimental
ATM. The latest Ubuntu should already have the correct version.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/dAKi_VRvcUI" height="1" width="1"/&gt;</summary><category term="python" /><category term="gtk" /><feedburner:origLink>http://andialbrecht.de//blog/2013/01/05/custom-treemodel-in-gtk3.html</feedburner:origLink></entry><entry><title>Slides from my pdb presentation</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/BdUGCVn9Uhc/slides-from-my-pdb-presentation.html" rel="alternate" /><updated>2012-08-08T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2012-08-08:/blog/2012/08/08/slides-from-my-pdb-presentation.html</id><summary type="html">&lt;p&gt;I've just uploaded the slides from my presentation of &lt;tt class="docutils literal"&gt;pdb&lt;/tt&gt;, the
builtin Python debugger, from yesterday's &lt;a class="reference external" href="http://pycologne.de"&gt;pyCologne&lt;/a&gt; meeting. The
slides are in German, if this isn't a problem for you, you can
&lt;a class="reference external" href="/files/pdb.pdf"&gt;download the PDF version&lt;/a&gt; here. Otherwise Doug Hellmann's &lt;a class="reference external" href="http://www.doughellmann.com/PyMOTW/pdb/"&gt;PyMOTW
article about pdb&lt;/a&gt; was the main source of this presentation and is a
must-read.&lt;/p&gt;
&lt;p&gt;The purpose of this presentation was to showcase the most basic
features of the builtin debugger everyone has at hand. Too often I
forget about this feature and add &lt;tt class="docutils literal"&gt;print&lt;/tt&gt; statements everywhere
instead of just jumping right into a pdb session and find out what's
actually happening at a certain piece of code.&lt;/p&gt;
&lt;p&gt;In most cases it's the right approach to add &lt;tt class="docutils literal"&gt;import pdb;
pdb.set_trace()&lt;/tt&gt; instead of another print. For me it turned out that
adding a second or third print statement most likely means that I'm
looking at the wrong piece of code and jumping into a debug session is
much more efficient to find the real cause of a misbehavior.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;pdb&lt;/tt&gt; is such a handy module and distributed in the standard
library. So why not use just it :)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/BdUGCVn9Uhc" height="1" width="1"/&gt;</summary><category term="python" /><category term="emacs" /><category term="pyCologne" /><feedburner:origLink>http://andialbrecht.de//blog/2012/08/08/slides-from-my-pdb-presentation.html</feedburner:origLink></entry><entry><title>Running flake8 in Emacs</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/BrDbS-8RvkY/running-flake8-in-emacs.html" rel="alternate" /><updated>2011-09-26T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2011-09-26:/blog/2011/09/26/running-flake8-in-emacs.html</id><summary type="html">&lt;p&gt;Some time ago I've discovered &lt;a class="reference external" href="https://gist.github.com/302848"&gt;python-pylint.el&lt;/a&gt;, a minor mode for Emacs
to run pylint in compilation mode. Running pylint in compilation mode
is really nice since you can jump between errors with the usual Emacs
keyboard shortcuts &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;M-g&lt;/span&gt; n / &lt;span class="pre"&gt;M-g&lt;/span&gt; p&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;But when focussing more on style then I want to run &lt;a class="reference external" href="https://bitbucket.org/tarek/flake8"&gt;flake8&lt;/a&gt;
instead. &lt;a class="reference external" href="https://gist.github.com/1241830"&gt;Here's a thin wrapper (gist.github.com)&lt;/a&gt; around python-pylint that runs
flake8 in compilation mode and parses the output. Nothing special,
just the right commands and a regex to parse the output - but helpful
:)&lt;/p&gt;
&lt;script src="https://gist.github.com/1241830.js?file=python-flake8.el"&gt;&lt;/script&gt;&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/BrDbS-8RvkY" height="1" width="1"/&gt;</summary><category term="python" /><category term="emacs" /><feedburner:origLink>http://andialbrecht.de//blog/2011/09/26/running-flake8-in-emacs.html</feedburner:origLink></entry><entry><title>For the context switchers - git branch I/O</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/9f0TxdC3-7A/for-the-context-switchers-git-branch-io.html" rel="alternate" /><updated>2011-08-11T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2011-08-11:/blog/2011/08/11/for-the-context-switchers-git-branch-io.html</id><summary type="html">&lt;p&gt;At work I'm switching contexts quite often. While working on a
long-term feature, I'll have to switch to do a bug fix, then I'll have
to add a feature request that's done in a few hours and so on…&lt;/p&gt;
&lt;p&gt;Usually I end up with several git branches in my working copy. For
example, currently I have three branches:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git branch
  issue3521
  master
 *work
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One goes upstream (&amp;quot;master&amp;quot;), one for instant fixes (&amp;quot;work&amp;quot;), one
feature branch (&amp;quot;issue3521&amp;quot;).&lt;/p&gt;
&lt;p&gt;To get a quick overview of the current state of all branches I've
added a very simple git extension to my setup that shows incoming and
outgoing commits for each branch. I found the script (besides the
issues it has) quite useful. It gives me just as little information as
needed to orient myself after a couple of changes here and there. It
just visualizes incoming and outgoing commits relative to a branch.&lt;/p&gt;
&lt;p&gt;The following screenshot illustrates it's usage. The colors have the
following meanings:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="39%" /&gt;
&lt;col width="61%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;green&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;everything's fine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;blue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;almost ok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;yellow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;take care&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;red&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;do something&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="figure"&gt;
&lt;img alt="Screenshot of git-branch-io in action." src="files/git-branchio.png" /&gt;
&lt;p class="caption"&gt;(Note, git bio is just an alias to git branch-io.)&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;So in that case I should keep an eye on issue3521 to keep it in sync
with master, should merge back one commit from work to master and
definitely should to something about the branch called outofdate.&lt;/p&gt;
&lt;p&gt;If you want to give it a try, here's the &lt;a class="reference external" href="https://gist.github.com/1000141"&gt;extension (gist.github.com)&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://gist.github.com/1000141.js?file=git-branch-io"&gt;&lt;/script&gt;&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/9f0TxdC3-7A" height="1" width="1"/&gt;</summary><category term="git" /><feedburner:origLink>http://andialbrecht.de//blog/2011/08/11/for-the-context-switchers-git-branch-io.html</feedburner:origLink></entry><entry><title>sqlparse 0.1.3</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/4OJWrwNaX_o/sqlparse-013.html" rel="alternate" /><updated>2011-07-29T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2011-07-29:/blog/2011/07/29/sqlparse-013.html</id><summary type="html">&lt;p&gt;I've just released a new bug fix release for &lt;a class="reference external" href="http://python-sqlparse.googlecode.com/"&gt;sqlparse&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="changes-since-0-1-2"&gt;
&lt;h2&gt;Changes since 0.1.2&lt;/h2&gt;
&lt;div class="section" id="bug-fixes"&gt;
&lt;h3&gt;Bug Fixes&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Improve parsing of floats (thanks to Kris).&lt;/li&gt;
&lt;li&gt;When formatting a statement a space before LIMIT was removed (issue35).&lt;/li&gt;
&lt;li&gt;Fix stripcomments flag (issue38, reported by ooberm…&amp;#64;gmail.com).&lt;/li&gt;
&lt;li&gt;Avoid parsing names as keywords (issue39, reported by djo…&amp;#64;taket.org).&lt;/li&gt;
&lt;li&gt;Make sure identifier lists in subselects are grouped (issue40,
reported by djo…&amp;#64;taket.org).&lt;/li&gt;
&lt;li&gt;Split statements with IF as functions correctly (issue33 and
issue29, reported by charles….&amp;#64;unige.ch).&lt;/li&gt;
&lt;li&gt;Relax detection of keywords, esp. when used as function names
(issue36, nyuhu…&amp;#64;gmail.com).&lt;/li&gt;
&lt;li&gt;Don't treat single characters as keywords (issue32).&lt;/li&gt;
&lt;li&gt;Improve parsing of stand-alone comments (issue26).&lt;/li&gt;
&lt;li&gt;Detection of placeholders in paramterized queries (issue22, reported
by Glyph Lefkowitz).&lt;/li&gt;
&lt;li&gt;Add parsing of MS Access column names with braces (issue27, reported
by frankz…&amp;#64;gmail.com).&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="other"&gt;
&lt;h3&gt;Other&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Replace Django by Flask in &lt;a class="reference external" href="http://sqlformat.appspot.com/"&gt;App Engine frontend&lt;/a&gt; (issue11).&lt;/li&gt;
&lt;li&gt;Documentation moved to &lt;a class="reference external" href="http://sqlparse.readthedocs.org/"&gt;Read the Docs&lt;/a&gt;. Thanks for this great
service!&lt;/li&gt;
&lt;li&gt;Special thanks to Andriy Senkovych for packaging sqlparse for
Debian/Ubuntu and giving some insights in all that packaging
stuff. Thanks to all other package maintainers too!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please file bug reports and feature request on the &lt;a class="reference external" href="http://code.google.com/p/python-sqlparse/issues/entry"&gt;issue tracker&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/4OJWrwNaX_o" height="1" width="1"/&gt;</summary><category term="sqlparse" /><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2011/07/29/sqlparse-013.html</feedburner:origLink></entry><entry><title>A Selenium and Python appetizer</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/73q43fQI2QM/a-selenium-and-python-appetizer.html" rel="alternate" /><updated>2011-07-12T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2011-07-12:/blog/2011/07/12/a-selenium-and-python-appetizer.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://code.google.com/p/selenium/"&gt;Selenium&lt;/a&gt; is a browser
automation framework and was released in &lt;a class="reference external" href="http://seleniumhq.wordpress.com/2011/07/08/selenium-2-0/"&gt;version 2.0.0&lt;/a&gt; just this
week. In this post I'll show you how easy it is to get started with
using this fine framework in Python.&lt;/p&gt;
&lt;div class="section" id="peparing-the-kitchen"&gt;
&lt;h2&gt;Peparing the kitchen&lt;/h2&gt;
&lt;p&gt;Let's create and jump into a &lt;a class="reference external" href="http://www.virtualenv.org/"&gt;virtualenv&lt;/a&gt;. You don't really need it, but at least
we don't need no sudo for anything :)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;$ virtualenv demo
New python executable in demo/bin/python
Installing distribute..................................................done.
$ source demo/bin/activate
(demo) $
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Installing the Selenium Python package is as easy as possible. Just
use your favorite package manage and install it from &lt;a class="reference external" href="http://pypi.python.org/pypi/selenium/"&gt;PyPI&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;(demo) $ pip install selenium
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This installs the selenium Python package which includes all necessary
drivers to run your browser automation using Firefox, Chrome or IE
locally or anything that the Selenium Remote Control provides. We come
back to that later.&lt;/p&gt;
&lt;p&gt;If you want to automate Google's Chrome on your local machine some
little setup is needed. If you want to follow the examples in this
post and if you want to use an installed Firefox feel free to skip
this section. To automate Chrome make sure Chrome is installed and
then download and unzip the chromedriver for your system from the
&lt;a class="reference external" href="http://code.google.com/p/selenium/downloads/list"&gt;Selenium download page&lt;/a&gt;. Place the
chromedriver binary somewhere in your &lt;tt class="docutils literal"&gt;$PATH&lt;/tt&gt;. That's it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="is-it-google"&gt;
&lt;h2&gt;Is it Google?&lt;/h2&gt;
&lt;p&gt;Now that Selenium is up and running, let's write a simple script that
verifies that the title of the Google page actually contains the word
&amp;quot;Google&amp;quot;. That's simple and obvious, but the script illustrates how to
use Selenium in &lt;a class="reference external" href="http://docs.python.org/library/unittest.html"&gt;UnitTests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And you can do all this fancy stuff interactive in the Python shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# Toggle comments to test with a different browser&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c"&gt;#self.driver = webdriver.Firefox()&lt;/span&gt;
        &lt;span class="c"&gt;#self.driver = wevdriver.Ie()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_title_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://google.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;title_tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_tag_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title_tag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Google&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This script has one little drawback. It initializes a new webdriver
(aka browser) for each tests. That's very accurate to start with a
fresh browser for each test, but very time consuming too. As an
alternative you can modify this script to use a global webdriver
instance that is initialized before unittest.main() and closed after
that line.&lt;/p&gt;
&lt;p&gt;Since webdriver uses a network &lt;a class="reference external" href="http://code.google.com/p/selenium/wiki/JsonWireProtocol"&gt;protocol&lt;/a&gt; to
communicate with the browser, it's painless to use it in an
interactive shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;(demo) $ python
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53)
&amp;gt;&amp;gt;&amp;gt; from selenium import webdriver
&amp;gt;&amp;gt;&amp;gt; driver = webdriver.Chrome()
&amp;gt;&amp;gt;&amp;gt; driver.get(&amp;#39;http://google.com&amp;#39;)
&amp;gt;&amp;gt;&amp;gt; driver.find_element_by_tag_name(&amp;#39;title&amp;#39;)
&amp;lt;selenium.webdriver.remote.webelement.WebElement object at 0x29c2510&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's a nice and easy way to figure out what webdriver commands are
available and how they work.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="going-headless"&gt;
&lt;h2&gt;Going headless&lt;/h2&gt;
&lt;p&gt;When using Selenium as part of your test suite, you don't have always
an X environment around to start Firefox or Chrome. As you can see in
the sample script above, it's easy to switch between different
browsers without the need to change anything in your actual tests.&lt;/p&gt;
&lt;p&gt;As the name suggests the remote webdriver talks to a &lt;a class="reference external" href="http://seleniumhq.org/projects/remote-control/"&gt;Remote Control
Server&lt;/a&gt; that let's
you choose a browser. The nice thing is that the standalone server
comes with a headless browser that even supports JavaScript execution
much like Firefox or Chrome.&lt;/p&gt;
&lt;p&gt;Download the standalone server JAR on the &lt;a class="reference external" href="http://code.google.com/p/selenium/downloads/list"&gt;Selenium download page&lt;/a&gt; and simply run
&lt;tt class="docutils literal"&gt;java &lt;span class="pre"&gt;-jar&lt;/span&gt; &lt;span class="pre"&gt;selenium-server-standalone-2.0.0.jar&lt;/span&gt;&lt;/tt&gt; to start it. When
the server is started you can initialize a headless browser in your
scripts like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Remote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;command_executor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://127.0.0.1:4444/wd/hub&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c"&gt;# that&amp;#39;s the default&lt;/span&gt;
    &lt;span class="n"&gt;desired_capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;browserName&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;htmlunit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="s"&gt;&amp;#39;version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="s"&gt;&amp;#39;javascriptEnabled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's all to run your tests using a headless browser. Ready for your
continuous integration system running on a server somewhere.&lt;/p&gt;
&lt;p&gt;Testing web pages is fun, isn't it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit 2011-07-12:&lt;/strong&gt; Corey Goldberg posted a &lt;a class="reference external" href="http://coreygoldberg.blogspot.com/2011/07/python-getting-started-with-selenium.html"&gt;very similar article&lt;/a&gt; on &lt;a class="reference external" href="http://coreygoldberg.blogspot.com/"&gt;his blog&lt;/a&gt; a few hours before I published this post.&lt;/p&gt;
&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/73q43fQI2QM" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2011/07/12/a-selenium-and-python-appetizer.html</feedburner:origLink></entry><entry><title>Back from PythonCamp</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/6_GKY1C2BSc/back-from-pythoncamp.html" rel="alternate" /><updated>2011-04-18T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2011-04-18:/blog/2011/04/18/back-from-pythoncamp.html</id><summary type="html">&lt;p&gt;I'm back from a phantastic weekend at the Python BarCamp in
Cologne. We've had two great days with lots of very interesting
sessions (I'm still wondering how it's possible to stuff that many
topics with such depth in two days, but it worked!), lightning talks,
discussions, pizza, &lt;a class="reference external" href="http://goo.gl/QZxVY"&gt;tweets&lt;/a&gt;, coffee, and and and … We're still
working on the &lt;a class="reference external" href="http://wiki.python-forum.de/PythonBarCamp2011"&gt;wiki pages&lt;/a&gt; to merge all notes taken during the
sessions on &lt;a class="reference external" href="http://openetherpad.org/pycamp"&gt;various Etherpads&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To get an impression search for &lt;a class="reference external" href="http://goo.gl/TK1np"&gt;#pycamp on flickr&lt;/a&gt;.&lt;/p&gt;
&lt;center&gt;&lt;object width="400" height="300"&gt; &lt;param name="flashvars" value="offsite=true&amp;lang=en-us&amp;page_show_url=%2Fsearch%2Fshow%2F%3Fq%3Dpycamp%26z%3De%26d%3Dtaken-20110415-20110418%26ct%3D0%26mt%3Dall%26adv%3D1&amp;page_show_back_url=%2Fsearch%2F%3Fq%3Dpycamp%26z%3De%26d%3Dtaken-20110415-20110418%26ct%3D0%26mt%3Dall%26adv%3D1&amp;method=flickr.photos.search&amp;api_params_str=&amp;api_text=pycamp&amp;api_tag_mode=bool&amp;api_min_taken_date=2011-04-15+00%3A00%3A00&amp;api_max_taken_date=2011-04-19+00%3A00%3A00&amp;api_media=all&amp;api_sort=relevance&amp;jump_to=&amp;start_index=0"&gt;&lt;/param&gt; &lt;param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=71649"&gt;&lt;/param&gt; &lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=71649" allowFullScreen="true" flashvars="offsite=true&amp;lang=en-us&amp;page_show_url=%2Fsearch%2Fshow%2F%3Fq%3Dpycamp%26z%3De%26d%3Dtaken-20110415-20110418%26ct%3D0%26mt%3Dall%26adv%3D1&amp;page_show_back_url=%2Fsearch%2F%3Fq%3Dpycamp%26z%3De%26d%3Dtaken-20110415-20110418%26ct%3D0%26mt%3Dall%26adv%3D1&amp;method=flickr.photos.search&amp;api_params_str=&amp;api_text=pycamp&amp;api_tag_mode=bool&amp;api_min_taken_date=2011-04-15+00%3A00%3A00&amp;api_max_taken_date=2011-04-19+00%3A00%3A00&amp;api_media=all&amp;api_sort=relevance&amp;jump_to=&amp;start_index=0" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/center&gt;&lt;p&gt;Last but not least, big, big thanks to everyone hanging around at the
&lt;a class="reference external" href="http://pythoncamp.de/"&gt;PythonCamp&lt;/a&gt; this year for making it a very interesting and relaxing
weekend!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/6_GKY1C2BSc" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2011/04/18/back-from-pythoncamp.html</feedburner:origLink></entry><entry><title>Regex to parse pylint's parseable output</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/dqXGfvzbvsA/regex-to-parse-pylints-parseable-output.html" rel="alternate" /><updated>2011-04-13T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2011-04-13:/blog/2011/04/13/regex-to-parse-pylints-parseable-output.html</id><summary type="html">&lt;p&gt;Just in case someone (including myself) is looking for that: Here's
the regex I'm using to parse the output from Pylint to extract error
messages. &lt;a class="reference external" href="http://www.logilab.org/project/pylint"&gt;Pylint&lt;/a&gt; needs to be called with the -f parseable flag.&lt;/p&gt;
&lt;script src="https://gist.github.com/917126.js?file=gistfile1.py"&gt;&lt;/script&gt;&lt;p&gt;Try to match each line of Pylint's output with this regex. If it
matches call &lt;tt class="docutils literal"&gt;groupdict()&lt;/tt&gt; to access to information bits easily.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/dqXGfvzbvsA" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2011/04/13/regex-to-parse-pylints-parseable-output.html</feedburner:origLink></entry><entry><title>Blog Moved</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/jGbKaQskiyo/blog-moved.html" rel="alternate" /><updated>2010-04-21T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2010-04-21:/blog/2010/04/21/blog-moved.html</id><summary type="html">&lt;p&gt;Finally I've had some time last weekend to move my blog from
wordpress.com over here. The main reason for doing this was the
missing support for ReStructured Text on wordpress.com. I prefer plain
text over WYSIWYG and I prefer to use my &lt;a class="reference external" href="http://www.gnu.org/software/emacs/"&gt;favorite editor&lt;/a&gt; for writing posts.&lt;/p&gt;
&lt;p&gt;This blog now runs on App Engine with the Bloggart engine and Disqus
for comments and this post is just an excuse to all my subscribers
that all old posts popped up as new during migration :)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/jGbKaQskiyo" height="1" width="1"/&gt;</summary><category term="app engine" /><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2010/04/21/blog-moved.html</feedburner:origLink></entry><entry><title>hgsvn 0.1.8 released</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/XZNvAcKE4uw/hgsvn-018-released.html" rel="alternate" /><updated>2010-01-24T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2010-01-24:/blog/2010/01/24/hgsvn-018-released.html</id><summary type="html">&lt;p&gt;A new maintenance release of &lt;a class="reference external" href="http://pypi.python.org/pypi/hgsvn"&gt;hgsvn&lt;/a&gt; is available on PyPI. Go and
grab it while it's hot :) Besides a lot of &lt;a class="reference external" href="http://pypi.python.org/pypi/hgsvn/#hgsvn-0-1-8"&gt;bug fixes&lt;/a&gt; this release
includes a few new features and improvements:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blogs.sccs.swarthmore.edu/fowles/"&gt;Matt Fowles&lt;/a&gt; added the
so-called &amp;quot;airplane mode&amp;quot;, when using hgimportsvn with
the --local-only flag no network access is required to convert a
already checked out Subversion repository to a hgsvn controlled
Mercurial repository.&lt;/li&gt;
&lt;li&gt;Commit messages when pushing back to SVN can be edited before
committing using the -e/--edit command line flag. Patch by eliterr.&lt;/li&gt;
&lt;li&gt;sterin provided a patch that takes care that no mq patches are
applied when running hgpushsvn or hgpullsvn.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to all contributors for &lt;a class="reference external" href="http://bitbucket.org/andialbrecht/hgsvn/issues/"&gt;reporting bugs&lt;/a&gt; and providing
patches!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/XZNvAcKE4uw" height="1" width="1"/&gt;</summary><category term="hgsvn" /><feedburner:origLink>http://andialbrecht.de//blog/2010/01/24/hgsvn-018-released.html</feedburner:origLink></entry><entry><title>Pluggable App Engine e-mail backends for Django</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/CfKsjiXSyEQ/pluggable-app-engine-e-mail-backends-for-django.html" rel="alternate" /><updated>2009-11-04T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-11-04:/blog/2009/11/04/pluggable-app-engine-e-mail-backends-for-django.html</id><summary type="html">&lt;p&gt;Yesterday support for &lt;a class="reference external" href="http://code.djangoproject.com/ticket/10355"&gt;pluggable e-mail backends&lt;/a&gt; has &lt;a class="reference external" href="http://code.djangoproject.com/changeset/11709"&gt;landed&lt;/a&gt; in Django's trunk
and today I'm happy to announce two e-mail backend implementations to
be used with Django-based App Engine applications.&lt;/p&gt;
&lt;p&gt;The e-mail backends allow you to use native Django functions for
sending e-mails like &lt;tt class="docutils literal"&gt;django.core.mail.send_mail()&lt;/tt&gt; on App Engine.&lt;/p&gt;
&lt;p&gt;To use pluggable e-mail backends you'll have to use a recent version
of Django. Support for pluggable e-mail backends was introduced in
&lt;a class="reference external" href="http://code.djangoproject.com/changeset/11709"&gt;rev11709&lt;/a&gt; and will
be released in Django 1.2.&lt;/p&gt;
&lt;div class="section" id="using-the-e-mail-backend"&gt;
&lt;h2&gt;Using the e-mail backend&lt;/h2&gt;
&lt;p&gt;The e-mail backends are available as a single package on bitbucket. To
check out the most recent sources run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hg clone http://bitbucket.org/andialbrecht/appengine_emailbackends/
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or got to the &lt;a class="reference external" href="http://bitbucket.org/andialbrecht/appengine_emailbackends/downloads/"&gt;downloads page&lt;/a&gt;
and grab a &lt;a class="reference external" href="http://bitbucket.org/andialbrecht/appengine_emailbackends/get/tip.zip"&gt;zip&lt;/a&gt;
file.&lt;/p&gt;
&lt;p&gt;To use the e-mail backend for App Engine copy the
&lt;tt class="docutils literal"&gt;appengine_emailbackend&lt;/tt&gt; directory to the top-level directory of you
App Engine application and add the following line to your
&lt;tt class="docutils literal"&gt;settings.py&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;EMAIL_BACKEND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;appengine_emailbackend&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you prefer asynchronous e-mail delivery use this line in your
&lt;tt class="docutils literal"&gt;settings.py&lt;/tt&gt; instead:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;EMAIL_BACKEND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;appengine_emailbackend.async&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When using the async backend e-mails will not be sent immediately but
delivered via a taskqueue.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="running-the-demo-application"&gt;
&lt;h2&gt;Running the demo application&lt;/h2&gt;
&lt;p&gt;There's also a demo application included in this repository. Run this
application using&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dev_appserver.py .
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and open &lt;a class="reference external" href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt; in your web browser. Remember to
include a current checkout of the Django SVN repository in the
top-level directory of the demo application.&lt;/p&gt;
&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/CfKsjiXSyEQ" height="1" width="1"/&gt;</summary><category term="django" /><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2009/11/04/pluggable-app-engine-e-mail-backends-for-django.html</feedburner:origLink></entry><entry><title>Dominion Set Designer</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/ZulevPQnWnw/dominion-set-designer.html" rel="alternate" /><updated>2009-10-11T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-10-11:/blog/2009/10/11/dominion-set-designer.html</id><summary type="html">&lt;p&gt;If you're a regular Dominion player like me, you're always in search
for new card sets. Fortunately I've had a little time this weekend to
write a simple App Engine application that gives me a random card set
on demand.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="Screenshot of the Dominion Set Designer" src="files/dominion.jpg" /&gt;
&lt;p class="caption"&gt;Screenshot of the Dominioin Set Designer&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can see it in action at &lt;a class="reference external" href="http://dominionsets.appspot.com"&gt;http://dominionsets.appspot.com&lt;/a&gt; That's it,
just a short post on this, now back to the next match :)&lt;/p&gt;
&lt;p&gt;P.s.: Some technical stuff... There's Django with it's i18n support
under the hood. The UI is powered by JQuery.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/ZulevPQnWnw" height="1" width="1"/&gt;</summary><category term="app engine" /><feedburner:origLink>http://andialbrecht.de//blog/2009/10/11/dominion-set-designer.html</feedburner:origLink></entry><entry><title>CrunchyFrog 0.4.1 released</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/0WnVdr7ZGdI/crunchyfrog-041-released.html" rel="alternate" /><updated>2009-10-01T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-10-01:/blog/2009/10/01/crunchyfrog-041-released.html</id><summary type="html">&lt;p&gt;A new bugfix release for CrunchyFrog is out in the wild. There are not
much visible changes, but a few bug fixes under the hood. One visible
change is the option to give the SQL editor much more space by hiding
all other UI elements in the main window. Here's a screenshot:&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="SQL editor withou results pane" src="files/cf_full_editor.png" /&gt;
&lt;p class="caption"&gt;Pure SQL editing pleasure&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The second screenshot shows the new highlighting of errors in the
editor. In this case there's no table called &amp;quot;actor&amp;quot;. When the
execution of a statement results in a SQL error, CrunchyFrog now tries
to find the position of the error in the SQL editor, highlights the
corresponding part in your SQL statement and moves the cursor to the
right place. I found it very handy to have the cursor in place to
correct the error and run the statement again without searching the
error before.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="SQL editor with error message" src="files/cf_errors.png" /&gt;
&lt;p class="caption"&gt;SQL editor with error message&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you're interested in the other, non-visible changes, please read
the &lt;a class="reference external" href="http://packages.python.org/crunchyfrog/changes.html#release-0-4-1-oct-1-2009"&gt;full list of changes&lt;/a&gt;
or just go straight to the &lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/"&gt;project page&lt;/a&gt; and give it a try :) Thanks
to everyone who contributed to this release by sending patches and
submitting issues on the &lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/issues/list"&gt;tracker&lt;/a&gt;!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/0WnVdr7ZGdI" height="1" width="1"/&gt;</summary><category term="crunchyfrog" /><category term="sql" /><category term="python" /><feedburner:origLink>http://andialbrecht.de//blog/2009/10/01/crunchyfrog-041-released.html</feedburner:origLink></entry><entry><title>"Thank you, Mark"</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/MvekV12QAGo/thank-you-mark.html" rel="alternate" /><updated>2009-07-03T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-07-03:/blog/2009/07/03/thank-you-mark.html</id><summary type="html">&lt;p&gt;The quote in the title is something that Dana Colley said after giving
credits to the members of Orchestra Morphine on some show circulating
through the internet about nine years ago. I think this show is still
available on the HI-N-DRY website and I strongly recommend to buy it,
it's a fascinating recording.&lt;/p&gt;
&lt;p&gt;I've choosen this quote because it expresses what I'm thinking today,
the 10th anniversary of &lt;a class="reference external" href="http://www.bostonphoenix.com/archive/music/99/07/08/MARK_SANDMAN.html"&gt;Mark Sandman's passing&lt;/a&gt;. His
music shaped me more than any other music I'm listening too. He had
that idea of a unique sound, not to mention the great lyrics. It's not
easy to explain to someone who never listened to Morphine what this
idea is about. It's not even easy to explain it to people that had to
hear quite a lot of Morphine the past ten (and more) years (many, many
thanks to my family for their patience with my musical taste they call
&amp;quot;a bit strange&amp;quot; from time to time :)...&lt;/p&gt;
&lt;p&gt;There's always something good about unique and innovative ideas: They
make their way through time. Without a doubt, Mark died way too
early. But his musical concept is still present today. Just have a
look at Twinemen, A.K.A.C.O.D., Orchestra Morphine and many other
artists and bands doing covers and claiming Marks musical ideas as a
major influence for their own ideas.&lt;/p&gt;
&lt;p&gt;I discoverd Morphine through a good friend of mine. He visited me
after a travel to Canada somewhere in the late 90's and had those
records in his hand, just saying &amp;quot;Listen to this.&amp;quot; That fundamentally
&lt;a class="reference external" href="http://www.youtube.com/watch?v=M34iZH4-qkI"&gt;changed the way&lt;/a&gt; I was
listening to music up to today. It was this mixture of blues, jazz,
rock and poetry carried through a suprising soundscape of a 2-string
slide bass, baritone saxophone and a straight and driving
drum. Unfortunately I've never had the chance to see them live. We
were about to see them in Brugge in July '99, but that show never
happened.&lt;/p&gt;
&lt;p&gt;I've met a few people since that who found the Morphine sound and
lyrics &amp;quot;so charmingly depressive&amp;quot;. But, hey, I'm not sure if they
really listened... :) One thing I really like about the lyrics in
Sandman songs is that there's always something that spontaneously
comes to your mind in certain situations. Here, it is to just say
&amp;quot;Yes&amp;quot; (in terms of &amp;quot;get in your go-cart and go, little sister&amp;quot;).&lt;/p&gt;
&lt;p&gt;A funny side-note regarding the lyrics: A few years ago I wrote my
thesis about fortune in medieval Arthurian literature and after a busy
day reading manuscripts, making notes und trying to understand the
different concepts of fortune that can be found, I've put the B-Sides
into my player and found myself laughing out loud, Mark summarizes
them all in &amp;quot;Lucky Day&amp;quot;:&lt;/p&gt;
&lt;blockquote&gt;
Now I'm down a little in fact I'm down a lot I'm on a roller
coaster ride that I can't stop My luck has changed but she'll come
back That's the beauty of a game of chance I can't loose forever
but I'm doomed to try Keep on hearing a voice inside Players win
and winners play, have a lucky day There's both the concept of
being at almighty Fortune's mercy and the concept of getting rid of
her by using your own virtue condensed in a few lines while I was
reading verse over verse to find a subtle phrase for it.&lt;/blockquote&gt;
&lt;p&gt;BTW, the version of &amp;quot;Lucky Day&amp;quot; found on the B-Sides is one of my
favorites. The instrumental part preceeding these lines is incredibly
powerfull and one of the most astonishing bits of music I've ever
heard.&lt;/p&gt;
&lt;p&gt;So, let me close with saying thanks too. Thanks to the folks at
&lt;a class="reference external" href="http://www.hindry.com/"&gt;HI-N-DRY&lt;/a&gt; and the &lt;a class="reference external" href="http://www.firstgiving.com/sandmanmusicproject"&gt;Mark Sandman Music
Project&lt;/a&gt; for
spreading a musical idea over years now, keeping up the memory of Mark
Sandman and of course for the honest and good music they make. And
thanks to Mark for giving his potion of uniqueness and sharing his
thoughts and insights in an fascinating musical way. I'm looking
forward to the first downloads on HI-N-DRY today featuring covers of
Sandman songs, poetry and unreleased songs from the depths of the
HI-N-DRY archive!&lt;/p&gt;
&lt;blockquote&gt;
With mistakes yea mistakes and sudden inspirations Edges corners
explosions convections All fast through a slow motion landscape&lt;/blockquote&gt;
&lt;p&gt;P.S.: The lyrics are copied from &lt;a class="reference external" href="http://lukin.com/tos/"&gt;The Other Side&lt;/a&gt;, kudos to Ian Hadfield for keeping this site up!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/MvekV12QAGo" height="1" width="1"/&gt;</summary><category term="morphine" /><category term="music" /><feedburner:origLink>http://andialbrecht.de//blog/2009/07/03/thank-you-mark.html</feedburner:origLink></entry><entry><title>Working on hgsvn</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/2mh-4U3qe0o/working-on-hgsvn.html" rel="alternate" /><updated>2009-06-20T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-06-20:/blog/2009/06/20/working-on-hgsvn.html</id><summary type="html">&lt;p&gt;Yesterday I've taken over maintenance of &lt;a class="reference external" href="http://pypi.python.org/pypi/hgsvn/"&gt;hgsvn&lt;/a&gt; from it's original author
Antoine Pitrou. Many thanks to Antoine at this point for doing a great
job on this tool!&lt;/p&gt;
&lt;p&gt;In case you don't know, hgsvn is &amp;quot;a set of scripts to work locally on
Subversion checkouts using Mercurial.&amp;quot;&lt;/p&gt;
&lt;p&gt;My aim is not to make these scripts a full-blown Subversion extensions
for Mercurial. &lt;a class="reference external" href="http://bitbucket.org/durin42/hgsubversion/wiki/Home"&gt;hgsubversion&lt;/a&gt; or the
&lt;a class="reference external" href="http://www.selenic.com/mercurial/wiki/WorkingWithSubversion#With_Convert_extension"&gt;convert extension&lt;/a&gt;
will probably do a much better job with tighter integration as it's in
the scope of hgsvn. So the main focus is on fixing bugs and to finish
and release the hgpushsvn script that commits changes in your
Mercurial repository back to Subversion.&lt;/p&gt;
&lt;p&gt;The reason why I decided to work on this project is pretty simple:
I've made good experiences with it and I still want to keep using it.&lt;/p&gt;
&lt;p&gt;With &lt;tt class="docutils literal"&gt;hgpushsvn&lt;/tt&gt; the tool provides three scripts for the most basic
tasks:&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;hgimportsvn&lt;/tt&gt; initializes a hg repository and fetches the sources from
svn, optionally starting at a specific revision.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;hgpullsvn&lt;/tt&gt; pulls new change sets from svn to your local hg repository.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;hgpushsvn&lt;/tt&gt; pushes back your local commits to the svn repository.&lt;/p&gt;
&lt;p&gt;Using hgsvn for a while now, I'm feeling very comfortable with these
scripts and they fit very well into my workflow.&lt;/p&gt;
&lt;p&gt;The next things I'm about to do are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;fix the unittests, at least one is broken ATM&lt;/li&gt;
&lt;li&gt;setup a buildbot to have some automated tests&lt;/li&gt;
&lt;li&gt;finish hgpullsvn&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you're using hgsvn, please file bug reports or submit patches on
the &lt;a class="reference external" href="http://bitbucket.org/andialbrecht/hgsvn/issues/"&gt;issue tracker&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/2mh-4U3qe0o" height="1" width="1"/&gt;</summary><category term="hgsvn" /><category term="mercurial" /><feedburner:origLink>http://andialbrecht.de//blog/2009/06/20/working-on-hgsvn.html</feedburner:origLink></entry><entry><title>How Do You Look When Merging Fails ;-)</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/hMRzKl2ApGg/how-do-you-look-when-merging-fails-.html" rel="alternate" /><updated>2009-05-09T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-05-09:/blog/2009/05/09/how-do-you-look-when-merging-fails-.html</id><summary type="html">&lt;p&gt;There was a Simpsons episode, I can't recall correctly, but I think
Bart recorded Lisa when her heart broke and he watched it in slow
motion to stop exactly at that point.&lt;/p&gt;
&lt;p&gt;I thought of this episode yesterday while playing around with my
laptop's webcam and a Python shell. Finally I wrote a little fun
script that does almost the same: Just register it as a hg hook and it
takes a picture of you exactly at the unique moment when merging fails
and it sends it directly and without any further questions to &lt;a class="reference external" href="http://twitpic.com/"&gt;Twitpic&lt;/a&gt; and &lt;a class="reference external" href="http://twitter.com/"&gt;Twitter&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;CVtypes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twitpic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TwitPicAPI&lt;/span&gt;


&lt;span class="n"&gt;DEVICE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;TWITTER_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xxx&amp;#39;&lt;/span&gt;  &lt;span class="c"&gt;# CHANGE THIS!&lt;/span&gt;
&lt;span class="n"&gt;TWITTER_PWD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xxx&amp;#39;&lt;/span&gt;   &lt;span class="c"&gt;# CHANGE THIS!&lt;/span&gt;
&lt;span class="c"&gt;# This is the time in seconds you need to realize that the merge has&lt;/span&gt;
&lt;span class="c"&gt;# failed. When setting this consider that it already takes about a second&lt;/span&gt;
&lt;span class="c"&gt;# for the camera to take the picture. &amp;amp;quot;0&amp;amp;quot; means no delay ;-)&lt;/span&gt;
&lt;span class="n"&gt;EMOTIONAL_SLUGGISHNESS_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;grab_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateCameraCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;how_do_you_look&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;HG_ERROR&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="c"&gt;# hmpf, maybe next time...&lt;/span&gt;
    &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkstemp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.jpg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;EMOTIONAL_SLUGGISHNESS_RATE&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EMOTIONAL_SLUGGISHNESS_RATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;grab_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;twit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TwitPicAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TWITTER_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TWITTER_PWD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;retcode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;twit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_to_twitter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Another merge failed.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;how_do_you_look&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You'll need the &lt;a class="reference external" href="http://sourceforge.net/project/showfiles.php?group_id=82407&amp;amp;package_id=232299"&gt;CVtypes&lt;/a&gt;
OpenCV wrapper and this &lt;a class="reference external" href="http://code.google.com/p/python-twitpic/"&gt;Twitpic Python module&lt;/a&gt;. I've patched the twitpic
module to support messages. Have a look at &lt;a class="reference external" href="http://code.google.com/p/python-twitpic/issues/detail?id=2"&gt;this issue&lt;/a&gt; if it's
already supported, otherwise a diff that adds the message keyword is
attached to the issue. To use it as a Mercurial hook just add to
.hg/hgrc:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;[hooks]
update =  /path/to/the/above/script.py
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and make the script executable.&lt;/p&gt;
&lt;p&gt;The results are pretty good :)&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="Another merge failed" src="files/hgfail.jpg" /&gt;
&lt;p class="caption"&gt;&lt;a class="reference external" href="http://twitpic.com/4txls"&gt;http://twitpic.com/4txls&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Have fun!&lt;/p&gt;
&lt;p&gt;BTW, the way how to access the camera is inspired by this &lt;a class="reference external" href="http://blog.jozilla.net/2008/06/27/fun-with-python-opencv-and-face-detection/"&gt;nice blog
post about face recognition&lt;/a&gt;
using OpenCV.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit (2009-05-12):&lt;/strong&gt; It was Ralph, not Lisa. Thanks Florian!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/hMRzKl2ApGg" height="1" width="1"/&gt;</summary><category term="python" /><category term="mercurial" /><category term="opencv" /><feedburner:origLink>http://andialbrecht.de//blog/2009/05/09/how-do-you-look-when-merging-fails-.html</feedburner:origLink></entry><entry><title>App Engine Tracebacks via E-Mail</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/f0_TSXmp40c/app-engine-tracebacks-via-e-mail.html" rel="alternate" /><updated>2009-04-30T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-04-30:/blog/2009/04/30/app-engine-tracebacks-via-e-mail.html</id><summary type="html">&lt;p&gt;&lt;strong&gt;Edit 2009-09-04:&lt;/strong&gt; Starting with App Engine SDK 1.2.5 &lt;a class="reference external" href="http://googleappengine.blogspot.com/2009/09/app-engine-sdk-125-released-for-python.html"&gt;released
today&lt;/a&gt;
there's now a much smarter approach. See the &lt;a class="reference external" href="http://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/ext/ereporter/ereporter.py"&gt;docstring of the
ereporter module&lt;/a&gt;
for details!&lt;/p&gt;
&lt;p&gt;Django's &lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/howto/error-reporting/#howto-error-reporting"&gt;error reporting via e-mail&lt;/a&gt;
is something I've missed for my App Engine applications. If you've
followed the instructions on &lt;a class="reference external" href="http://code.google.com/appengine/articles/django10_zipimport.html"&gt;how to use Django on App Engine&lt;/a&gt;
errors are already logged. But honestly, I don't check the logs very
often and sometimes it's just a silly typo that's fixed in a minute
;-)&lt;/p&gt;
&lt;p&gt;So if you've followed the instructions you can simply extend the
log_exception function to send an email after the exception is logged:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwds&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Django signal handler to log an exception.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;excinfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excinfo&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Exception in request: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;# Send an email to the admins&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;request&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;repr_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;request&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;repr_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Request repr() not available.&amp;#39;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;repr_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Request not available.&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Application: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Version: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
         &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;APPLICATION_ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;CURRENT_VERSION_ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;excinfo&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;repr_request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;[&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;APPLICATION_ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_mail_to_admins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;APP_ADMIN@EXAMPLE.COM&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Make sure to replace &amp;quot;&lt;a class="reference external" href="mailto:APP_ADMIN&amp;#64;EXAMPLE.COM"&gt;APP_ADMIN&amp;#64;EXAMPLE.COM&lt;/a&gt;&amp;quot; with the email address of
an admin for this application and to add these two imports somewhere
at the top of your &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;traceback&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.appengine.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You don't need to comment this when developing your app to avoid lots
of useless mails. The dev server doesn't fully implement
&lt;tt class="docutils literal"&gt;send_mail_to_admins()&lt;/tt&gt; most likely as there's no real concept of an
designated admin user in the SDK. It just writes a short message to
the log, even if the sendmail option is enabled.&lt;/p&gt;
&lt;p&gt;P.S.: If you use webapp instead of Django have a look at the &lt;a class="reference external" href="http://sites.google.com/site/io/best-practices---building-a-production-quality-application-on-google-app-engine"&gt;seventh
slide of this presentation&lt;/a&gt;
by Ken Ashcraft. He shows a similar implementation for webapp based
applications.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/f0_TSXmp40c" height="1" width="1"/&gt;</summary><category term="App Engine" /><category term="Python" /><category term="Django" /><feedburner:origLink>http://andialbrecht.de//blog/2009/04/30/app-engine-tracebacks-via-e-mail.html</feedburner:origLink></entry><entry><title>SQL Parsing with Python, Pt. I</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/tKcr1Bh0LYo/sql-parsing-with-python-pt-i.html" rel="alternate" /><updated>2009-03-29T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-03-29:/blog/2009/03/29/sql-parsing-with-python-pt-i.html</id><summary type="html">&lt;p&gt;Some time ago &lt;a class="reference external" href="http://andialbrecht.wordpress.com/2008/09/11/on-crunchyfrog-03/"&gt;I was in search&lt;/a&gt;
for some kind of SQL parser module for Python. As you can guess, my
search wasn't really successfull. &lt;a class="reference external" href="http://pyparsing.wikispaces.com/"&gt;pyparsing&lt;/a&gt; needs a grammar, but I'm not
really interested in writing a full-blown grammar for SQL with it's
various dialects. AFAICT &lt;a class="reference external" href="http://cvs.zope.org/*checkout*/Packages/GadflyDA/gadfly/gadfly.html"&gt;Gadfly&lt;/a&gt;
implements a customized SQL parser, but I was not able to figure out
how to use it. And there's &lt;a class="reference external" href="http://pypi.python.org/pypi/sqlliterals/"&gt;sqlliterals&lt;/a&gt;, but as the name suggests
it's just for identifying literals within statements.&lt;/p&gt;
&lt;p&gt;I expect such a module to do the following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It should be fast ;-)&lt;/li&gt;
&lt;li&gt;scalable - what the parser needs to know about a string containing
SQL statements depends on what I want to do with it. If I just want
to split that string in separate statements I don't need to know as
much as when I want to know what identifiers occur in it.&lt;/li&gt;
&lt;li&gt;non-validating - Parsing shouldn't fail if the statement is
syntactically incorrect as I want to use it for an SQL editor where
the statements are (naturally) incorrect most of the time.&lt;/li&gt;
&lt;li&gt;some beautifying is a nice-to-have - We all know SQL generated by a
script and copied from a logging output to a database front-end - it
always looks ugly and is pretty unreadable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only thing I've found that comes close to my needs is &lt;a class="reference external" href="http://pygments.org/"&gt;Pygments&lt;/a&gt; (yes, the syntax highlighter). It does a
really good job highlighting SQL, so it &amp;quot;understands&amp;quot; at least
something. To be concrete, it has a rock-solid and fast lexer. There's
a &lt;cite&gt;FAQ &amp;lt;http://pygments.org/faq/#understand-code&amp;gt;&lt;/cite&gt; entry if it's
possible to use Pygments for progamming language processing. The
answer is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Pygments lexing machinery is quite powerful can be used to
build &lt;strong&gt;lexers&lt;/strong&gt; for basically all languages. However, &lt;strong&gt;parsing&lt;/strong&gt;
them is not possible, though some lexers go some steps in this
direction in order to e.g. highlight function names differently.&lt;/p&gt;
&lt;p&gt;Also, error reporting is not the scope of Pygments. It focuses on
correctly highlighting syntactically valid documents, not finding
and compensating errors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's a nice distinction between lexers and parser in this
answer. The former does lexical and the latter syntactical analysis of
a given stream of characters. For my needs this led to a two step
process: first to do the lexing using Pygments mechanism (BTW, read
&amp;quot;focuses on [...] syntactically valid documents, not finding [...]
errors&amp;quot; as &amp;quot;non-validating&amp;quot;) and then to add parsing on top of the
token stream.&lt;/p&gt;
&lt;p&gt;So I stripped the lexing mechanism for SQL out of Pygments to get rid
of some overhead not needed for my purposes (e.g. loading of
extensions). The only changes to the Pygments tokenizer was to replace
a huge regex for finding keywords by a dictionary-based lookup and to
add a few new tokens to make live easier in the second processing
stage. The achieved performance improvement by replacing the regex
doesn't play a significant role for Pygments, it just speeds up the
lexing a bit.&lt;/p&gt;
&lt;p&gt;In the second step the very efficient token-type/value stream
generated in step 1 is grouped together in nested lists of simple
classes. Instantiating a bunch of classes results in a performance
loss, but with the benefit of some helpful methods for analyzing the
now classified tokens. As the classes are pretty simple most of the
performance loss was recaptured by using &lt;a class="reference external" href="http://docs.python.org/glossary.html#term-slots"&gt;slots&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The grouping mechanism is again non-validating. It tries to find
patterns like identifiers and their aliases or WHERE clauses and their
conditions in the token stream, but it just leaves the tokens
unchanged if it doesn't find what it's looking for. So it actually
defines some sort of &amp;quot;grammar&amp;quot;, but very unrestrictive. That means
that the quality of answers to questions like &amp;quot;Is there a WHERE
clause?&amp;quot; or &amp;quot;Which tables are affected?&amp;quot; heavily depends on the
syntactical clearness of the given statement at the time when it's
processed and it's up to the user or application using the module to
intrepet the answers right. As the module is seen as a helper during
the develoment process of SQL statements it doesn't insist on
syntactical correctness as the targeted database system would do when
the statement is executed.&lt;/p&gt;
&lt;p&gt;All in all these are the two (simple) processing steps:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Lexing a given string using regular expressions and generate a
token-type/value stream (in fact, this is the Pygments lexer).&lt;/li&gt;
&lt;li&gt;Parsing&lt;ol class="arabic"&gt;
&lt;li&gt;Instantiate a simple token class for each item in the stream to
ease syntactical analysis.&lt;/li&gt;
&lt;li&gt;Group those tokens in nested lists by using simple functions by
finding patterns.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Depending on what should be done with the statements, only the first
step is required. For example, splitting statements or simple
formatting rules don't require parsing.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://andialbrecht.de/blog/2009/03/29/sql-parsing-with-python-pt-ii.html"&gt;Pt. II&lt;/a&gt;
of this blog post will cover the two remaining points: scalibality and
the beautifiying goodie. And it will give a closer look at the module
I came up with, including some performance stats. For the impatient:
the source code of this module is available &lt;a class="reference external" href="http://code.google.com/p/python-sqlparse/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/tKcr1Bh0LYo" height="1" width="1"/&gt;</summary><category term="Python" /><category term="SQL" /><category term="parsing" /><feedburner:origLink>http://andialbrecht.de//blog/2009/03/29/sql-parsing-with-python-pt-i.html</feedburner:origLink></entry><entry><title>SQL Parsing with Python, Pt. II</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/Im3p8MSybFI/sql-parsing-with-python-pt-ii.html" rel="alternate" /><updated>2009-03-29T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-03-29:/blog/2009/03/29/sql-parsing-with-python-pt-ii.html</id><summary type="html">&lt;p&gt;After I've described some basics in &lt;a class="reference external" href="http://andialbrecht.wordpress.com/2009/03/29/sql-parsing-with-python-pt-i/"&gt;part I&lt;/a&gt;
let's have a closer look to the actual Python module called
sqlparse. First off, have a look at the &lt;a class="reference external" href="http://code.google.com/p/python-sqlparse/"&gt;project page&lt;/a&gt; on how to download and
install this module in case you're interested. But now, let's have
some fun...&lt;/p&gt;
&lt;p&gt;The API of the module is pretty simple. It provides three top-level
functions on module level: &lt;tt class="docutils literal"&gt;sqlparse.split(sql)&lt;/tt&gt; splits sql into
separate statements, &lt;tt class="docutils literal"&gt;sqlparse.parse(sql)&lt;/tt&gt; parses sql and returns a
tree-like structure and &lt;tt class="docutils literal"&gt;sqlparse.format(sql, **kwds)&lt;/tt&gt; returns a
beautified version of sql according to kwds.&lt;/p&gt;
&lt;p&gt;As mentioned in my previous post, what effort needs to be done to
build the return values depends on what lexing and parsing work is
needed to find the result. For example &lt;tt class="docutils literal"&gt;sqlparse.split()&lt;/tt&gt; does the
following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Generate a token-type/value stream basically with a copy of Pygments lexer&lt;/li&gt;
&lt;li&gt;Apply a filter (in terms of a Pygments stream filter) to find statements&lt;/li&gt;
&lt;li&gt;Serialize the token stream back to unicode&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;sqlparse.parse()&lt;/tt&gt; does all the grouping work and
&lt;tt class="docutils literal"&gt;sqlparse.format()&lt;/tt&gt; runs through those groups, modifies them
according to the given formatting rules and finally converts it back
to unicode.&lt;/p&gt;
&lt;p&gt;Here's an example session in a Python shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sqlparse&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Splitting statements:&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;select * from foo; select * from bar;&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Formatting statemtents:&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;select * from foo where id in (select id from bar);&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reindent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;upper&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;SELECT *&lt;/span&gt;
&lt;span class="go"&gt;FROM foo&lt;/span&gt;
&lt;span class="go"&gt;WHERE id IN&lt;/span&gt;
&lt;span class="go"&gt;  (SELECT id&lt;/span&gt;
&lt;span class="go"&gt;   FROM bar);&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Parsing&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;select * from &amp;quot;someschema&amp;quot;.&amp;quot;mytable&amp;quot; where id = 1&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;&amp;lt;&amp;lt; stmt = res[0]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_unicode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c"&gt;# converting it back to unicode&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;&amp;lt;&amp;lt; u&amp;#39;select * from &amp;quot;someschema&amp;quot;.&amp;quot;mytable&amp;quot; where id = 1&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# This is how the internal representation looks like:&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;span class="go"&gt;[&amp;lt;DML &amp;#39;select&amp;#39; at 0x2a8dba8&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8dc00&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Wildcard &amp;#39;*&amp;#39; at 0x2a8dc58&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8dcb0&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Keyword &amp;#39;from&amp;#39; at 0x2a8dd08&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8dd60&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Identifier &amp;#39;&amp;quot;somes...&amp;#39; at 0x2a8f628&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8dec0&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Where &amp;#39;where ...&amp;#39; at 0x2a8f518&amp;gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, how does the grouping work? Grouping is done with a set of simple
functions. Each function searches for a simple pattern and if it finds
one a new group is built. Let's have a look at the function that finds
the &lt;tt class="docutils literal"&gt;WHERE&lt;/tt&gt; clauses.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;group_where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;group_where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sgroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sgroup&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_sublists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_next_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;WHERE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stopwords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ORDER&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;GROUP&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;LIMIT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;UNION&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tidx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_next_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tidx&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stopwords&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c"&gt;# WHERE is at the end of the statement&lt;/span&gt;
            &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens_between&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token_next_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;WHERE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;tlist&lt;/tt&gt; is a list of tokens, possible subgroups are handled first
(bottom-up approach). Then it grabs the first occuring &amp;quot;WHERE&amp;quot; and
looks for the next matching stop word. I'm pretty unsure if the stop
words approach is right here, but at least it works for now... If it
finds a stop word, a group using the class Where is created, the
tokens between &lt;tt class="docutils literal"&gt;WHERE&lt;/tt&gt; and the stop word are attached to it and -
still within the while loop - the next &lt;tt class="docutils literal"&gt;WHERE&lt;/tt&gt; keyword is used as
the next starting point.&lt;/p&gt;
&lt;p&gt;So why not use a grammar here? At first, this piece of code is pretty
simple and easy to maintain. But it can also handle grammatically
incorrect statements more lazily, e.g. it's no problem to add an if
clause that - when for example an unexpected token occurs - the
function just jumps to the next occurance of &lt;tt class="docutils literal"&gt;WHERE&lt;/tt&gt; without
changing anything or even raising an exception. To achieve this the
token classes provide helper functions to inspect the surroundings of
an occurrence (in fact, just simple list operations). There's no
limitation what a grouping function can do with the given token list,
so you could even &amp;quot;guess&amp;quot; a proper group with some nasty algorithm.&lt;/p&gt;
&lt;p&gt;The current problem with this approach is performance. Here are some
numbers:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="50%" /&gt;
&lt;col width="26%" /&gt;
&lt;col width="24%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Size&lt;/th&gt;
&lt;th class="head"&gt;split()&lt;/th&gt;
&lt;th class="head"&gt;parse()&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;100kb (20600 tokens)&lt;/td&gt;
&lt;td&gt;0.3 secs.&lt;/td&gt;
&lt;td&gt;1.8 secs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1.9MB (412500 tokens)&lt;/td&gt;
&lt;td&gt;5.53 secs.&lt;/td&gt;
&lt;td&gt;37 secs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most of the performance is lost when giving up the stream-oriented
approach in the parsing phase. The numbers are based on the first
unrevised working version. I expect performance improvents especially
in the way how token lists are handled behind the scenes with upcoming
versions. For real life statements the parser behaves quite well. BTW,
the Pygments lexer takes about 6 seconds (compared to 5.5 secs. for
splitting) for the 1.9MB of SQL.&lt;/p&gt;
&lt;p&gt;The non-validating approach is a disadvantage too. You'll never know
if a statement is valid. You can even parse &lt;a class="reference external" href="http://books.google.com/books?id=fRaLfuvzem8C&amp;amp;dq=hartmann+iwein&amp;amp;printsec=frontcover&amp;amp;source=bl&amp;amp;ots=8xNAgOe5DW&amp;amp;sig=jOXtB-oEB7GZHBfRqqLHlqG_GCM&amp;amp;hl=en&amp;amp;ei=EJXPSY6_N4OP-Abhy_DUBw&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;resnum=1&amp;amp;ct=result#PPA3,M1"&gt;middle high german
phrases&lt;/a&gt;
and receive a result:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;swer an rehte güete wendet sîn gemüete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;span class="go"&gt;[&amp;lt;Identifier &amp;#39;swer an&amp;#39; at 0x2a8f0d8&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8d788&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Identifier &amp;#39;rehte ...&amp;#39; at 0x2a8f1e8&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8d8e8&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Identifier &amp;#39;wendet...&amp;#39; at 0x2a8f2f8&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Whitespace &amp;#39; &amp;#39; at 0x2a8da48&amp;gt;,&lt;/span&gt;
&lt;span class="go"&gt; &amp;lt;Identifier &amp;#39;gemüete&amp;#39; at 0x2a8f408&amp;gt;]&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's up to the user of this module to provide suitable input and to
interpret the output. Furthermore the parser only supports not every
nifty edge of an SQL dialect. Currently it's mostly ANSI-SQL with some
PostgreSQL specific stuff. But it should be easy to implement further
grouping functions to provide more SQL varieties.&lt;/p&gt;
&lt;p&gt;The splitting feature is currently used by &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/svn/tags/0.3.4/utils/command/build_manpage.py"&gt;CrunchyFrog&lt;/a&gt;
and does a pretty good job there. I assume that SQL splitting works
stable and reliable in most cases. Beautifiying and parsing is very
new in the module and full functionality needs to be proven with
time. Luckily the top-level API with it's three functions is damn
simple and keeps the doors open for significant changes behind the
scenes if they're needed.&lt;/p&gt;
&lt;p&gt;The sources of the sqlparse module are currently hosted on &lt;a class="reference external" href="http://github.com/andialbrecht/python-sqlparse"&gt;github.com&lt;/a&gt; but may move to
Google Code anytime soon. Refer to the &lt;a class="reference external" href="http://code.google.com/p/python-sqlparse/"&gt;project page&lt;/a&gt; on Google Code for
downloads and how to access the sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; The current repository is once again on &lt;a class="reference external" href="http://github.com/andialbrecht/sqlparse"&gt;http://github.com/andialbrecht/sqlparse&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition there's a &lt;a class="reference external" href="http://sqlformat.appspot.com/"&gt;simple AppEngine application&lt;/a&gt; that exposes the formatting features
as an online service.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/Im3p8MSybFI" height="1" width="1"/&gt;</summary><category term="Python" /><category term="SQL" /><category term="Parsing" /><feedburner:origLink>http://andialbrecht.de//blog/2009/03/29/sql-parsing-with-python-pt-ii.html</feedburner:origLink></entry><entry><title>Creating Man Pages Using optparse and distutils</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/DnH7cfZHQ8Q/creating-man-pages-using-optparse-and-distutils.html" rel="alternate" /><updated>2009-03-17T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-03-17:/blog/2009/03/17/creating-man-pages-using-optparse-and-distutils.html</id><summary type="html">&lt;p&gt;This blog post describes how to generate a simple man page during
build time for Python applications using &lt;tt class="docutils literal"&gt;distutils&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;optparse&lt;/tt&gt;. Technically this post describes how to write a custom
HelpFormatter and a custom build command.&lt;/p&gt;
&lt;p&gt;For GUI applications command line options usually bother me not
much. Just in some rare cases, mainly when debugging or feeling
unlucky with the startup behavior, I'm interested in these
options. Even then I prefer to use &amp;quot;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/tt&gt;&amp;quot;. BTW, for the &amp;quot;real&amp;quot;
command line tools like &lt;tt class="docutils literal"&gt;sed&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;ls&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;grep&lt;/tt&gt; this is totally
different.&lt;/p&gt;
&lt;p&gt;So, due to my lack of interest in man pages for GUI applications I've
shipped the previous release of &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/"&gt;CrunchyFrog&lt;/a&gt; along with a totally out-dated
man page. Moreover I really don't like writing man pages or keeping
them in sync with the actual command line options already documented
by &lt;tt class="docutils literal"&gt;OptionParser&lt;/tt&gt;'s help features...&lt;/p&gt;
&lt;p&gt;Did I say &lt;em&gt;&amp;quot;already documented&amp;quot;&lt;/em&gt;? I'm not a friend of doing things
twice, reusing that &amp;quot;already documented&amp;quot; should be very pleasant to go
to get rid of my problem.&lt;/p&gt;
&lt;div class="section" id="what-to-put-on-a-man-page"&gt;
&lt;h2&gt;What To Put On A Man Page?&lt;/h2&gt;
&lt;p&gt;The short answer is: It should mention the application's name, it's
purpose, how to start it, who wrote it, where to find additional
information and what command line options it provides. Or, in terms of
man page sections: &amp;quot;NAME&amp;quot;, &amp;quot;SYNOPSIS&amp;quot;, &amp;quot;DESCRIPTION&amp;quot;, &amp;quot;OPTIONS&amp;quot;, &amp;quot;SEE
ALSO&amp;quot; and &amp;quot;AUTHOR&amp;quot; (as you may already have noticed, I'm focussing
just on man page (1) in this post). Of course, there are more possible
sections, but after inspecting some randomly choosen man pages for GUI
applications on my system, it seems that those sections are the most
common ones.&lt;/p&gt;
&lt;p&gt;(If you are interested in the full answer, read the &lt;a class="reference external" href="http://tldp.org/HOWTO/Man-Page/q3.html"&gt;Linux Man Page
Howto&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Now back to our Python application that uses &lt;tt class="docutils literal"&gt;optparse&lt;/tt&gt; to provide
command line options.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;optparse&lt;/tt&gt; comes with a pretty good out-of-the-box &lt;a class="reference external" href="http://docs.python.org/library/optparse.html#generating-help"&gt;help feature&lt;/a&gt;. As
stated in the docs the primary purpose is to assist in creating
user-friendly command-line interfaces and not to generate help
documents (like man pages). But the ability to write a custom
HelpFormatter makes it easy to generate such documents.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="writing-a-custom-helpformatter"&gt;
&lt;h2&gt;Writing a Custom HelpFormatter&lt;/h2&gt;
&lt;p&gt;Let's have a look at the custom formatter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ManPageFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HelpFormatter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent_increment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_help_position&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;short_first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Constructor. Unfortunately HelpFormatter is no new-style class.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;optparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HelpFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent_increment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;max_help_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;short_first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Prepares txt to be used in man pages.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_usage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Formate the usage/synopsis line.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;Format a heading.&lt;/span&gt;
&lt;span class="sd"&gt;        If level is 0 return an empty string. This usually is the string &amp;quot;Options&amp;quot;.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.TP&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Format a single option.&lt;/span&gt;

&lt;span class="sd"&gt;        The base class takes care to replace custom optparse values.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;option_strings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.TP&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;.B &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;help_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The three methods starting with &lt;tt class="docutils literal"&gt;format_*&lt;/tt&gt; do all the formatting for
us. The base class has additional formatting methods, but there's no
need to overwrite them as they do nothing special except wrapping
lines to the desired width.&lt;/p&gt;
&lt;p&gt;To use a custom formatter either give it as an parameter to the
OptionParser constructor or use&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ManPageFormatter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;if your option parser is already instantiated.&lt;/p&gt;
&lt;p&gt;Now, when you call &lt;tt class="docutils literal"&gt;parser.format_option_help()&lt;/tt&gt; you'll see a man
page fragment with all options (including nicely formatted option
groups). Maybe you wonder why not use &lt;tt class="docutils literal"&gt;parser.print_help()&lt;/tt&gt; here,
which normally adds a nice usage line and the description if given to
the output. The reason for that is quite simple: Many of the
documentation features of &lt;tt class="docutils literal"&gt;optparse&lt;/tt&gt; are optional. If there's no usage
defined, no usage line will be formatted. So using it for the SYNOPSIS
section and eventually the header of the man page isn't a good
idea. Everything surrounding the OPTIONS section of the man page
should be generated by something else, that takes care to include a
proper header and footer. We are using the custom formatter just for
the options themself.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="adding-a-custom-build-command"&gt;
&lt;h2&gt;Adding a Custom Build Command&lt;/h2&gt;
&lt;p&gt;To write the rest of the man page we're using a custom build command
for &lt;tt class="docutils literal"&gt;distutils&lt;/tt&gt;. This command is executed when you run &lt;tt class="docutils literal"&gt;python
setup.py build&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;install&lt;/tt&gt; that depends on build. There's a nice
side-effect when generating the man page on build time: You can simply
skip this step when the &lt;tt class="docutils literal"&gt;setup.py&lt;/tt&gt; commands are run on an OS that
doesn't know what a man pages is ,-)&lt;/p&gt;
&lt;p&gt;The skeleton of a distutils command looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;build_manpage command -- Generate man page from optparse/dist metadata.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;distutils.command.build&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;distutils.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;build_manpage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Generate man page.&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;user_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="c"&gt;# Do something useful!&lt;/span&gt;


&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub_commands&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;build_manpage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, &amp;quot;build_manpage&amp;quot; is added as a sub-command to the build
command and the build_manpage class has some methods for option
handling and a &lt;tt class="docutils literal"&gt;run()&lt;/tt&gt; method.&lt;/p&gt;
&lt;p&gt;To use this command add the following to your &lt;tt class="docutils literal"&gt;setup.py&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;distutils.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;build_manpage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;build_manpage&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;cmdclass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;build_manpage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;build_manpage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="adding-command-options"&gt;
&lt;h3&gt;Adding Command Options&lt;/h3&gt;
&lt;p&gt;Generating the man page needs some options. The &lt;tt class="docutils literal"&gt;build_manpage&lt;/tt&gt;
commands needs to know where to write the generated man page and which
option parser it should use. To achieve this we have to modify the
&lt;tt class="docutils literal"&gt;user_options&lt;/tt&gt; attribute and the two corresponding methods in our
skeleton class:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;user_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;output=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;O&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;output file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;parser=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;module path to optparser (e.g. mymod:func&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;DistutilsOptionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s"&gt; option is required&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;DistutilsOptionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;parser&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s"&gt; option is required&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mod_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fromlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;__import__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromlist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fromlist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func_name&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ManPageFormatter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Writing man page &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;user_options&lt;/tt&gt; is a list of 3-tuples (long option, short option,
help), &lt;tt class="docutils literal"&gt;initialize_options()&lt;/tt&gt; sets default values and
&lt;tt class="docutils literal"&gt;finalize_options()&lt;/tt&gt; makes sure that the given options are sane (nb,
the Command class has some validation functions, unfortunately you'll
have to read the source to find them...). Run &lt;tt class="docutils literal"&gt;setup.py build_manpage
&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/tt&gt; to see how to use your options on the command line. Besides
setting these options on the command line you can set them in a file
called &lt;tt class="docutils literal"&gt;setup.cfg&lt;/tt&gt; too:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;[build_manpage]
output=data/mymanpage.1
parser=myapp.somemod:get_parser
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Both options are required. &lt;tt class="docutils literal"&gt;output&lt;/tt&gt; tells the command to which file
it should write the generated man page, &lt;tt class="docutils literal"&gt;parser&lt;/tt&gt; is a string
pointing to a function that returns an &lt;tt class="docutils literal"&gt;OptionParser&lt;/tt&gt; instance (a dotted
module path with a function name separated by a single colon).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="putting-it-all-together"&gt;
&lt;h2&gt;Putting It All Together&lt;/h2&gt;
&lt;p&gt;This is done by the &lt;tt class="docutils literal"&gt;run()&lt;/tt&gt; method, of course. In
&lt;tt class="docutils literal"&gt;finalize_options()&lt;/tt&gt; the option parser was imported and our
&lt;tt class="docutils literal"&gt;ManPageFormatter&lt;/tt&gt; was attached to it. The &lt;tt class="docutils literal"&gt;run()&lt;/tt&gt; method now does
the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;manpage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;manpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_write_header&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;manpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_write_options&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;manpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_write_footer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manpage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let's have a look at the &lt;tt class="docutils literal"&gt;_write_header()&lt;/tt&gt; helper function
(&lt;tt class="docutils literal"&gt;_write_options()&lt;/tt&gt; is basically a call to the formatter and
&lt;tt class="docutils literal"&gt;_write_footer()&lt;/tt&gt; just adds the author and a link to the homepage):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_write_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;appname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.TH &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; 1 &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                  &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_today&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%Y&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;-%m&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_description&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                         &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.SH NAME&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;synopsis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_usage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;synopsis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;synopsis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;synopsis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.SH SYNOPSIS&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;.B &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                                  &lt;span class="n"&gt;synopsis&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;long_desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_long_description&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;long_desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.SH DESCRIPTION&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;long_desc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, this method makes use of another advantage of using a
&lt;tt class="docutils literal"&gt;distutils&lt;/tt&gt; command. It fetches some information not covered by
&lt;tt class="docutils literal"&gt;optparse&lt;/tt&gt; from distutils' meta data and in addition takes care of
optparse's optional usage and description attributes.&lt;/p&gt;
&lt;p&gt;The complete implementation of the &lt;tt class="docutils literal"&gt;build_manpage&lt;/tt&gt; command is
available &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/svn/tags/0.3.4/utils/command/build_manpage.py"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is how the resulting man page for CrunchyFrog looks like:&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="files/cfmanpage_large.jpg"&gt;&lt;img alt="Man page of CrunchyFrog" src="files/cfmanpage.jpg" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;&lt;tt class="docutils literal"&gt;man crunchyfrog&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I'll have to admit, it's a very simple approach to have a man page
without writing it and - even worse - maintaining it. But at least it
solved my problem to distribute a GUI application with an up-to-date
man page. This approach has no extra requirements as everything needed
can be found in Python's stdlib and the man page is generated when
needed. Luckily the build command is extensible, so it's no problem to
add additional sections (what about connection the script to a bug
tracker and add a hopefully not too long BUGS section?).&lt;/p&gt;
&lt;p&gt;P.S.: In case your interested in another formatter example, I've recently written another HelpFormatter that creates a Google Code Wiki page for &lt;a class="reference external" href="http://codereview.appspot.com/"&gt;Rietveld&lt;/a&gt;'s upload.py. The source are available &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/svn/tags/0.3.4/utils/command/build_manpage.py"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit (2009-03-25):&lt;/strong&gt; There's a nice &lt;a class="reference external" href="http://bazaar.launchpad.net/~necoro/portato/trunk/revision/387"&gt;patch&lt;/a&gt; written by René Neumann that adds a SEE ALSO option to the distutils command.&lt;/p&gt;
&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/DnH7cfZHQ8Q" height="1" width="1"/&gt;</summary><category term="python" /><category term="man page" /><category term="optparse" /><feedburner:origLink>http://andialbrecht.de//blog/2009/03/17/creating-man-pages-using-optparse-and-distutils.html</feedburner:origLink></entry><entry><title>Something completely different</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/vzxdDpjhLc4/something-completely-different.html" rel="alternate" /><updated>2009-02-06T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-02-06:/blog/2009/02/06/something-completely-different.html</id><summary type="html">&lt;p&gt;Spent half of the evening crawling through sites I've rarely visited
in the last few months.&lt;/p&gt;
&lt;p&gt;There are at least two &amp;quot;new&amp;quot; Sandman songs on &lt;a class="reference external" href="http://www.myspace.com/marksandmanmusicproject"&gt;MySpace&lt;/a&gt;. Ok, they're not
new at all, but mystic... ;-) BTW if you haven't did so already,
support the Mark Sandman Music Project in any way...&lt;/p&gt;
&lt;p&gt;While clicking links back and forth I came across a Hi-N-Dry
photostream on &lt;a class="reference external" href="http://www.flickr.com/photos/sandmanmusicproject/"&gt;Flickr&lt;/a&gt;. hm.. the new
rooms look way too clean compared to the legendary old &lt;a class="reference external" href="http://hi-n-dry.com/images/contact.jpg"&gt;ones&lt;/a&gt; (but that's maybe just
because they're new...)&lt;/p&gt;
&lt;p&gt;While talking about fresh music, you definitely have to &lt;a class="reference external" href="http://www.myspace.com/thechipsmithproject"&gt;listen&lt;/a&gt; or to &lt;a class="reference external" href="http://www.youtube.com/user/chipsmithproject"&gt;view&lt;/a&gt; the Chip Smith
Project! What a refreshing music, ol' Chip Smith must have been a very
honorable man!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/vzxdDpjhLc4" height="1" width="1"/&gt;</summary><feedburner:origLink>http://andialbrecht.de//blog/2009/02/06/something-completely-different.html</feedburner:origLink></entry><entry><title>A cleaner UI for CrunchyFrog</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/hknd9Og7yGE/a-cleaner-ui-for-crunchyfrog.html" rel="alternate" /><updated>2009-01-29T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2009-01-29:/blog/2009/01/29/a-cleaner-ui-for-crunchyfrog.html</id><summary type="html">&lt;p&gt;Today I've released a new version of &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/"&gt;CrunchyFrog&lt;/a&gt;. Besides beeing mainly a bug
fix release, CrunchyFrog 0.3.2 introduces some enhancements under the
hood.&lt;/p&gt;
&lt;p&gt;The most visible is of course a &lt;a class="reference external" href="http://picasaweb.google.com/albrecht.andi/CrunchyFrogScreenshots?feat=embedwebsite#5296584789946073714"&gt;cleaner user interface&lt;/a&gt;. The
more or less experimental &lt;a class="reference external" href="http://python-pocdock.googlecode.com/"&gt;dock implementation&lt;/a&gt; was removed in favour of a -
at least in my opinion - cleaner interface heavily inspired by &lt;a class="reference external" href="http://projects.gnome.org/gedit/"&gt;GEdit&lt;/a&gt;. Along with this changes I've
cleaned up some requirements: Removed modules that where only used in
a few places and made others optional. The latter includes the various
GNOME bindings.&lt;/p&gt;
&lt;p&gt;This changes were made to provide initial support for running
CrunchyFrog with other desktop environments and on different
platforms. With this release a new &lt;a class="reference external" href="https://launchpad.net/~crunchyfrog/+archive/ppa"&gt;package archiv&lt;/a&gt; on launchpad
opened it's doors. If you're an Ubuntu (or Debian) user you can use
this PPA to update your CrunchyFrog installation from this repository.&lt;/p&gt;
&lt;p&gt;Read the full &lt;a class="reference external" href="http://groups.google.com/group/crunchyfrog/msg/a35f6aa2a608dac7"&gt;announcement&lt;/a&gt;
for more information about what has changed since the last
release. Any further enhancements like a terminal plugin to access
native database command lines within the application or a somewhat
improved schema browsing will go into the next major release.&lt;/p&gt;
&lt;p&gt;Thanks to everyone for giving feedback, reporting bugs and for the
good ideas during the last months! Have fun with it and - as always -
feel free to report any &lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/issues/entry?template=User%20defect%20report"&gt;bugs&lt;/a&gt;
or &lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/issues/entry?template=Feature%20Request"&gt;feature requests&lt;/a&gt;... ,-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/hknd9Og7yGE" height="1" width="1"/&gt;</summary><category term="crunchyfrog" /><category term="python" /><category term="gtk" /><category term="ui" /><feedburner:origLink>http://andialbrecht.de//blog/2009/01/29/a-cleaner-ui-for-crunchyfrog.html</feedburner:origLink></entry><entry><title>Experiences with Rietveld on Django</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/Iw0g9gL6Hx8/experiences-with-rietveld-on-django.html" rel="alternate" /><updated>2008-11-14T00:00:00+01:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-11-14:/blog/2008/11/14/experiences-with-rietveld-on-django.html</id><summary type="html">&lt;p&gt;It's been a while now that we're running a Rietveld instance on Django
at our company for internal reviews. Besides the basic setup as
described in my &lt;a class="reference external" href="http://code.google.com/appengine/articles/pure_django.html"&gt;article&lt;/a&gt; on
Google's App Engine site we've made some more modifications to
integrate Rietveld into our infrastructure. Here's a short description
of what we've done and what problems we've had (and still have):&lt;/p&gt;
&lt;p&gt;First off, we're using PostgreSQL as the database backend. Thanks to
Django the database setup was quite simple: We've just changed the
database settings in settings.py, ran &amp;quot;manage.py syncdb&amp;quot; and that's
it. The first time we've ran this setup a few minor bugs regarding
type conversion in &lt;a class="reference external" href="http://django-gae2django.googlecode.com/"&gt;gae2django&lt;/a&gt; came up, but they were
fixed soon. For authentication we've added a simple backend that talks
to our company's ActiveDirectory (using python-ldap). Implementing
that was again quite easy, just follow Django's documentation on how
to write a &lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/topics/auth/#writing-an-authentication-backend"&gt;custom authentication backend&lt;/a&gt;
and let Django do the rest. The most time consuming change was the
integration with an existing web server. We've already had a lighttpd
server running and Rietveld should live inside a subfolder on this
server. Because Rietveld assumes to sit in a top-level directory we
need to change all absolute URLs in Rietveld's sources, templates and
in the JavaScript&lt;/p&gt;
&lt;p&gt;We still have some broken links in our setup, e.g. using the keyboard
shortcuts to navigate between deltas still doesn't work and a few
weeks ago there was a change in Rietveld (collapsed patch sets on the
issue page are loaded with an AJAX call when expanded) that is still
not ported to our setup yet. It's just a matter of time to have this
fixed. Django runs in fcgi mode (threaded) and lighttpd acts as a
proxy as described in the &amp;quot;&lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/howto/deployment/fastcgi/#lighttpd-setup"&gt;Deployment&lt;/a&gt;&amp;quot;
section of Django's documentation. In the first few days we've had
some strange problems with sending emails. Some emails were lost. We
had some troubles to find the actual bug, but after I've done some
improvements to gae2django the problem was almost gone. Currently only
the sender of an email doesn't receive his own mail but this might be
an issue related to the setup of our mail servers. We're using
upload.py to create and update issues. To avoid confusions we've
renamed it and changed the default settings for the login URL and the
address of the review server. The master template has some slight
modifications too: We removed the link to the issue creation form and
to all repository related pages, added a little question mark pointing
to a help page and we've added a nice header image to match our CI.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="files/rv_screenshot.png"&gt;&lt;img alt="Screenshot of custumized Rietveld instance" src="files/rv_screenshot_thumb.png" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Rietveld running on local infrastructure&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Updating this customized installation of Rietveld is done using a
Makefile-driven patch system. First we fetch the latest Rietveld
sources from it's Subversion repository, then we update gae2django and
apply the patches from this project required to get Rietveld running
on Django. On top of that we apply the changes I've described
above. Of course, this procedure still needs some work, but at least
it works and keeping those patches in sync isn't that complicated as
you might think ,-)&lt;/p&gt;
&lt;p&gt;But besides the email issue and some broken links due to our
customized setup Rietveld runs pretty stable on Django. Currently the
Django server runs 3 weeks without any troubles even without a
traceback ;-)&lt;/p&gt;
&lt;p&gt;Finally a few notes on how code reviews were integrated in our
workflow. When we've decided to use a code review tool, there were
some concerns about the time needed to review other peoples code. But
after a short period of getting comfortable with this tool - and now
we feel very comfortable with it ;-) - it turned out that it's less
effort to do a review than expected. Mostly one or two iterations are
sufficient enough to eliminate the most obvious things and to give a
LGTM. Only changes that have to go immediately into the code base are
reviewed afterwards.&lt;/p&gt;
&lt;p&gt;Besides that, the knowledge about special parts of the code has spread
and as another side-effect there's now much more discussion about
best-practices for some application specific tasks and the process of
finding and defining them has accelerated noticeably.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit 2009-01-12:&lt;/strong&gt; The email issue seems to be solved. Our Postfix had
some troubles with multiple addresses given as in a single, comma
separated string. Splitting this string into a list of string
(addresses) solved the problem.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/Iw0g9gL6Hx8" height="1" width="1"/&gt;</summary><category term="Rietveld" /><category term="code reviews" /><category term="django" /><feedburner:origLink>http://andialbrecht.de//blog/2008/11/14/experiences-with-rietveld-on-django.html</feedburner:origLink></entry><entry><title>CrunchyFrog 0.3.0 has landed</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/z3c2o-YsvLE/crunchyfrog-030-has-landed.html" rel="alternate" /><updated>2008-10-24T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-10-24:/blog/2008/10/24/crunchyfrog-030-has-landed.html</id><summary type="html">&lt;p&gt;A new version of &lt;a class="reference external" href="http://cf.andialbrecht.de/"&gt;CrunchyFrog&lt;/a&gt; is
out. Besides a bunch of bug fixes and UI improvements this release
adds support for GNOME keyring and finally the SQL editor supports
multiple statements, the most visible change in this version.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="files/cf_sql_splitting.png"&gt;&lt;img alt="Screenshot of CrunchyFrog" src="files/cf_sql_splitting_thumb.png" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Option to split SQL statements before actually executing them.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Splitting of SQL statements for various dialects is very tricky and
may fail under certain circumstances. Fortunately the SQL editor is
clever enough to mark statements with a little arrow besides the line
numbers while you're hacking. So if you're not lucky with the
splitting of your queriesm you can always disable this feature either
by using the context menu (see highlighted entry in screenshot) to
temporarily disable it or open the preferences menu and disable it
permanently. (Keep in mind, it's still possible to execute only parts
by doing a text selection in the editor.) Getting CrunchyFrog up and
running is simple four step process (assuming &lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/wiki/Requirements"&gt;all required Python
modules&lt;/a&gt; are
already installed on your system):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://crunchyfrog.googlecode.com/files/crunchyfrog-0.3.0.tar.gz"&gt;Download&lt;/a&gt;
the source archive&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;tar xvfz &lt;span class="pre"&gt;crunchyfrog-0.3.0.tar.gz&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;cd &lt;span class="pre"&gt;crunchyfrog-0.3.0/&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;./crunchyfrog&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please file bug reports and feature requests on the &lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/issues/list"&gt;issue tracker&lt;/a&gt; and feel free to
&lt;a class="reference external" href="http://code.google.com/p/crunchyfrog/wiki/Development"&gt;contribute&lt;/a&gt;
;-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/z3c2o-YsvLE" height="1" width="1"/&gt;</summary><category term="CrunchyFrog" /><category term="SQL" /><feedburner:origLink>http://andialbrecht.de//blog/2008/10/24/crunchyfrog-030-has-landed.html</feedburner:origLink></entry><entry><title>A Google Code Sphinx Theme</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/KMafJUzPezQ/a-google-code-sphinx-theme.html" rel="alternate" /><updated>2008-10-21T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-10-21:/blog/2008/10/21/a-google-code-sphinx-theme.html</id><summary type="html">&lt;p&gt;The wiki feature on &lt;a class="reference external" href="http://code.google.com/"&gt;Google Code&lt;/a&gt; is a
great feature to provide documentation for a project. In my opinion it
has to big benefits: You can version your documentation and (thanks to
the wiki markup) it's easy to maintain. But on the other hand writing
your projects documentation using the wiki has some limitations.&lt;/p&gt;
&lt;p&gt;Especially when you know &lt;a class="reference external" href="http://sphinx.pocoo.org/"&gt;Sphinx&lt;/a&gt;, the
documentation generator used by Python, you will miss some features
like Python-specific markup, automagic index generation, sections and
chapters and last but ot least the power of restructured text ;-)&lt;/p&gt;
&lt;p&gt;Unfortunately Google Code Hosting offers no official way to serve
static files. So if you want to use Sphinx for your documentation, you
need to publish it somewhere else and put a link on your project
page. Wouldn't it be great to use Sphinx and integrate it with
everything else? So let's do it, but first off here's how it looks
like (visit this project page to see it in action):&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="files/gcsphinx1.png"&gt;&lt;img alt="files/gcsphinx1_thumb.png" src="files/gcsphinx1_thumb.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;And two other screenshots. The first one shows an index...&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="files/gcsphinx2.png"&gt;&lt;img alt="files/gcsphinx2_thumb.png" src="files/gcsphinx2_thumb.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;... and the second shows some search results using the Sphinx search engine:&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="files/gcsphinx3.png"&gt;&lt;img alt="files/gcsphinx3_thumb.png" src="files/gcsphinx3_thumb.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Here's how to use this theme for your project and how to &amp;quot;publish&amp;quot; it
on Google Code.&lt;/p&gt;
&lt;p&gt;Read the &lt;a class="reference external" href="http://sphinx.pocoo.org/contents.html"&gt;Sphinx documentation&lt;/a&gt; if you don't know anything
about Sphinx yet. To make this work, your documentation files and the
generated HTML output must live somewhere in your Subversion
repository. The theme consists of three files: Grab the CSS from &lt;a class="reference external" href="http://python-pocdock.googlecode.com/svn/trunk/docs/source/_static/custom.css"&gt;here&lt;/a&gt;
and place it in the directory for static files (usually
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;DOCROOT/source/.static&lt;/span&gt;&lt;/tt&gt; where DOCROOT is the root directory of your
Sphinx documentation), then grab the two templates (&lt;a class="reference external" href="http://python-pocdock.googlecode.com/svn/trunk/docs/source/_templates/layout.html"&gt;layout.html&lt;/a&gt;,
&lt;a class="reference external" href="http://python-pocdock.googlecode.com/svn/trunk/docs/source/_templates/search.html"&gt;search.html&lt;/a&gt;)
and place them in the templates folder
(&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;DOCROOT/source/.templates&lt;/span&gt;&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;Now open the configuration file of your documentation (&lt;tt class="docutils literal"&gt;conf.py&lt;/tt&gt;)
and make sure it has this line &lt;tt class="docutils literal"&gt;html_file_suffix = '.html'&lt;/tt&gt;. It's
required to build the correct URL for search results. The layout
template has some configuration options. Open layout.html in your
favorite editor and customize them for your needs: gcprj --&amp;gt; set this
to match your project name (the one in the URL...). gcusedownloads --&amp;gt;
1 = show &amp;quot;Downloads&amp;quot; tab, 0 = don't show it gcusewiki --&amp;gt; 1 = show
&amp;quot;Wiki&amp;quot; tab, 0 = don't show it gcuseissues --&amp;gt; 1 = show &amp;quot;Issues&amp;quot; tab, 0
= don't show it gcusespurce --&amp;gt; 1 = show &amp;quot;Source&amp;quot; tab, 0 = don't show
it Then build the HTML version of your documentation by running make
html. Make sure that all generated files are under version control
(e.g. by running svn status build/html) as we will deliver all content
straight out of the repository... ,-) Finally you need to set the
correct mime types for all generated files. Otherwise they're
delivered as plain text which is not very useful in our case. You can
easily set the correct SVN properties by running the following
commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;svn propset -R svn:mime-type text/css &lt;span class="sb"&gt;`&lt;/span&gt;find build/html/ -name .svn -type f -prune -o -name *.css&lt;span class="sb"&gt;`&lt;/span&gt;
svn propset -R svn:mime-type text/javascript &lt;span class="sb"&gt;`&lt;/span&gt;find build/html/ -name .svn -type f -prune -o -name *.js&lt;span class="sb"&gt;`&lt;/span&gt;
svn propset -R svn:mime-type image/x-png &lt;span class="sb"&gt;`&lt;/span&gt;find build/html/ -name .svn -type f -prune -o -name *.png&lt;span class="sb"&gt;`&lt;/span&gt;
svn propset -R svn:mime-type text/html &lt;span class="sb"&gt;`&lt;/span&gt;find build/html/ -name .svn -type f -prune -o -name *.html&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it. Now &amp;quot;publish&amp;quot; your documentation by running svn
commit... ,-) To view your documentation point your browser to
&lt;a class="reference external" href="http:/"&gt;http:/&lt;/a&gt;/&amp;lt;YOUR_PROJECT&amp;gt;.googlecode.com/svn/&amp;lt;DOCROOT&amp;gt;/build/html/index.html.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/KMafJUzPezQ" height="1" width="1"/&gt;</summary><category term="Google Code" /><category term="Sphinx" /><feedburner:origLink>http://andialbrecht.de//blog/2008/10/21/a-google-code-sphinx-theme.html</feedburner:origLink></entry><entry><title>Some Thoughts on CrunchyFrog 0.3</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/iyd9-g9KF3M/some-thoughts-on-crunchyfrog-03.html" rel="alternate" /><updated>2008-09-11T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-09-11:/blog/2008/09/11/some-thoughts-on-crunchyfrog-03.html</id><summary type="html">&lt;p&gt;In the last few months some people asked me how to execute single
statements in &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/"&gt;CrunchyFrog&lt;/a&gt;. It's possible, just select
the text you want to execute and hit &lt;tt class="docutils literal"&gt;F5&lt;/tt&gt;. If no text is selected
all text in the SQL editor is executed in whole. This is not really
satisfying so I played around with a SQL splitter. It's inspired by
Google's &lt;a class="reference external" href="http://googleappengine.googlecode.com/svn/trunk/google/appengine/ext/gql/__init__.py"&gt;GQL parser&lt;/a&gt;
found in their &lt;a class="reference external" href="http://code.google.com/appengine/"&gt;App Engine SDK&lt;/a&gt;
and currently it only tries to find statement boundaries in the SQL
editor. The statements are marked with a little arrow and that's all
for the moment. The backend that executes the statements still needs
some work. If everything works as expected some simple SQL parsing
will be part of the next major release. If anyone knows a good SQL
parser module for Python, leave a comment! Even a solid SQL splitter
would be enough in the first run. Another topic for the next major
release is the plugin system, especially the install and update
mechanism. I plan to use &lt;a class="reference external" href="http://capuchin.k-d-w.org/"&gt;Capucchin&lt;/a&gt;. It
should be easy to integrate in the current sources and it's definitely
better than another homegrown plugin updater and installer.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/iyd9-g9KF3M" height="1" width="1"/&gt;</summary><category term="CrunchyFrog" /><category term="SQL" /><feedburner:origLink>http://andialbrecht.de//blog/2008/09/11/some-thoughts-on-crunchyfrog-03.html</feedburner:origLink></entry><entry><title>Using ReStructuredText on App Engine</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/tTwCHwjZKyc/using-restructuredtext-on-app-engine.html" rel="alternate" /><updated>2008-08-14T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-08-14:/blog/2008/08/14/using-restructuredtext-on-app-engine.html</id><summary type="html">&lt;p&gt;To use docutils in your App Engine application to render
&lt;cite&gt;ReStructuredText &amp;lt;http://docutils.sourceforge.net/rst.html&amp;gt;&lt;/cite&gt; _first
download the &lt;cite&gt;module here &amp;lt;http://docutils.sourceforge.net/&amp;gt;&lt;/cite&gt; and
place the &lt;tt class="docutils literal"&gt;docutils&lt;/tt&gt; directory and &lt;tt class="docutils literal"&gt;roman.py&lt;/tt&gt; in the top-level
directory of your application:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;$ cd my_appengine_app/
$ wget http://prdownloads.sourceforge.net/docutils/docutils-0.5.tar.gz?download
$ tar xvfz docutils-0.5.tar.gz
$ mv docutils-0.5/docutils/ docutils-0.5/extras/roman.py .
$ rm -rf docutils-0.5*
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By default, docutils tries to read configuration files from various
locations. But this will ail in the App Engine environment. So you
have to disable it by overwriting the default settings:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;docutils.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;publish_parts&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;someview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;publish_parts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;some_rst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;writer_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;html4css1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;settings_overrides&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_disable_config&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fragment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are using the &lt;a class="reference external" href="http://code.google.com/p/google-app-engine-django/"&gt;Django helpers&lt;/a&gt;, add&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;RESTRUCTUREDTEXT_FILTER_SETTINGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_disable_config&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;in &lt;tt class="docutils literal"&gt;settings.py&lt;/tt&gt; to use the restructuredtext filter. Refer to the
&lt;a class="reference external" href="http://www.djangoproject.com/documentation/add_ons/#markup"&gt;Django documentation&lt;/a&gt; on how
to activate and use this filter.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/tTwCHwjZKyc" height="1" width="1"/&gt;</summary><category term="App Engine" /><category term="ReST" /><feedburner:origLink>http://andialbrecht.de//blog/2008/08/14/using-restructuredtext-on-app-engine.html</feedburner:origLink></entry><entry><title>CrunchyFrog 0.2.1 released</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/O0tMsRGNy40/crunchyfrog-021-released.html" rel="alternate" /><updated>2008-06-10T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-06-10:/blog/2008/06/10/crunchyfrog-021-released.html</id><summary type="html">&lt;p&gt;Just some minor bug fixes and improvements for this release:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Support for recent files.&lt;/li&gt;
&lt;li&gt;UI improvements and fixes.&lt;/li&gt;
&lt;li&gt;SQL server and httplog backend.&lt;/li&gt;
&lt;li&gt;Improved plugin handling.&lt;/li&gt;
&lt;li&gt;New and updated translations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two new backends (MSSQL server, HttpLog backend for combined log
files) are very basic for now. Database queries with these backends
are ok but overall they should be seen as experimental (forgot to
mention that in the release notes ;-).&lt;/p&gt;
&lt;p&gt;Visit the &lt;a class="reference external" href="http://crunchyfrog.googlecode.com/"&gt;GoogleCode pages&lt;/a&gt; for
additional information and downloads.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/O0tMsRGNy40" height="1" width="1"/&gt;</summary><category term="CrunchyFrog" /><feedburner:origLink>http://andialbrecht.de//blog/2008/06/10/crunchyfrog-021-released.html</feedburner:origLink></entry><entry><title>CrunchyFrog on Arch Linux</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/IJ0JulRxXSI/crunchyfrog-on-arch-linux.html" rel="alternate" /><updated>2008-05-17T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2008-05-17:/blog/2008/05/17/crunchyfrog-on-arch-linux.html</id><summary type="html">&lt;p&gt;To all &lt;a class="reference external" href="http://www.archlinux.org/"&gt;Arch Linux&lt;/a&gt; users out there,
&lt;a class="reference external" href="http://crunchyfrog.googlecode.com/"&gt;CrunchyFrog&lt;/a&gt; is now available
in the user-community repository. Thanks to angvp and anyone else
involved for packaging and debugging!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/IJ0JulRxXSI" height="1" width="1"/&gt;</summary><category term="CrunchyFrog" /><category term="ArchLinux" /><feedburner:origLink>http://andialbrecht.de//blog/2008/05/17/crunchyfrog-on-arch-linux.html</feedburner:origLink></entry><entry><title>Debugging gobject signals</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/EFWtC2Qt_ZY/debugging-gobject-signals.html" rel="alternate" /><updated>2007-09-05T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2007-09-05:/blog/2007/09/05/debugging-gobject-signals.html</id><summary type="html">&lt;p&gt;Here's a little snippet I oftenly use to debug GObject signals:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gobject&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Helper function to test signals&lt;/span&gt;

&lt;span class="sd"&gt;    It connects all `obj` signals to a dummy function&lt;/span&gt;
&lt;span class="sd"&gt;    which simply prints out all given arguments.&lt;/span&gt;

&lt;span class="sd"&gt;    Args:&lt;/span&gt;
&lt;span class="sd"&gt;      obj: A ``gobject.GObject``&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gobject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GObject&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;test_signals failed: obj must be a GObject&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Signal &lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s"&gt; emitted by &lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s"&gt;:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                            &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;signame&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;gobject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal_list_names&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In fact, it does nothing special, but I found it very useful during
development. Example usage:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;somewhere&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;test_signals&lt;/span&gt;
&lt;span class="n"&gt;test_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mywidget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/EFWtC2Qt_ZY" height="1" width="1"/&gt;</summary><category term="GTK" /><category term="GObject" /><category term="Python" /><feedburner:origLink>http://andialbrecht.de//blog/2007/09/05/debugging-gobject-signals.html</feedburner:origLink></entry><entry><title>Pulsing ProgressBar with text</title><link href="http://feedproxy.google.com/~r/andialbrecht/~3/pktqmICf-qw/pulsing-progressbar-with-text.html" rel="alternate" /><updated>2007-08-20T00:00:00+02:00</updated><author><name>Andi Albrecht</name></author><id>tag:andialbrecht.de,2007-08-20:/blog/2007/08/20/pulsing-progressbar-with-text.html</id><summary type="html">&lt;p&gt;Here's a little snippet for a pulsing ProgressBar with changing text.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gtk&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gobject&lt;/span&gt;

&lt;span class="n"&gt;win&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;delete_event&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main_quit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;pb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProgressBar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_pulse_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;win&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_pulse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pulse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Step: &lt;/span&gt;&lt;span class="si"&gt;%.2f&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;
    &lt;span class="n"&gt;gobject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_pulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;gobject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_pulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nothing special, anyway...&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/andialbrecht/~4/pktqmICf-qw" height="1" width="1"/&gt;</summary><category term="GTK" /><category term="Python" /><feedburner:origLink>http://andialbrecht.de//blog/2007/08/20/pulsing-progressbar-with-text.html</feedburner:origLink></entry></feed>
