<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="FeedCreator 1.7.6(BH)" -->
<rss version="2.0">
    <channel xmlns:g="http://base.google.com/ns/1.0">
        <title>Planet Maemo: category &quot;feed:b1704e4f997dfb2448842ae2dc57ed3f&quot;</title>
        <description>Blog entries from Maemo community</description>
        <link>http://maemo.org/news/planet-maemo/</link>
        <lastBuildDate>Tue, 09 Jun 2026 02:50:20 +0000</lastBuildDate>
        <generator>FeedCreator 1.7.6(BH)</generator>
        <language>en</language>
        <managingEditor>planet@maemo.org</managingEditor>
        <item>
            <title>Latin-1 or Windows-1252?</title>
            <link>http://mg.pov.lt/blog/latin1-or-cp1252</link>
            <description><![CDATA[

<p>Michael Foord wrote about <a
  href="http://www.voidspace.org.uk/python/weblog/arch_d7_2010_01_02.shtml#e1147">some
  Latin-1 control character fun</a> in a blog that's hard to read (the RSS feed
syndicated on Planet Python is truncated, grr!) and hard to reply (<del>no comments
  on the blog!</del> my Chromium's AdBlock+ hid the comment link so I couldn't
find it), but never mind that.</p>

<blockquote>
  <q>Unfortunately the data from the customers included some \x85 characters,
    which were breaking the CSV parsing.</q>
</blockquote>

<p>0x85 is a control character (NEXT LINE or NEL) in Latin-1, but it's a
printable character (HORIZONTAL ELLIPSIS) in Microsoft's code page 1252, which
is often mistaken for Latin-1.  I would venture a suggestion that the encoding
of the customer data was not latin-1 but rather cp1252.</p>

<blockquote><pre>
<span class="prompt">&gt;&gt;&gt;</span> <span class="string">'<span class="escape">\x85</span>'</span>.<span class="name">decode</span>(<span class="string">'cp1252'</span>)
<span class="string">u'<span class="escape">\u2026</span>'</span>
</pre></blockquote>

<span class="net_nemein_favourites">3 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=9cfb2090fbd511de8a966ff0d5d3b4c3b4c3&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/9cfb2090fbd511de8a966ff0d5d3b4c3b4c3/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>11 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=9cfb2090fbd511de8a966ff0d5d3b4c3b4c3&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/9cfb2090fbd511de8a966ff0d5d3b4c3b4c3/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Thu, 07 Jan 2010 21:42:52 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-9cfb2090fbd511de8a966ff0d5d3b4c3b4c3</guid>
        </item>
        <item>
            <title>GTimeLog: not dead yet!</title>
            <link>http://mg.pov.lt/blog/gtimelog-not-dead-yet</link>
            <description><![CDATA[

<p>Back in 2004 I wrote a small Gtk+ app to help me keep track of my time, and
called it <a href="http://mg.pov.lt/gtimelog/">GTimeLog</a>.  I shared it with
my coworkers, put it on the web (on the general "release early, release often"
principles), and it got sort-of popular before I found the time to polish it
into a state where I wouldn't be ashamed to show it to other people.</p>

<p>Fast-forward to 2008: there are actual users out there (much to my
surprise), I still haven't added the originally-envisioned spit and polish,
haven't done anything to foster a development community, am wracked by guilt of
not doing my maintainerly duties properly, which leads to depression and
burnout.  So I do the only thing I can think of: run away from the project and
basically ignore its existence for a year.  Unreviewed patches accumulate in my
inbox.</p>

<p>It seems that the sabbatical helped: yesterday, triggered by a <a
  href="http://bugs.debian.org/560981">new Debian bug report</a>, I sat down,
fixed the <a href="">bug</a>, implemented a <a
  href="https://bugs.launchpad.net/gtimelog/+bug/308750">feature</a>, applied a
<a href="https://bugs.launchpad.net/gtimelog/+bug/328118">couple</a> of <a
  href="https://bugs.launchpad.net/gtimelog/+bug/255618">patches</a>
languishing in the bug tracker, and <a
  href="http://pypi.python.org/pypi/gtimelog">released version 0.3</a> (which
was totally broken thanks to setuptools magic that suddenly stopped
working; so released 0.3.1 just now).  Then went through my old unread email,
created <a
  href="https://bugs.launchpad.net/gtimelog">bugs in Launchpad</a> and sent
replies to everyone.  Except <a href="http://blog.pierlux.com/en/">Pierre-Luc
  Beaudoin</a>, since his @collabora.co.uk email address bounced.  If anyone
knows how to contact him, I'd appreciate a note.</p>

<p><img src="http://mg.pov.lt/gtimelog-about-dialog.png"
        alt="version is now shown in the about dialog" /></p>

<p>There are also some older changes that I made before I emerged out of the
funk and so hadn't widely announced:</p>

<ul>
  <li>
    There's a <a href="http://groups.google.com/group/gtimelog">mailing
      list</a> for user and developer discussions (if there still are any ;).
  </li>
  <li>
    GTimeLog's <a href="https://code.launchpad.net/gtimelog">source code</a>
    now lives on Launchpad (actually, I <a
      href="http://mg.pov.lt/blog/happenings.html">mentioned</a> this on my
    blog once).
  </li>
</ul>
<span class="net_nemein_favourites">14 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=6a658410eb7011deb928a9020feae84de84d&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/6a658410eb7011deb928a9020feae84de84d/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>2 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=6a658410eb7011deb928a9020feae84de84d&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/6a658410eb7011deb928a9020feae84de84d/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Fri, 18 Dec 2009 00:58:00 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-6a658410eb7011deb928a9020feae84de84d</guid>
        </item>
        <item>
            <title>Unix is an IDE, or my Vim plugins</title>
            <link>http://mg.pov.lt/blog/unix-is-an-ide</link>
            <description><![CDATA[

<p><a href="http://c2.com/cgi/wiki?UnixIsAnIde">Unix is an IDE</a>.  I do my
development (Python web apps mostly) with <a href="http://www.vim.org/">Vim</a>
with a <a href="http://mg.pov.lt/vim/">bunch of custom plugins</a>, shell
(in GNOME Terminal: tabs rule!), GNU make, ctags, find + grep,
svn/bzr/hg/git.</p>

<p>The current working directory is my project configuration/state.  I run
tests here (bin/test), I search for code here (vim -t TagName, find + grep), I
run applications here (make run or bin/<em>appname</em>).  I can multitask
freely, for example, if I'm in the middle of typing an SVN commit message, I
can hit Ctrl+Shit+T, get a new terminal tab in the same working directory, and
look something up.  No aliases/environment variables/symlinks/<a
  href="http://blog.doughellmann.com/2009/12/switching-development-contexts-with.html">scripts
  making changes to config files</a>.  I can work on multiple projects at the
same time.  I can work remotely (over ssh).</p>

<p><a href="http://vimeo.com/user1043515">Gary Bernhardt's screencasts on
  Vimeo</a> show how productive you can get if you learn Vim and tailor it
to your needs.  I have Vim scripts that let me</p>

<ul>
  <li>
    See the name of the class and function that I'm editing in the statusbar,
    even if the class/function definition is offscreen:
    <a href="http://mg.pov.lt/vim/plugin/pythonhelper.vim">pythonhelper.vim</a>.
  </li>
  <li>
    See all pyflakes warnings and errors in a list as soon as I press F2 to
    save the file: <a
      href="http://mg.pov.lt/vim/plugin/python_check_syntax.vim">python_check_syntax.vim</a>.
  </li>
  <li>
    Add a "from foo.bar import Something" line at the top of the file if I
    press F5 when my cursor is on Something, looking up the package and module
    from ctags: <a
      href="http://mg.pov.lt/vim/plugin/python-imports.vim">python-imports.vim</a>.
  </li>
  <li>
    Switch between production code and unit tests with a single key if the
    project uses one of several conventions for tests (e.g. ./foo.py
    <tt>&lt;-&gt;</tt> ./tests/test_foo.py):
    <a href="http://mg.pov.lt/vim/plugin/py-test-switcher.vim">py-test-switcher.vim</a>.
  </li>
  <li>
    Generate a command line for running one particular unit test (the one
    my cursor is inside) and copy it into the system clipboard, so I can
    run that test by Alt-Tabbing into my terminal window and pasting.
    <a href="http://mg.pov.lt/vim/plugin/py-test-runner.vim">py-test-runner.vim</a>.
  </li>
  <li>
    Open the right file and move the cursor to the right line if I
    triple-click a line of traceback in a shell (or an email) then press F7 in
    my gvim window:
    <a href="http://mg.pov.lt/vim/plugin/py-test-locator.vim">py-test-locator.vim</a>.
  </li>
  <li>
    Compare my version of the code with the pristine version in source control
    in an interactive side-by-side diff that lets me revert bits I no longer
    want:
    <a href="http://mg.pov.lt/vim/plugin/vcscommand.vim">vcscommand.vim</a>.
  </li>
  <li>
    Highlight which lines of the source are covered by my tests, if I have
    coverage information in trace.py format:
    <a href="http://mg.pov.lt/vim/plugin/py-coverage-highlight.vim">py-coverage-highlight.vim</a>.
  </li>
  <li>
    Show the signature of a function/class's __init__ when I type the name
    of that class/function and an open parenthesis (looked up from tags):
    <a href="http://mg.pov.lt/vim/plugin/py-function-signature.vim">py-function-signature.vim</a>.
  </li>
  <li>
    Fold code into an outline so I only see names of methods or classes
    instead of their full bodies:
    <a href="http://mg.pov.lt/vim/vimrc">vimrc</a>, function PythonFoldLevel.
  </li>
  <li>
    Fold diff files so I can see whole hunks/files and can delete those with
    a single key (well, two keys -- dd).  Useful for reviewing <em>large</em>
    diffs (tens of thousands of lines):
    <a href="http://mg.pov.lt/vim/vimrc">vimrc</a>, function DiffFoldLevel.
  </li>
</ul>

<p>Some of these come from <a href="http://www.vim.org">www.vim.org</a>, some
I've written myself, some I've taken and modified a little bit to avoid an
irritating quirk or add a missing feature.  Some things I don't have (and envy
Emacs or IDE users for having -- like an integrated debugger for Python apps,
and, generally, integration with other tools, running in the background).</p>

<p>It's been my plan for a long time to polish my plugins, release them
somewhere (github?  bitbucket? launchpad?) and upload to vim.org, but as it
doesn't seem to be happening, I thought I'd at least put an <a
  href="http://mg.pov.lt/vim">svn
  export of my ~/.vim</a> on the web.</p>
<span class="net_nemein_favourites">13 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=b42806c4e45211dea3fb91165ce15eaa5eaa&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/b42806c4e45211dea3fb91165ce15eaa5eaa/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>3 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=b42806c4e45211dea3fb91165ce15eaa5eaa&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/b42806c4e45211dea3fb91165ce15eaa5eaa/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Tue, 08 Dec 2009 23:37:52 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-b42806c4e45211dea3fb91165ce15eaa5eaa</guid>
        </item>
        <item>
            <title>Displaying multiline text in Zope 3</title>
            <link>http://mg.pov.lt/blog/zope3-displaying-multiline-text</link>
            <description><![CDATA[

<p>zope.schema has Text and TextLine.  The former is for multiline text, the
latter is for a single line, as the name suggests.  Zope 3 forms will use a
text area for Text fields and an input box for TextLine fields.  Display
widgets, however, apply no special formatting (other than HTML-quoting of
characters like &lt;, &gt; and &amp;), and since newlines are treated the same
way as spaces in HTML, your multiline text gets collapsed into a single
paragraph.</p>

<p>Here's a pattern I've been using in Zope 3 to display multiline user-entered
text as several paragraphs:</p>

<blockquote>
<pre>
<span class="keyword">import</span> <span class="name">cgi</span>

<span class="keyword">from</span> <span class="name">zope</span>.<span class="name">component</span> <span class="keyword">import</span> <span class="name">adapts</span>
<span class="keyword">from</span> <span class="name">zope</span>.<span class="name">publisher</span>.<span class="name">browser</span> <span class="keyword">import</span> <span class="name">BrowserView</span>
<span class="keyword">from</span> <span class="name">zope</span>.<span class="name">publisher</span>.<span class="name">interfaces</span> <span class="keyword">import</span> <span class="name">IRequest</span>


<span class="def">class</span> <span class="name">SplitToParagraphsView</span>(<span class="name">BrowserView</span>):
    <span class="string">"""Splits a string into paragraphs via newlines."""</span>

    <span class="name">adapts</span>(<span class="name">None</span>, <span class="name">IRequest</span>)

    <span class="def">def</span> <span class="name">paragraphs</span>(<span class="name">self</span>):
        <span class="keyword">if</span> <span class="name">self</span>.<span class="name">context</span> <span class="keyword">is</span> <span class="name">None</span>:
            <span class="keyword">return</span> []
        <span class="keyword">return</span> <span class="name">filter</span>(<span class="name">None</span>, [<span class="name">s</span>.<span class="name">strip</span>() <span class="keyword">for</span> <span class="name">s</span> <span class="keyword">in</span> <span class="name">self</span>.<span class="name">context</span>.<span class="name">splitlines</span>()])

    <span class="def">def</span> <span class="name">__call__</span>(<span class="name">self</span>):
        <span class="keyword">return</span> <span class="string">""</span>.<span class="name">join</span>(<span class="string">'&lt;p&gt;%s&lt;/p&gt;<span class="escape">\n</span>'</span> % <span class="name">cgi</span>.<span class="name">escape</span>(<span class="name">p</span>)
                        <span class="keyword">for</span> <span class="name">p</span> <span class="keyword">in</span> <span class="name">self</span>.<span class="name">paragraphs</span>())
</pre>
</blockquote>

<p>View registration</p>

<blockquote>
<pre>
&lt;<span class="def">configure</span>
    xmlns="http://namespaces.zope.org/zope"&gt;

  &lt;<span class="def">view</span>
      <span class="name">for</span>=<span class="string">"*"</span>
      <span class="name">name</span>=<span class="string">"paragraphs"</span>
      <span class="name">type</span>=<span class="string">"zope.publisher.interfaces.browser.IBrowserRequest"</span>
      <span class="name">factory</span>=<span class="string">".views.SplitToParagraphsView"</span>
      <span class="name">permission</span>=<span class="string">"zope.Public"</span>
      /&gt;

&lt;/<span class="def">configure</span>&gt;
</pre>
</blockquote>

<p>and usage</p>

<blockquote>
<pre>
&lt;<span class="def">p</span> <span class="name">tal</span>:<span class="name">replace</span>=<span class="string">"structure object/attribute/@@paragraphs"</span> /&gt;
</pre>
</blockquote>

<p><strong>Update:</strong>  The view really ought to be registered twice: once
for basestring and once for NoneType.  I was too lazy to figure out the dotted
names for those (or check if zope.interface has external interface declarations
for them), so I registered it for "*".  You should know that this makes the
view available for arbitrary objects (but won't work for most of them, since
they don't have a splitlines method), and that it is, sadly, accessible to
users who may try to hack your system by typing things like @@paragraphs in the
browser's address bar.  Ignas Mikalajūnas offers an <a
  href="http://blog.pow.lt/2009/12/02/formatting-and-processing-text-in-tal-templates/">alternative
  solution using TALES path adapters</a>.</p>
<span class="net_nemein_favourites">8 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=995d9b30deab11de89f2c9e4914c19751975&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/995d9b30deab11de89f2c9e4914c19751975/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>4 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=995d9b30deab11de89f2c9e4914c19751975&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/995d9b30deab11de89f2c9e4914c19751975/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Tue, 01 Dec 2009 18:57:56 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-995d9b30deab11de89f2c9e4914c19751975</guid>
        </item>
        <item>
            <title>Escaping hotel firewalls with ssh over port 80</title>
            <link>http://mg.pov.lt/blog/escaping-hotel-firewall</link>
            <description><![CDATA[

<p>I booked a stay at a particular hotel because the web page said "Free WiFi".
It didn't say "all outgoing ports firewalled except for port 80 and a few
other (useless) ones".  Not having SSH access is most painful.  Luckily,
there's a solution.</p>

<p>You need a web server running Apache and SSH.  Enable mod_proxy and
mod_proxy_connect and add this to the <em>first</em> (i.e. default) virtual
host configuration:</p>
<blockquote><pre>
&lt;VirtualHost <em>whatever</em>:80&gt;
...

  # allow ssh to localhost over http proxy
  ProxyRequests on
  AllowCONNECT 22
  &lt;Proxy localhost&gt;
    Order allow,deny
    Allow from all
  &lt;/Proxy&gt;

&lt;/VirtualHost&gt;
</pre></blockquote>
Reload Apache configuration.  The setup is done.  (Instructions based on <a
  href="http://dag.wieers.com/howto/ssh-http-tunneling/">Tunneling SSH over
  HTTP(S)</a> by Dag Wieers.)</p>

<p>On the client side you need <a
  href="http://proxytunnel.sourceforge.net/">proxytunnel</a>.  Sadly, it's not
packaged for Ubuntu yet, but compiling from sources is trivial.  Edit ~/.ssh/config
and add an entry for your proxied ssh connection:</p>

<blockquote><pre>
Host p<em>myservername</em>
ProxyCommand proxytunnel -q -p <em>myserver.mydomain.com</em>:80 -d localhost:22
</pre></blockquote>

<p>That's it.  Now you can <tt>ssh p<em>myservername</em></tt>.  (The p prefix
is a reminder that I'm using a proxied connection: ssh fridge versus ssh
pfridge.  Also it reminds me of Terry Pratchett's <a
  href="http://www.amazon.com/Pyramids-Terry-Pratchett/dp/0061020656">Pyramids</a>.).

<p>For extra fun (e.g. IRC) use ssh's built-in SOCKS5 proxy: <tt>ssh -D 1080
  p<em>myservername</em></tt>.  Then tell the apps to use a SOCKS5 proxy on
localhost.  Since telling each app to use a proxy (and then, later, telling it
to stop using it) is a big *pain*, and some apps (e.g. ssh) don't support
proxies directly, a wrapper like <a
  href="http://tsocks.sourceforge.net/">tsocks</a> is handy.  Edit
/etc/tsocks.conf and set the default socks server to 127.0.0.1, then use
it to run apps:</p>

<blockquote><pre>
<span class="prompt">$</span> <span class="typing">tsocks xchat-gnome</span>
<span class="prompt">$</span> <span class="typing">tsocks bzr push lp:<em>myprojectname</em></span>
</pre></blockquote>

<p>tsocks is packaged for Ubuntu.</p>

<p>If your hotel doesn't have free WiFi, a prepaid SIM card with 3G access
could be cheaper than roaming charges.  Apparently you can get one with a
virtually unlimited (for a short stay, anyway) data plan for 27 EUR in
Amsterdam.</p>
<span class="net_nemein_favourites">19 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=e951e854b6a511dea0e37d075c7120882088&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/e951e854b6a511dea0e37d075c7120882088/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=e951e854b6a511dea0e37d075c7120882088&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/e951e854b6a511dea0e37d075c7120882088/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Sun, 11 Oct 2009 20:37:35 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-e951e854b6a511dea0e37d075c7120882088</guid>
        </item>
        <item>
            <title>Pylons and SQL schema migration</title>
            <link>http://mg.pov.lt/blog/pylons-and-sql-migration</link>
            <description><![CDATA[

<p>I'm at the point in my hobby project where I'd like to be able to change my models
without losing all my test data.  And I'm too lazy to do manual dumps and edit
the SQL in place before reimporting it.</p>

<p>I want a system</p>

<ul>
  <li>that is <em>transparent</em> to the user: if my database is at schema
      version 1, and my code is at version 3, I want it to be automatically
      upgraded to version 3 on server startup.</li>
  <li>that is <em>not too hard</em> on the programmer: dropping a numbered Python or SQL
      script in a directory ought to be sufficient to define a transition from
      schema version X to schema version X+1.</li>
  <li>that <em>handles errors gracefully</em>: makes a backup of the database
      with the old schema version; runs my script in a transaction and aborts
      that transaction if the conversion fails (while showing me enough
      information to debug the problem).</li>
  <li><em>allows prototyping</em> without having to increment the schema number for every
      little change I make to the models; I should be the one who decides that a new
      schema is ready to go out to the world.</li>
</ul>

<p>I've been glancing at <a
  href="http://code.google.com/p/sqlalchemy-migrate/">SQLAlchemy-Migrate</a>, since I've
been brought up to believe <abbr title="Not Invented Here">NIH</abbr>ing is
Bad.  But Migrate is <em>scary</em>.  I have to admit that the longer I stare
at its documentation, the less I can describe <em>why</em> I think so.  All
those shell commands&mdash;but there's an API for invoking them from Python, so maybe I can
achieve my goals.  I'll have to try and see.
</p>

<span class="net_nemein_favourites">3 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=93bb7a70a6d511debb430d74bf89d997d997&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/93bb7a70a6d511debb430d74bf89d997d997/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>6 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=93bb7a70a6d511debb430d74bf89d997d997&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/93bb7a70a6d511debb430d74bf89d997d997/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Mon, 21 Sep 2009 17:38:28 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-93bb7a70a6d511debb430d74bf89d997d997</guid>
        </item>
        <item>
            <title>Pylons with zc.buildout, continued</title>
            <link>http://mg.pov.lt/blog/pylons-with-buildout-2</link>
            <description><![CDATA[

<p><a href="http://mg.pov.lt/blog/pylons-with-buildout.html">Last time</a> I
mentioned that running bin/buildout with the -N flag makes it run faster
(since it skips looking for newer versions to upgrade).  You can tell
buildout to do this by default by putting 'newest = false' into the [buildout]
section of buildout.cfg. We'll be running bin/buildout a lot now, since we'll
be making changes to the project environment, so this will save wear and tear
on the '-', 'N' and Shift keys.  (And, by the way, I'm not trying to soak up
Google juice by repeating the word 'buildout' a lot, honest!)</p>

<p>I will omit bzr commits from this narrative as it's getting long; you can
assume that every self-contained change was committed separately.</p>

<h4>tests</h4>

<p>  First, I want a bin/test script to run the test
suite.  Pylons uses nose, so we need to tell buildout to install the nosetests
script (under a different name, since I'm used to typing bin/test no matter
what test runner a project happens to use):</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr diff</span>
=== modified file 'buildout.cfg'
--- buildout.cfg	2009-09-15 19:49:11 +0000
+++ buildout.cfg	2009-09-15 19:49:18 +0000
@@ -8,5 +8,8 @@
 recipe = zc.recipe.egg
 eggs = Pylons
        PasteScript
+       nose
        asharing
 interpreter = python
+scripts = paster
+          nosetests=test

<span class="prompt">$</span> <span class="typing">bin/buildout</span>
...
Generated script '/tmp/AlliterationSharing/bin/paster'.
Generated script '/tmp/AlliterationSharing/bin/test'.
...
<span class="prompt">$</span> <span class="typing">bin/test</span>

----------------------------------------------------------------------
Ran 0 tests in 0.276s

OK
</pre>

<h4>ctags</h4>

<p>Documentation is good, but sometimes you want to look at the source code of
the framework.  There's a tool called <a
  href="http://ctags.sourceforge.net/">ctags</a> that builds a database of
identifiers.  The popular text editors <a href="http://www.vim.org/">Vim</a>
and <a href="http://www.gnu.org/software/emacs/">Emacs</a> can then use the
tags database to jump to a definition of any name with a single keystroke
(Ctrl-] in vim, M-. in emacs).</p>

<p>Building the tags database is complicated by each Python package being
installed into a separate directory.  There's a buildout recipe called
z3c.recipe.tag that finds those directories and lets you build a unified tags
file.  We'll also ask buildout to make sure it <em>unzips</em> any packages
distributed as .egg files, since ctags doesn't process those:</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr diff</span>
@@ -1,8 +1,9 @@
 [buildout]
 develop = .
-parts = pylons
+parts = pylons ctags
 
 newest = false
+unzip = true
 
 [pylons]
 recipe = zc.recipe.egg
@@ -13,3 +14,7 @@
 interpreter = python
 scripts = paster
           nosetests=test
+
+[ctags]
+recipe = z3c.recipe.tag:tags
+eggs = ${pylons:eggs}

<span class="prompt">$</span> <span class="typing">bin/buildout</span>
...
Generated script '/tmp/AlliterationSharing/bin/ctags'.
...
<span class="prompt">$</span> <span class="typing">bin/ctags</span>
</pre>

<h4>omelette</h4>

<p>ctags lets you find classes and functions by name; it doesn't let you find
packages or modules.  There's another recipe, collective.recipe.omelette that
creates a tree of symlinks mirroring the Python package structure (here
'unzip = true' also comes in handy):</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr diff</span>
=== modified file 'buildout.cfg'
--- buildout.cfg	2009-09-15 20:04:42 +0000
+++ buildout.cfg	2009-09-15 20:05:30 +0000
@@ -1,6 +1,6 @@
 [buildout]
 develop = .
-parts = pylons ctags
+parts = pylons ctags omelette
 
 newest = false
 unzip = true
@@ -18,3 +18,7 @@
 [ctags]
 recipe = z3c.recipe.tag:tags
 eggs = ${pylons:eggs}
+
+[omelette]
+recipe = collective.recipe.omelette
+eggs = ${pylons:eggs}

<span class="prompt">$</span> <span class="typing">bin/buildout </span>
...
<span class="prompt">$</span> <span class="typing">ls -l parts/omelette</span>
...
</pre>

<p>The symlink tree is created under parts/omelette/.  For example, if you want
to see what webhelper tags were available, you can open
parts/omelette/webhelper/html/builder.py in your editor and see.

<h4>Makefile</h4>

<p>This is getting long (and not everyone may be interested<sup
  class="footnote">1</sup>), but one long post is easier to skip than five
medium ones in a row, so I'll continue.</p>

<blockquote class="footnotes">
  <p><sup>1</sup> Sorry, <a href="http://maemo.org/news/planet-maemo/">Planet
    Maemo</a>!  There's an <a
    href="http://mg.pov.lt/blog/tag/maemo/index.rss">RSS feed of posts tagged
    'maemo'</a>, if you can figure out the URL, which is very well hidden by
    PyBlosxom, *sigh*.
  </p>
</blockquote>

<p>Wouldn't it be nice if new developers could check out your project and start
it up with just a couple of commands?  Make is a time-tested tool that works
well for this:</p>

<pre>
<span class="prompt">$</span> <span class="typing">cat Makefile</span>
# Just remember that you need to use real tabs, not spaces, in a Makefile

PYTHON = python

.PHONY: all
all: bin/paster

.PHONY: run
run: bin/paster
        bin/paster serve development.ini --reload

.PHONY: test check
test check: bin/test
        bin/test

.PHONY: tags
tags: bin/ctags
        bin/ctags

bin/paster bin/test bin/python bin/ctags: bin/buildout
        bin/buildout

bin/buildout: bootstrap.py
        $(PYTHON) bootstrap.py
</pre>

<p>Now all you need to do after checking out is run 'make' to set up a working
development environment.  'make run' or 'make test' will also do that, if
necessary, so this one-liner is sufficient to get a working Hello World
application on port 5000:</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr branch lp:~mgedmin/+junk/AlliterationSharing &amp;&amp; cd AlliterationSharing &amp;&amp; make run</span>
</pre>

<p>Try it!  You'll get a Bazaar branch with all the history of this little
blog project.</p>
<span class="net_nemein_favourites">1 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=1acca3cea23811de9f53e17b362a90929092&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/1acca3cea23811de9f53e17b362a90929092/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>6 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=1acca3cea23811de9f53e17b362a90929092&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/1acca3cea23811de9f53e17b362a90929092/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Tue, 15 Sep 2009 20:40:56 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-1acca3cea23811de9f53e17b362a90929092</guid>
        </item>
        <item>
            <title>Starting a Pylons project with zc.buildout</title>
            <link>http://mg.pov.lt/blog/pylons-with-buildout</link>
            <description><![CDATA[

<p>For software development I prefer <a
  href="http://www.buildout.org">buildout</a> to <a
  href="http://pypi.python.org/pypi/virtualenv">virtualenv</a>.  This is
because buildout has a text file describing the state of your working
environent, which can be versioned and used later to recreate it, as well
as during development to modify the environment slightly.</p>

<p>To start a new Pylons project, first create an empty directory.  Let's
call our new project AlliterationSharing<sup
class="footnote">1</sup>, because everybody is sick of 'foo'
and 'bar'.</p>

<blockquote class="footnotes">
<p><sup>1</sup> Generated by randomly picking two words from
/usr/share/dict/words, then chosen over among 120 other variants that weren't
as good.</p>
</blockquote>

<pre>
<span class="prompt">$</span> <span class="typing">mkdir -p ~/src/AlliterationSharing</span>
<span class="prompt">$</span> <span class="typing">cd ~/src/AlliterationSharing</span>
</pre>

<p>Now create a file called buildout.cfg with the following content:</p>

<pre>
<span class="prompt">$</span> <span class="typing">cat buildout.cfg</span>
[buildout]
parts = pylons

[pylons]
recipe = zc.recipe.egg
eggs = Pylons
       PasteScript
interpreter = python
</pre>

<p>Download <a
  href="http://svn.zope.org/zc.buildout/trunk/bootstrap/">bootstrap.py</a> to
it and run it to get bin/buildout.  Note: you can chose which Python version you
want to use by running bootstrap.py with it.  All other scripts under bin/
will be generated by buildout and will use the same Python interpreter.</p>

<pre>
<span class="prompt">$</span> <span class="typing">wget http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py</span>
<span class="prompt">$</span> <span class="typing">python bootstrap.py</span>
Creating directory '.../AlliterationSharing/bin'.
Creating directory '.../AlliterationSharing/parts'.
Creating directory '.../AlliterationSharing/eggs'.
Creating directory '.../AlliterationSharing/develop-eggs'.
Generated script '.../AlliterationSharing/bin/buildout'.
</pre>

<p>Run bin/buildout to install Pylons into your sandbox.</p>

<pre>
<span class="prompt">$</span> <span class="typing">bin/buildout</span>
Installing pylons.
Generated script '.../AlliterationSharing/bin/paster'.
Generated interpreter '.../AlliterationSharing/bin/python'.
</pre>

<p>Aside: buildout has this very nice feature where it can share Python
packages between projects.  This will save you enormous amounts of time that
would otherwise be spent downloading and unpacking eggs.  To make use of this
facility, create a file ~/.buildout/default.cfg with</p>
<pre>
<span class="prompt">$</span> <span class="typing">cat ~/.buildout/default.cfg </span>
[buildout]
eggs-directory = /home/mg/tmp/buildout-eggs
# XXX replace /home/mg with the full path of *your* home directory
# it would be much nicer if buildout let me use ~ or $HOME
# see <a href="https://bugs.launchpad.net/zc.buildout/+bug/190260">https://bugs.launchpad.net/zc.buildout/+bug/190260</a>
</pre>

<p>Another useful trick is to pass the -N flag to bin/buildout, which will tell
it not to bother looking for newer versions of packages on the Internet when
there's already an existing version installed in your eggs directory.</p>

<p>Back to business: now you've got two new scripts: bin/python and bin/paster.
You can use the first one to play with the interactive Python console where you
can now import pylons and all the dependencies; it has no other value.</p>

<p>Now is a good point to add the files you've created into a version control
system.  I'll arbitrarily use Bazaar.</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr init .</span>
<span class="prompt">$</span> <span class="typing">bzr add bootstrap.py buildout.cfg</span>
<span class="prompt">$</span> <span class="typing">bzr ignore bin parts eggs develop-eggs .installed.cfg</span>
<span class="prompt">$</span> <span class="typing">bzr commit -m "Create AlliterationSharing project"</span>
</pre>

<p>Run bin/paster create -t pylons to create a skeleton project.</p>

<pre>
<span class="prompt">$</span> <span class="typing">bin/paster create -t pylons asharing</span>
<span class="prompt">$</span> <span class="typing">bzr ignore *.egg-info</span>
<span class="prompt">$</span> <span class="typing">bzr add asharing</span>
<span class="prompt">$</span> <span class="typing">bzr commit -m "Generated project files with paster create"</span>
</pre>

<p>Now paster creates a directory structure that I don't like:</p>

<pre>
AlliterationSharing/
  buildout.cfg
  bin/
  asharing/
    setup.py
    README.txt
    MANIFEST.in
    asharing/
      __init__.py
      config/
      controllers/
      templates/
      public/
</pre>

<p>I'd like the README and setup.py to be in the top level, and I dislike
repeating 'asharing' twice in directory names.  I'll move some files around</p>

<pre>
<span class="prompt">$</span> <span class="typing">cd asharing/</span>
<span class="prompt">$</span> <span class="typing">bzr mv development.ini docs MANIFEST.in README.txt setup.* test.ini ../</span>
<span class="prompt">$</span> <span class="typing">bzr rm ez_setup.*</span>
<span class="prompt">$</span> <span class="typing">cd ..</span>
<span class="prompt">$</span> <span class="typing">bzr mv asharing src</span>
<span class="prompt">$</span> <span class="typing">bzr ci -m "Moved some files around"</span>
</pre>

<p>Now the tree looks like this:</p>

<pre>
AlliterationSharing/
  buildout.cfg
  setup.py
  README.txt
  MANIFEST.in
  bin/
  src/
    asharing/
      __init__.py
      config/
      controllers/
      templates/
      public/
</pre>

<p>We have to tell setup.py where to find the source tree</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr diff</span>
=== modified file 'MANIFEST.in'
--- MANIFEST.in	2009-09-13 13:04:00 +0000
+++ MANIFEST.in	2009-09-13 13:05:59 +0000
@@ -1,3 +1,3 @@
-include asharing/config/deployment.ini_tmpl
-recursive-include asharing/public *
-recursive-include asharing/templates *
+include src/asharing/config/deployment.ini_tmpl
+recursive-include src/asharing/public *
+recursive-include src/asharing/templates *

=== modified file 'setup.py'
--- setup.py	2009-09-13 13:04:00 +0000
+++ setup.py	2009-09-13 13:04:40 +0000
@@ -17,7 +17,8 @@
         "SQLAlchemy&gt;=0.5",
     ],
     setup_requires=["PasteScript&gt;=1.6.3"],
-    packages=find_packages(exclude=['ez_setup']),
+    packages=find_packages('src', exclude=['ez_setup']),
+    package_dir={'': 'src'},
     include_package_data=True,
     test_suite='nose.collector',
     package_data={'asharing': ['i18n/*/LC_MESSAGES/*.mo']},
</pre>

<p>(I'm not sure if you also need to change package_data and/or setup.cfg; it's
possible that I left i18n in a broken state.  Can somebody comment on
this?)</p>

<p>And we have to tell buildout that we've got a new Python package to enable
in the project environment</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr diff buildout.cfg </span>
=== modified file 'buildout.cfg'
--- buildout.cfg	2009-09-13 12:57:21 +0000
+++ buildout.cfg	2009-09-13 13:08:05 +0000
@@ -1,8 +1,10 @@
 [buildout]
+develop = .
 parts = pylons
 
 [pylons]
 recipe = zc.recipe.egg
 eggs = Pylons
        PasteScript
+       asharing
 interpreter = python
</pre>

<p>Now you can re-run bin/buildout and start your hello-world project</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr commit -m "Include the new package in the build"</span>
<span class="prompt">$</span> <span class="typing">bin/buildout -N</span>
<span class="prompt">$</span> <span class="typing">bin/paster serve --reload development.ini</span>
</pre>

<p>Happy hacking!</p>

<p>To be continued: <a href="http://mg.pov.lt/blog/pylons-with-buildout-2.html">telling buildbot to create bin/test; using ctags and omelette</a>.</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=8bfb699ca06a11de9a77a57757fc87668766&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/8bfb699ca06a11de9a77a57757fc87668766/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>7 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=8bfb699ca06a11de9a77a57757fc87668766&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/8bfb699ca06a11de9a77a57757fc87668766/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Sun, 13 Sep 2009 13:37:08 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-8bfb699ca06a11de9a77a57757fc87668766</guid>
        </item>
        <item>
            <title>Footnotes done well</title>
            <link>http://mg.pov.lt/blog/footnotes-done-well</link>
            <description><![CDATA[

<p>I like the way footnotes are implemented here: <a
  href="http://jacobian.org/writing/snakes-on-the-web/"> Snakes on the Web</a>
by Jacob Kaplan-Moss.</p>

<center>
  <img src="http://mg.pov.lt/jacobian-footnote.gif" width="400" height="80"
       alt="mini-screencast of animated footnote" />
</center>

<p><small>(Recorded with <a
    href="http://people.freedesktop.org/~company/byzanz/">byzanz</a>.  My gif-fu
  is nonexistent or I would make it loop, but with a sufficiently long delay
  at the end to avoid irritation.  Now you have to reload the whole page if
  you missed the animation.)</small></p>

<p>I'm somewhat ambivalent about the animation effect.  On one hand, shiny!  On
the other hand, hitting tiny clickable areas is not good usability.  Still,
<em>shiny!</em></p>

<p>Footnotes are kind of a <a
  href="http://mg.pov.lt/blog/footnotes-on-the-web.html">personal pet-peeve of
  mine</a>.</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=d4e25dd09f2c11de81225df885a388a888a8&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/d4e25dd09f2c11de81225df885a388a888a8/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>9 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=d4e25dd09f2c11de81225df885a388a888a8&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/d4e25dd09f2c11de81225df885a388a888a8/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Fri, 11 Sep 2009 23:42:32 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-d4e25dd09f2c11de81225df885a388a888a8</guid>
        </item>
        <item>
            <title>Local changes to buildout.cfg</title>
            <link>http://mg.pov.lt/blog/buildout-local-config</link>
            <description><![CDATA[

<p>Most of Python packages in the Zope world use <a
  href="http://www.buildout.org">Buildout</a>:</p>

<pre>
svn co svn+ssh://svn.zope.org/repos/main/plone.z3cform/trunk plone.z3cform
cd plone.z3cform
python2.4 bootstrap.py
bin/buildout
bin/test -pvc
</pre>

<p>Now suppose you want to change the buildout environment somehow, e.g.
use the current development version of zope.testing instead of whatever is
specified in buildout.cfg.  Don't edit the existing buildout.cfg (you might
accidentally commit your local debug changes), instead create a new cfg file,
e.g. test.cfg:
</p>

<pre>
[buildout]
extends = buildout.cfg
develop += ../zope.testing

[versions]
# override any existing version pins
zope.testing =
</pre>

<p>Now re-run buildout</p>

<pre>
bin/buildout -c test.cfg
bin/test -pvc
</pre>

<p>And the tests should be run with the newest zope.testing.code.</p>

<p>Only this does not work with plone.z3cform, and I have no clue why.
It generally works with other packages (at least those that use the
zc.recipe.testrunner rather than collective.recipe.z2testrunner).
Buildout is like that sometimes :(</p>
<span class="net_nemein_favourites">1 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=7fa32e0e805411de8cadfdd71097b935b935&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/7fa32e0e805411de8cadfdd71097b935b935/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>4 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=7fa32e0e805411de8cadfdd71097b935b935&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/7fa32e0e805411de8cadfdd71097b935b935/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Mon, 03 Aug 2009 17:38:41 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-7fa32e0e805411de8cadfdd71097b935b935</guid>
        </item>
        <item>
            <title>Python-related updates for the last couple of months</title>
            <link>http://mg.pov.lt/blog/happenings</link>
            <description><![CDATA[

<p>Went to <a href="http://www.europython.eu/">EuroPython</a>, met new people,
had a great time.</p>

<p>Updated <a href="https://launchpad.net/gtkeggdeps">gtkeggdeps</a>, the
interactive Python package dependency browser.  Collaborated with <a
  href="http://thomas-lotze.de/en/">Thomas Lotze</a>, who maintains the engine
(<a href="http://pypi.python.org/pypi/tl.eggdeps">tl.eggdeps</a>) that
gtkeggdeps wraps, to resolve API mismatches.  Moved the sources to <a
  href="https://code.launchpad.net/gtkeggdeps">launchpad.net</a>, added a test
suite, made it use <a href="http://buildout.org">zc.buildout</a> for convenient
development.</p>

<p>Moved the source repository of <a
  href="https://launchpad.net/gtimelog">gtimelog</a>, the simple desktop time
tracker, to <a href="https://code.launchpad.net/gtimelog">launchpad.net</a>.
Failed to do anything else with it.  <tt>:-(</tt></p>

<p>Tried to work on <a
  href="http://code.google.com/p/jrfonseca/wiki/XDot">xdot</a>, wrestled with
git-svn merges, failed abysmally.  <a
  href="http://code.google.com/p/jrfonseca/issues/detail?id=19">Asked
  upstream</a> to upload xdot to <a
  href="http://pypi.python.org/pypi">PyPI</a>.</a>

<p>Released <a href="https://launchpad.net/zodbbrowser">ZODB Browser</a>, but
this deserves a separate post.</p>

<p>Sent a bunch of <a
  href="http://www.divmod.org/trac/wiki/DivmodPyflakes">pyflakes</a> patches from
<a href="https://code.launchpad.net/~mgedmin/pyflakes/pyflakes-mg">my old
  branch</a> upstream, created <a
  href="http://www.divmod.org/trac/query?status=new&status=assigned&status=reopened&reporter=mgedmin&component=Pyflakes&order=priority">trac
  tickets</a> for the rest.  Wrestled with bzr-svn merges, failed abysmally.</p>
<span class="net_nemein_favourites">4 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=58091b0278ab11de9f343b03fa2b09e209e2&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/58091b0278ab11de9f343b03fa2b09e209e2/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>1 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=58091b0278ab11de9f343b03fa2b09e209e2&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/58091b0278ab11de9f343b03fa2b09e209e2/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Fri, 24 Jul 2009 23:40:02 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-58091b0278ab11de9f343b03fa2b09e209e2</guid>
        </item>
        <item>
            <title>df</title>
            <link>http://mg.pov.lt/blog/disk-free</link>
            <description><![CDATA[

<p>Modern Linux system have all sorts of fake filesystems cluttering the output
of df and mount: tmpfs, bind mounts, fuse for ~/.gvfs, etc.  I have only one
real partition on my laptop, yet mount returns 22 lines of output.</p>

<p>Question: are there any df-like utilities that filter out all the crap and
show only interesting bits?  The standard df as well as <a
  href="http://kassiopeia.juls.savba.sk/~garabik/software/pydf/">pydf</a> both
display 8 lines instead of 1.  <a
  href="http://nickshontz.com/blog/ubuntu-hard-drive-usage">Discus</a> is
worse: it shows 20.  GUI utilities like <a
  href="http://www.marzocca.net/linux/baobab/">Baobab</a> also suffer from this
confusion, especially bind mounts.</p>

<p>Ironically, Ubuntu's update-motd <a
  href="https://bugs.launchpad.net/ubuntu/+source/update-motd/+bug/399513">gets
  confused</a> by Ubuntu's private user directories and displays disk stats for
~/Private as if it were a real partition.  <!-- This bug probably qualifies as
a <a href="https://wiki.ubuntu.com/PaperCut">paper cut</a>, but I hear Ubunteros
already have more of those than they can fix...  Well, okay, the "average user"
probably never sees /etc/motd, so it fails one of the prerequisites of being
a paper cut. --></p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=176aec1a70c711de98439308b943bd21bd21&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/176aec1a70c711de98439308b943bd21bd21/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>4 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=176aec1a70c711de98439308b943bd21bd21&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/176aec1a70c711de98439308b943bd21bd21/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Tue, 14 Jul 2009 22:38:32 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-176aec1a70c711de98439308b943bd21bd21</guid>
        </item>
        <item>
            <title>Surprising old-style class behaviour</title>
            <link>http://mg.pov.lt/blog/suprising-old-style-classes</link>
            <description><![CDATA[

<p><span style="text-decoration: line-through">Some anonymous Planet Python poster (at least I couldn't find the author's
  name on the blog)</span> Christian Wyglendowski <a
href="http://blog.dowski.com/2009/05/21/odd-old-style-vs-new-style-class-behavior/">asks
about a surprising difference between old-style and new-style classes</a>.
Since the comments on their blog are closed (which you find out only after
pressing Submit), I'll answer here.</p>

<p>The question, slightly paraphrased: given a class</p>
<blockquote><pre>
<span class="def">class</span> <span class="name">LameContainerOld</span>:
    <span class="def">def</span> <span class="name">__init__</span>(<span class="name">self</span>):
        <span class="name">self</span>.<span class="name">_items</span> = {<span class="string">'bar'</span>:<span class="string">'test'</span>}
 
    <span class="def">def</span> <span class="name">__getitem__</span>(<span class="name">self</span>, <span class="name">name</span>):
        <span class="keyword">return</span> <span class="name">self</span>.<span class="name">_items</span>[<span class="name">name</span>]
 
    <span class="def">def</span> <span class="name">__getattr__</span>(<span class="name">self</span>, <span class="name">attr</span>):
        <span class="keyword">return</span> <span class="name">getattr</span>(<span class="name">self</span>.<span class="name">_items</span>, <span class="name">attr</span>)
</pre></blockquote>
<p>why does the 'in' operator work</p>
<blockquote><pre>
<span class="prompt">&gt;&gt;&gt;</span> <span class="name">container</span> = <span class="name">LameContainerOld</span>()
<span class="prompt">&gt;&gt;&gt;</span> <span class="string">'foo'</span> <span class="keyword">in</span> <span class="name">container</span>
<span class="output">False</span>
<span class="prompt">&gt;&gt;&gt;</span> <span class="string">'bar'</span> <span class="keyword">in</span> <span class="name">container</span>
<span class="output">True</span>
</pre></blockquote>
<p>when the equivalent new-style class raises a KeyError: 0 exception? Also, why
does __getattr__ appear to be called to get the bound __getitem__ method of the
dict?</p>
<blockquote><pre>
<span class="prompt">&gt;&gt;&gt;</span> <span class="name">container</span>.<span class="name">__getitem__</span>
<span class="output">&lt;bound method LameContainerNew.__getitem__ of {'bar': 'test'}></span>
</pre></blockquote>

<p>What actually happens here is that LameOldContainer.__getattr__ gets called
for special methods such as __contains__ and __repr__.  This is why (1) the
'in' check works, and (2) it appears, at first glance, that you get the wrong
__getitem__ bound method.   If you pay close attention to the output, you'll
see that it's the __getitem__ of LameOldContainer; it's just that
repr(LameOldContainer()) gets proxied through to the dict.__repr__ when you
don't expect it:</p>

<blockquote><pre>
<span class="prompt">&gt;&gt;&gt;</span> <span class="name">container</span>
<span class="output">{'bar': 'test'}</span>
</pre></blockquote>

</p>Special methods never go through __getattr__ for new-style classes,
therefore neither __contains__ nor __repr__ are proxied if you make the
container inherit object.  If there's no __contains__ method, Python falls back
to the sequence protocol and starts calling __getitem__ for numbers 0 through
infinity, or until it gets an IndexError exception.</p>
<span class="net_nemein_favourites">2 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=01a3d842464211de9f6bc795ba3382a582a5&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/01a3d842464211de9f6bc795ba3382a582a5/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>4 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=01a3d842464211de9f6bc795ba3382a582a5&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/01a3d842464211de9f6bc795ba3382a582a5/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Thu, 21 May 2009 19:59:53 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-01a3d842464211de9f6bc795ba3382a582a5</guid>
        </item>
        <item>
            <title>Enabling comments in PyBlosxom</title>
            <link>http://mg.pov.lt/blog/blog-comments</link>
            <description><![CDATA[

<p>I've just spent the whole night setting up blog comments.  PyBlosxom doesn't
make it painless, sadly, more like the opposite.</p>

<p>First: don't be scared by the list of comment-related plugins on the
PyBlosxom site.  There's only one important plugin: comments.  All others
depend on it and enhance its functionality. The last three or four times I was
about to add comments to my blog I got scared at step one: evaluate the
available plugins.  Don't repeat my mistake!</p>

<p>Second, follow the instructions carefully.  There's no shortcut.</p>

<p>Third, fix what's broken.  Be prepared to debug the source code.  <tt>print
  &gt;&gt; sys.stderr, "message"</tt> is your friend.</p>

<p>Fourth, fiddle with the look (CSS and HTML).</p>

<p>Fifth, write a blog post and eagerly await your first comments.</p>

<p>Step 3 screams for an explanation, doesn't it?  Problem 1: the comments
plugin requires that you use categories in your blog.  I'm not (I'm holding out
for tags).  Workaround: comment out <tt>if entry['absolute_path']</tt> check in
cb_story and cb_story_end.</p>

<p>Problem 2: the AJAX post returns "Empty response from server".  Workaround:
modify cb_story_end to call readComments directly if <tt>entry['num_comments']
  is
  None</tt>, since cb_story, which usually does the read, is not called during
the AJAX post.</p>

<p>Problem 3: if you enable comment moderation (by setting comment_draft_ext to
a different value from comment_ext), the AJAX post returns "Empty response from
server" once more.  Workaround: modify cb_prepare to notice this case and set
<tt>data['moderated'] = True</tt>, create a new template comment-moderated and
render it in cb_story_end just like the preview template is rendered; also modify
__shouldOutput to return True when rendering comment-moderated.</p>

<p>I'll post patches to the pyblosxom mailing tomorrow, unless I forget.  It's
6 am already, and I'm kind of sleepy.  I just hope I haven't inadvertently
broken my RSS feed or flooded any planets.</p>

<p>Oh, and a helpful hint: don't name the post you're writing
<tt>comments.txt</tt>, or the #comments anchor will point to the start of the
story instead of the comments.</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=52ecefe241cc11de9f2d253fe76e30dd30dd&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/52ecefe241cc11de9f2d253fe76e30dd30dd/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>5 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=52ecefe241cc11de9f2d253fe76e30dd30dd&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/52ecefe241cc11de9f2d253fe76e30dd30dd/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Sat, 16 May 2009 03:46:13 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-52ecefe241cc11de9f2d253fe76e30dd30dd</guid>
        </item>
        <item>
            <title>Buildbot issues on Ubuntu Hardy</title>
            <link>http://mg.pov.lt/blog/hardy-nfs-sighup</link>
            <description><![CDATA[

<p><strong>Update</strong>: The story continues, but solution is not in sight
yet.</p>

<p>I upgraded a buildbot slave to Ubuntu 8.04 (Hardy) recently and now I'm
getting a strange intermittent failure: sometimes
<tt>cp -r /local/dir /nfs/mounted/dir</tt> fails
("process killed by signal 1", i.e. SIGHUP).</p>

<p>I wonder if NFS is relevant or incidental to the issue?</p>

<p>Google finds <a
  href="http://osdir.com/ml/python.buildbot.devel/2005-07/msg00000.html">an old
  thread from 2005</a>, with a workaround (usepty=False), but I'd like to
understand the problem before applying random fixes.</p>

<p>So far three different build steps doing <tt>cp -r</tt> have failed during
10 days.  I've now changed them all to <tt>cp -rv</tt>, so I can at least see
if the failure is in the middle of the copy or at the end, if it fails
again.</p>

<p><strong>Update</strong>: so far 4 build steps have failed on 6 separate
occasions:</p>

<!--
  ./ivija-coverage/754-log-cp-stdio
  ./ivija-coverage/757-log-cp-stdio
  ./ivija-coverage/757-log-cp_2-stdio
  ./ivija-coverage/770-log-rm_2-stdio
  ./ivija-coverage/773-log-rm_2-stdio
  ./ivija-docs/342-log-cp-stdio
  -->
<pre>
May  5 02:31: cp -r local-dir1 nfs-mounted-dir1  <!-- ivija-coverage cp -->
May  6 02:31: cp -r local-dir1 nfs-mounted-dir1  <!-- ivija-coverage cp -->
May  6 04:33: cp -r local-dir2 nfs-mounted-dir2  <!-- ivija-coverage cp_2 -->
May 15 02:00: cp -r local-dir3 nfs-mounted-dir3  <!-- ivija-docs cp -->
May 17 04:32: rm -rf nfs-mounted-dir4            <!-- ivija-coverage rm_2 -->
May 20 04:31: rm -rf nfs-mounted-dir4            <!-- ivija-coverage rm_2 -->
</pre>

<p>I see no particular correlation between step duration and results, e.g.
the rm -rf step usually takes between 2.2 and 4.6 seconds.  The two SIGHUPs
happened after 2.4 seconds.
</p>

<!--
     cd /var/lib/buildbot/masters/ivija
     python
     import pickle, pprint
     pp = pprint.pprint
     coverage_jobs = [pickle.load(file('ivija-coverage/' + str(n))) for n in range(750, 774)]
     docs_jobs = [pickle.load(file('ivija-docs/' + str(n))) for n in range(300, 349)]

     jjobs = docs_jobs
     pp(['%s %.1f %s' % (s.name, s.finished - s.started, s.results) for s in (b.steps[[ss.name for ss in jobs[-1].steps].index('cp')] for b in jobs) if s.finished])

     rm_2 takes between 2.2 and 4.6 seconds.  The two failures were at 2.4
     seconds.

     cp_2 takes between 6.2 and 19.2 seconds.  The one failure was after 7.2
     seconds.

     cp takes between 3.5 and 15.7 seconds.  The two failures were after 3.8
     and 4.1 seconds.

     ivija-docs cp takes between 0.8 and 4.1 seconds.  The failure was after
     1.6 seconds.
  -->

<p>They all make no output.  When I changed the cp steps and added a -v, they
stopped failing, but that could be just a coincidence.</p>

<p>We're having an email conversation with Jean-Paul Calderone ("exarkun")
about the possibility of this being PTY-related, with no clear resolution
so far.</p>

<p>And, hey, now this blog supports comments ;)</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=0040580c414e11de8c9f87581446ff2aff2a&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/0040580c414e11de8c9f87581446ff2aff2a/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>6 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=0040580c414e11de8c9f87581446ff2aff2a&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/0040580c414e11de8c9f87581446ff2aff2a/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Fri, 15 May 2009 12:43:25 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-0040580c414e11de8c9f87581446ff2aff2a</guid>
        </item>
        <item>
            <title>Expert Python Programming</title>
            <link>http://mg.pov.lt/blog/expert-python-programming</link>
            <description><![CDATA[

<p>It's been a while since the last <a
  href="http://www.packtpub.com/expert-python-programming/book">Expert Python
  Programming</a> review on <a href="http://planet.python.org">Planet
  Python</a>.  Y'all might've forgotten about this book by now.  Time for a
reminder?  (Actually, I'm just <span style="text-decoration:
  line-through">lazy</span> busy, and this is why this review hasn't appeared
sooner.)</p>

<p>I received a free PDF copy of this book from Packt Publishing, with the
understanding that I'll post a review on my blog.  This is it.  Short summary:
it is a good book marred by a lot of mostly inconsequential little mistakes.
I'd give it four stars out of five.</p>

<p><small>Aside: the PDF that I could download was personalized and had my
  name and address in the footer of every page.  A very nice form of DRM that
  did not restrict my software choices for reading the book (Evince and also
  PDF Reader on Nokia Internet Tablets).</small></p>

<p><small>I bring it up here because it seems that Packt could've also applied fixes
  for <a
    href="http://www.packtpub.com/view_errata/book/expert-python-programming">the
    known errata</a> to the personalized version, yet missed that opportunity.
  Perhaps it's technically more difficult than slapping a footer on every page.
  Or maybe it's better if everyone buying the book, whether in paper or in
  PDF, gets to see the same text.</small></p> 

<p>The author (Tarek Ziade) covers a wide range of topics in the book, ranging
from syntax (probably useful for those who've been programming in Python for
quite a few years, and didn't have the time to keep up with the language
changes before picking up this book) to style, source code organization,
project infrastructure, software life cycle, documentation, testing and
optimization, and finally ending with a review of some of the popular design
patterns.  The middle parts were the most interesting for me personally.  I
learned a thing or two, disagreed with the author on a few minor points (which
are mostly a matter of preference), and managed to finish the book despite
constant irritating little pricks I feel when I notice an error (I confess I'm
a pedant.  A missing space after a colon drives me up the wall).</p>

<p>As an example of the disagreement: I have an aversion to code-generating
tools where you have to edit the generated code by hand.  I could say more, but
this is a topic for another time.  Next, I strongly dislike <tt>sudo
  easy_install</tt> since it scribbles onto the part of the filesystem
exclusively reserved for your OS's package management tools.  And I don't think
porting the original 23 design patterns to other programming languages is a
good way to describe what those languages are about.  (Also, <tt>set
  tabstop=4</tt> in your .vimrc?  Heresy!  The Right Thing To Do is
 <tt>set <em>soft</em>tabstop=4</tt>, as all right-thinking Vim users will
 doubtlessly agree.  All hail the one true text editor! <small>Oh dear, now I'm
   glad I don't have comments on this blog...</small>)</p>

<p>The goodies: Chapter 1 (the bits about <a
  href="http://docs.python.org/using/cmdline.html#envvar-PYTHONSTARTUP">PYTHONSTARTUP</a>
on page 19) gave me persistent history for my interactive Python prompt, nicely
complementing the coloured prompt and tab-completion I already had snarfed from
somewhere else on the net (probably Peter Norvig's <a
  href="http://norvig.com/python-iaq.html">Python IAQ</a>).  Chapter 12
provided good examples of how to do profiling for time (page 281) and memory
(page 291).  I like Tarek's @profile decorator (measure time, pystones
<em>and</em> memory at the same time).  <small>My <a
    href="http://pypi.python.org/pypi/profilehooks">profilehooks</a> module was
not mentioned, *sniff*  ;-).</small>  Chapter 13 told me about <a
  href="http://docs.python.org/library/queue.html#Queue.Queue.join">Queue.join</a>
and <a
  href="http://docs.python.org/library/queue.html#Queue.Queue.task_done">task_done</a>
that snuck into the stdlib with Python 2.5 without me noticing.</p>

<!--
<p>Chapter 2 really ought to mention <a
  href="http://docs.python.org/library/functools.html#functools.wraps">functools.wraps</a>
when discussing decorators and how the name of the wrapper function may make
tracebacks confusing.</p>

<p>Chapter 3 presents a rare example of circumstances in which Python's
significant whitespace is a downside: the code example on page 75 is
syntactically incorrect (partly because of broken indentation), as well as
semantically incorrect (it shows the mechanism behind setattr, not getattr),
and the correction in the official errata list <em>omits the indentation
  altogether</em>.</p>

<p>At around page 95, according to my notes, I invented a new metric of book
quality: WTFs per page, It's closely related to <a
  href="http://www.osnews.com/story/19266/WTFs_m">WTFs per minute</a>, but
independent of your reading speed.  Then, at around page 165 I got tired of
making a note of every little thing and started just reading.  This was
somewhat more enjoyable.</p>

<p>I was happy to see examples of how to do profiling on page 281, and especially
examples with memory profiling with the eccentric <a
  href="http://pypi.python.org/pypi/guppy">Guppy/Heapy</a> on page 291.  My <a
  href="http://pypi.python.org/pypi/profilehooks">profilehooks</a> module was
not mentioned, *sniff*.  ;-)</p>
-->

<p>I haven't mentioned topics covered in the book that I was already familiar
with, such as setuptools, virtualenv, zc.buildout, Sphinx, Nose, Buildbot, or
Mercurial.  Yet, in my opinion, those are the most useful parts of the book.
The breadth of the topics is amazing: I could hardly think of something that
every serious Python programmer should know that isn't wasn't mentioned.  I
believe the depth was exactly right: mention solutions that are available, show
how they feel when used and what they can do, point to the relevant web page
and then stop.  And not only tools, the descriptions of workflows (how to
organize your source trees, how to develop software consisting of multiple
packages, how to make releases), while hardly universal, are invaluable.</p>

<p>One thing prevents this from being a perfect book: errata.  At around page
95, according to my notes, I invented a new metric of book quality: WTFs per
page, It's closely related to <a
  href="http://www.osnews.com/story/19266/WTFs_m">WTFs per minute</a>, but
independent of your reading speed.  At around page 165 I got tired of making a
note of every little thing that I noticed and started just reading.  This was
considerably more enjoyable.  I hope there's a second edition will all the bugs
shaken out.  To that end, I should go through my notes again and submit them
via the online errata form.  <small>Yay, more work...</small></p>
<span class="net_nemein_favourites">3 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=cb0259f43b8211debf270fc3e520a9cda9cd&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/cb0259f43b8211debf270fc3e520a9cda9cd/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>3 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=cb0259f43b8211debf270fc3e520a9cda9cd&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/cb0259f43b8211debf270fc3e520a9cda9cd/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Fri, 08 May 2009 03:46:14 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-cb0259f43b8211debf270fc3e520a9cda9cd</guid>
        </item>
        <item>
            <title>Filing bugs on GUI apps</title>
            <link>http://mg.pov.lt/blog/filing-bugs-on-gui-apps</link>
            <description><![CDATA[

<p>Ubuntu people want users to use apport to report bugs.  There's a command-line
tool called 'ubuntu-bug' that you can use if you know the name of the package
or at least the name of executable.  There's a "Report a problem" menu item
in many, but not all GUI apps.</p>

<p>Here's what you can do if the GUI app in question doesn't have that menu
item, and you don't remember what it's called, and you're the same sort of a
crazy command-line person that I am:</p>

<pre>
<span class="prompt">$</span> <span class="typing">xprop|grep PID</span>
</pre>

<p>Click on the app's window.  Watch that shell command return a line that
looks like</p>

<pre>
_NET_WM_PID(CARDINAL) = 807
</pre>

Now run

<pre>
<span class="prompt">$</span> <span class="typing">ps 807</span>  # substitute the real number
</pre>

<p>You'll see the command name, e.g.</p>

<pre>
  PID TTY      STAT   TIME COMMAND
  807 ?        S      0:02 /usr/lib/indicator-applet/indicator-applet --oaf-acti
</pre>

<p>Now you can run</p>

<pre>
<span class="prompt">$</span> <span class="typing">ubuntu-bug /usr/lib/indicator-applet/indicator-applet</span>
</pre>

<p>If you're not running Jaunty, you'll need to do one more step to find the
name of the Ubuntu package:</p>

<pre>
<span class="prompt">$</span> <span class="typing">dpkg -S /usr/lib/indicator-applet/indicator-applet</span>
indicator-applet: /usr/lib/indicator-applet/indicator-applet
</pre>

<p>You can use that with apport-cli or on the Launchpad online bug reporting
form.</p>

<p>For some (many?) programs you can short-circuit this trail by looking at
the WM_CLASS property instead of the _NET_WM_PID property:</p>

<pre>
<span class="prompt">$</span> <span class="typing">xprop|grep CLASS</span>
WM_CLASS(STRING) = "indicator-applet", "Indicator-applet"
</pre>

<p>While there is no requirement for the window class name to match the name of
the Ubuntu package or the name of the executable, it may give you a reasonable
guess.</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=60a51d76253e11dea1c70164bcff94aa94aa&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/60a51d76253e11dea1c70164bcff94aa94aa/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>7 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=60a51d76253e11dea1c70164bcff94aa94aa&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/60a51d76253e11dea1c70164bcff94aa94aa/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Thu, 09 Apr 2009 19:41:08 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-60a51d76253e11dea1c70164bcff94aa94aa</guid>
        </item>
        <item>
            <title>Upgrade to Ubuntu Jaunty</title>
            <link>http://mg.pov.lt/blog/upgrade-to-jaunty</link>
            <description><![CDATA[

<p>Ubuntu 9.04 is going to be released in around three weeks.  As usual I
couldn't wait (and saw that some bugs that were irritating me every day
were fixed in Jaunty), so I upgraded to the current beta.</p>

<p>After <a
  href="https://bugs.launchpad.net/ubuntu/+source/update-manager/+bug/154236/comments/3">a
  little hiccough</a> at the beginning, the upgrade was the smoothest Ubuntu
upgrade I've ever had: I spent those two and a half hours browsing the web,
watching screencasts and chatting on IRC, while update-manager worked in the
background.  Firefox was mostly very responsive, only stuttering when
update-manager got around to unpacking openoffice.org-common.  There were few
debconf or conffile questions (one from sysstat 2 minutes into the upgrade,
then a conffile question after 1 hour and 20 minutes, then two more after 15
minutes, and one more 5 minutes later.  And the last one 10 minutes later).
There were no ugly theme changes or failing GNOME applet error messages during
the upgrade.  Near the end X-Chat automatically started showing new-style
notifications (beautiful!) and Firefoxes nicely asked to be restarted with a
fold-down notification bar.</p>

<p>Nice.  Now, after a reboot things were not so nice: I couldn't login.
After typing in my password and a couple of mode changes I was kicked back
to the GDM prompt.  I panicked and started logging into the text consoles
and trying to run startx, quite in vain, since when I just tried gdm again
it worked fine.</p>

<p>The intel video driver feels slower, as promised by the release notes,
but it's acceptable as long as you don't try to rotate the external screen.
Then it's horrible and unusable&mdash;a regression since Intrepid.  I'll have
to retry with UXA.</p>

<p>Compiz failed to enable a plugin (GNOME Compatibility), so a couple of
key bindings didn't work (Alt+F1 to get the menu, Alt+F2 to get the run dialog,
my custom keybinding to open a terminal) until someone on FreeNode told me
what to enable.</p>

<p>The Flash plugin is now swfdec, and it is unable to cope with Youtube music
videos&mdash;the sound is all choppy.  I'm wondering if this is swfdec's fault,
pulseaudio's fault (it's common knowledge that all audio problems stem from
pulseaudio, right? ;) or X.org's fault (top shows it's X that's eating 90% CPU
when swfdec is trying to play a video).</p>

<p>A lot of very irritating bugs are gone.  I don't need to restart Compiz
after playing with xrandr.  X doesn't crash after I play with xrandr.
Two-finger scrolling with the Synaptics touchpad doesn't produce phantom
scroll-down-17-pages events when I take my fingers off the touchpad.  The
GNOME panels don't migrate to the external screen when I play with xrandr
(but one of them <a
  href="https://bugs.launchpad.net/ubuntu/+source/gnome-panel/+bug/355848">jumps
  from the bottom to the top</a> when I play some more).  The new splash
screen has a pretty gradient for its progress bar (but is displayed off-center,
maybe because I added vga=872 to my GRUB kernel options list to avoid ugly
stretching of text consoles).  X.org no longer distorts the aspect ratio of
1024x768 when stretching it to fit the 1280x800 screen&mdash;now I get sensible
black bars on the sides. <strong>The new notification bubbles are
  beautiful!</strong>.  I could stare at them all day.  (But the new indicator
applet <a
  href="https://bugs.launchpad.net/ubuntu/+source/indicator-applet/+bug/334490">is
  ugly</a>.)</p>

<p>Overall I'm happy.  A bunch of very irritating bugs were replaced with
a smaller bunch of somewhat less irritating bugs.  The intel video slowdown
scares me a bit, though, but the prettiness of the notification bubbles
outweigh everything else.  What can I say, I like pretty things&mdash;Ooh,
shiny!</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=b9766d3a223a11de82bfeb414ec71fd31fd3&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/b9766d3a223a11de82bfeb414ec71fd31fd3/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>6 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=b9766d3a223a11de82bfeb414ec71fd31fd3&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/b9766d3a223a11de82bfeb414ec71fd31fd3/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Sun, 05 Apr 2009 23:37:26 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-b9766d3a223a11de82bfeb414ec71fd31fd3</guid>
        </item>
        <item>
            <title>Submitting patches the Launchpad way</title>
            <link>http://mg.pov.lt/blog/smooth-bug-reporting</link>
            <description><![CDATA[

<p>Today I happened to read about <a
  href="http://pypi.python.org/pypi/lazr.enum">lazr.enum</a> in a mailing list.
I went to <a href="http://pypi.python.org/pypi/lazr.enum">the PyPI page</a> and
saw raw <a href="http://docutils.sourceforge.net/rst.html">ReStructuredText</a>
markup instead of a nicely formatted page.  Now I know from prior experience
that this happens when the package's description has an error in the markup.
I thought I'd report a bug and provide a patch.</p>

<p>Leap of knowledge: since I know lazr.enum was created by the Launchpad.net
team I could safely assume they were keeping the sources in Launchpad.  Therefore
I was pretty sure I could get them with</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr branch lp:lazr.enum</span>
</pre>

<p>so I ran that command and it worked.</p>

<p>Next I looked at setup.py to see how it produces the long_description field.
It was reading the contents of a couple of text files, one of them being
src/lazr/enum/README.txt.  I looked at that and saw a
<tt>..&nbsp;toc-tree:</tt> directive that does not exists in plain docutils
(it's a <a href="http://sphinx.pocoo.org/">Sphinx</a> extension).</p>

<p>I added up a couple of lines to setup.py to strip that out, tested it
(with <tt>setup.py --long-description &gt; test.rst; restview test.rst</tt>)
committed to my local branch, and created a bug report in Launchpad.  Then I
was a bit lost, since I didn't know how to make my fix available.  Attach a
patch?  Maybe, but I wanted to see if this distributed version control thing is
good for anything else.</p>

<p>I thought that first I'd make that branch public, and then see if there
was a way to link it to the bug report.  I ran</p>

<pre>
<span class="prompt">$</span> <span class="typing">bzr push lp:~mgedmin/lazr.enum/pypi-fix</span>
</pre>

<p>which took a few seconds to create a new public branch on Launchpad with my
fix in it (it would be nice if I didn't have to explicitly specify my Launchpad
username and the project name&mdash;both of which bzr already knows&mdash;and
just specify the name of the branch).  Then I went back to my bug report and
saw an option to link it to a branch.  There was a search field in the popup
that found my "~mgedmin/lazr.enum/pypi-fix" easily enough when I pasted it
into the search box.</p>

<p>After clicking on the branch, I saw a "propose a merge" option.  I did that
and Launchpad sent an email to the developers asking them to merge my fix.</p>

<p>I made one mistake, I think: I should've created the bug report
<em>first</em>, and then mentioned the bug number in my commit message (with
<tt>bzr commit --fixes=NNN</tt>, although here I'm suddenly not sure if the bug
number should be left bare, or prefixed with something like "lp" to indicate it
was a Launchpad bug number?).</p>

<p>Other than that it was a pretty smooth experience.  When will I be able to
do that for Ubuntu packages?</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=47899d3a223211de99a1d5063f6416691669&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/47899d3a223211de99a1d5063f6416691669/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>6 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=47899d3a223211de99a1d5063f6416691669&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/47899d3a223211de99a1d5063f6416691669/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Sun, 05 Apr 2009 22:36:59 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-47899d3a223211de99a1d5063f6416691669</guid>
        </item>
        <item>
            <title>Baking CSS into RSS</title>
            <link>http://mg.pov.lt/blog/baking-css-into-rss</link>
            <description><![CDATA[

<p>Wanted: PyBlosxom plugin to bake CSS styles into the RSS feed, i.e.
replace things like &lt;span style="keyword"&gt; in the main blog pages with
things like &lt;span style="color: #ff7700"&gt; in the RSS.</p>

<p>Also wanted: a spam-resistant comments plugin.  (Maybe some/all of them are?
I was always afraid to try.)   And a tags plugin (categories are too limiting).
And useful page titles.  And a pony.</p>

<p><strong>Update:</strong> Laurence Rowe and Alexander Artemenko suggested I
check out <a href="http://code.google.com/p/cssutils/">cssutils</a>.  Alexander
also pointed out that <a
  href="http://stackoverflow.com/questions/118685/how-can-i-apply-my-css-stylesheet-to-an-rss-feed">a
  similar question was asked on stackoverflow</a>.  <!-- Sadly, most of the answers
there are variations "you shouldn't do that you impurely-thinking person", with
some exceptions that allow as to how this may sometimes be a valid need, but
don't point to any tools achieving that.-->  Peter Bengtsson recently posted <a
  href="http://www.peterbe.com/plog/premailer.py">a similar tool</a> built with
lxml and regexps.  Meanwhile, I took the plunge and installed a comments
plugin, and even somehow managed not to get drowned in spam.</p>
<span class="net_nemein_favourites">0 <a href="http://maemo.org/news/?net_nemein_favourites_execute=fav&net_nemein_favourites_execute_for=6e0c1a12202211deaad199dce53ab9acb9ac&net_nemein_favourites_url=https://maemo.org/news/favorites//json/fav/midgard_article/6e0c1a12202211deaad199dce53ab9acb9ac/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-favorite.png" style="border: none;" alt="Add to favourites" title="Add to favourites" /></a>7 <a href="http://maemo.org/news/?net_nemein_favourites_execute=bury&net_nemein_favourites_execute_for=6e0c1a12202211deaad199dce53ab9acb9ac&net_nemein_favourites_url=https://maemo.org/news/favorites//json/bury/midgard_article/6e0c1a12202211deaad199dce53ab9acb9ac/" class="net_nemein_favourites_create"><img src="http://static.maemo.org:81/net.nemein.favourites/not-buried.png" style="border: none;" alt="Bury" title="Bury" /></a></span>]]></description>
            <author>Marius Gedminas &lt;marius@gedmin.as&gt;</author>
            <category>feed:b1704e4f997dfb2448842ae2dc57ed3f</category>
            <pubDate>Fri, 03 Apr 2009 07:38:27 +0000</pubDate>
            <guid>http://maemo.org/midcom-permalink-6e0c1a12202211deaad199dce53ab9acb9ac</guid>
        </item>
    </channel>
</rss>
