<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <link>http://mikewatkins.ca/tags/python/</link>
  <atom:link href="http://mikewatkins.ca/tags/python/feeds/rss" type="application/rss+xml" rel="self"/>
  <lastBuildDate>Mon, 03 Nov 2008 21:15:54 GMT</lastBuildDate>
  <title>mike watkins dot ca</title>
  <description>XML Feed for mike watkins dot ca</description>
  <language>en</language>
  <generator>Parlez/0.1</generator>
<item>
  <title>NFB: National Film Board Online</title>
  <link>http://mikewatkins.ca/2008/11/03/nfb-national-film-board-online/</link>
  <description><![CDATA[
<div class="document">
<p><a class="reference" href="http://beta.nfb.ca/">http://beta.nfb.ca/</a> - Canada's National Film Board is undergoing a major change to what it offers and how it offers it, courtesy of some <a class="reference" href="http://beta.nfb.ca/about/">Python technology</a> (<a class="reference" href="http://www.djangoproject.com/">Django</a>).</p>
<p>Full-length films available for your viewing pleasure. I particularly enjoy watching those from the 60's and 70's - it truly feels like walking back in time.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:634</guid>
  <pubDate>Mon, 03 Nov 2008 17:40:34 GMT</pubDate>
</item>
<item>
  <title>Python 3.0: Templating Observations</title>
  <link>http://mikewatkins.ca/2008/11/02/python-30-templating-observations/</link>
  <description><![CDATA[
<div class="document">
<p>Stumbling through Python-related posts on <a class="reference" href="http://planet.python.org/">Planet Python</a> led me to a curious Python 3.0 discovery.</p>
<p>First I noted phililon wrote <a class="reference" href="http://philikon.wordpress.com/2008/10/22/chameleon-byte-compiler-for-zpt-and-genshi/">Chameleon: byte compiler for ZPT and Genshi</a> (&quot;Fricken fast&quot;, apparently), which led me to <a class="reference" href="http://code.google.com/p/spitfire/">Google Spitfire</a> (supposed to be even faster - it's certainly very fast on Python 2.5), which led me to <a class="reference" href="http://code.google.com/p/spitfire/source/browse/trunk/tests/perf/bigtable.py">Google's implementation</a> of the Genshi <a class="reference" href="http://genshi.edgewall.org/wiki/GenshiPerformance">bigtable.py</a> benchmark.</p>
<p>As I'm currently beta testing / porting my code base to be compatible with Python 3.0, and never having seen a performance test I didn't want to try or mangle the interpretation of, this naturally led me to check out my choice of web framework <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a>'s default template package <a class="reference" href="http://www.mems-exchange.org/software/qpy/">QPY</a> on Python 3.0.</p>
<p>Using the Google adaptation of the Genshi <tt class="docutils literal"><span class="pre">bigtable.py</span></tt> benchmark, I added an entry for QP; the benchmark results for packages which are included in or run on both Python 2.5 and Python 3.0 are interesting:</p>
<div class="highlight"><pre><span class="nv">%</span> <span class="nv">python2</span><span class="o">.</span><span class="mi">5</span> <span class="n">test</span><span class="o">/</span><span class="n">bigtable</span><span class="o">.</span><span class="n">py</span>
<span class="n">ElementTree</span>                       <span class="mi">743</span><span class="o">.</span><span class="mi">76</span> <span class="n">ms</span>
<span class="n">StringIO</span>                          <span class="mi">137</span><span class="o">.</span><span class="mi">80</span> <span class="n">ms</span>
<span class="n">list</span> <span class="n">concat</span>                        <span class="mi">22</span><span class="o">.</span><span class="mi">25</span> <span class="n">ms</span>
<span class="n">QPY</span> <span class="n">Template</span>                      <span class="mi">125</span><span class="o">.</span><span class="mi">84</span> <span class="n">ms</span>

<span class="nv">%</span> <span class="nv">rmtempfiles</span>

<span class="nv">%</span> <span class="nv">python3</span><span class="o">.</span><span class="mi">0</span> <span class="n">test</span><span class="o">/</span><span class="n">bigtable</span><span class="o">.</span><span class="n">py</span>
<span class="n">ElementTree</span>                       <span class="mi">412</span><span class="o">.</span><span class="mi">08</span> <span class="n">ms</span>
<span class="n">StringIO</span>                          <span class="mi">136</span><span class="o">.</span><span class="mi">97</span> <span class="n">ms</span>
<span class="n">list</span> <span class="n">concat</span>                        <span class="mi">23</span><span class="o">.</span><span class="mi">19</span> <span class="n">ms</span>
<span class="n">QPY</span> <span class="n">Template</span>                       <span class="mi">36</span><span class="o">.</span><span class="mi">09</span> <span class="n">ms</span>
</pre></div>
<p>Sadly there isn't much to compare QPY to on py3k at present. Hopefully other web frameworks, including the big ones, are busy at work producing code compatible with Python 2.x and 3.0. My purpose for doing the comparison was to see if Python 3.0 had a material effect on other templating packages, not so much to compare functionality of say Django templates  to QPY as their intended audience and feature set are so very different.</p>
<p>Incidentally the py3k improvement for QPY represents a 71.3% speed-up for QPY on Python 3.0rc1 over what was already amply quick on Python 2.5.1. Whatever the difference is, its not unique to QPY: ElementTree also benefits greatly, turning in a 44.6% better performance.</p>
<p>These timings are done on an old desktop Unix workstation; more modern machines will turn in much better numbers but relatively speaking I believe these improvements will generally hold up.</p>
<p>On David Binger's Macbook Pro:</p>
<div class="highlight"><pre><span class="c"># py3k  22 ms</span>
<span class="c"># 2.5.1 55 ms</span>
<span class="k">from</span> <span class="nn">timeit</span> <span class="k">import</span> <span class="n">Timer</span>
<span class="k">print</span><span class="p">(</span><span class="n">Timer</span><span class="p">(</span><span class="s">&#39;qpy_template()&#39;</span><span class="p">,</span> <span class="s">&#39;from qpy.example.example2 import</span>
<span class="n">qpy_template</span><span class="s">&#39;).timeit(1000))</span>
</pre></div>
<p>Using timeit to get time for qpy_template() call:</p>
<pre class="literal-block">
25   ms  Python2.5.1 with quote.so
50   ms  Python2.5.1 without quote.so
11.7 ms  Python3.0 with quote.so
44   ms  Python 3.0 without quote.so

Compiled speedup is 25/11.7 = 2.1
Uncompiled speedup is 50/44 = 1.1
</pre>
<p>David also notes:</p>
<blockquote>
When using profile to test template execution, the it looks like we get a speedup from 254 ms to 199 ms per qpy_template() call between Python 2.5.1 and Python 3.0. Nothing in the profile jumps out at me as being responsible for the change.  The C code does, though, do some extra work for Python 2 to force the default encoding to be utf8.  Maybe that adds up to a significant amount of time.</blockquote>
<p><strong>QPY?</strong> The QPY template model is going to look upside down if your experience or preference is with any of the many code-in-content template packages out there. Here's qpy-enabled Python source compared to a couple of other templating approaches:</p>
<div class="highlight"><pre><span class="n">table</span> <span class="o">=</span> <span class="p">[</span><span class="nb">dict</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">b</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span><span class="n">c</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span><span class="n">d</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span><span class="n">e</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span><span class="n">f</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span><span class="n">g</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span><span class="n">h</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span><span class="n">i</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span><span class="n">j</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
      <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">)]</span>

<span class="c"># QPY</span>
<span class="k">def</span> <span class="nf">qpy_template</span><span class="p">:</span><span class="n">xml</span><span class="p">():</span>
    <span class="s">&#39;&lt;table&gt;&#39;</span>
    <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">table</span><span class="p">:</span>
        <span class="s">&#39;&lt;tr&gt;&#39;</span>
        <span class="k">for</span> <span class="n">col</span> <span class="ow">in</span> <span class="n">row</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
            <span class="s">&#39;&lt;td&gt;</span><span class="si">%s</span><span class="s">&lt;/td&gt;&#39;</span> <span class="o">%</span> <span class="n">col</span>
        <span class="s">&#39;&lt;/tr&gt;&#39;</span>
    <span class="s">&#39;&lt;/table&gt;&#39;</span>

<span class="c"># Genshi</span>
<span class="k">def</span> <span class="nf">test_genshi</span><span class="p">():</span>
    <span class="n">stream</span> <span class="o">=</span>  <span class="n">MarkupTemplate</span><span class="p">(</span><span class="s">&quot;&quot;&quot;</span>
<span class="s">    &lt;table xmlns:py=&quot;http://genshi.edgewall.org/&quot;&gt;</span>
<span class="s">    &lt;tr py:for=&quot;row in table&quot;&gt;</span>
<span class="s">    &lt;td py:for=&quot;c in row.values()&quot; py:content=&quot;c&quot;/&gt;</span>
<span class="s">    &lt;/tr&gt;</span>
<span class="s">    &lt;/table&gt;&quot;&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="n">table</span><span class="o">=</span><span class="n">table</span><span class="p">)</span>
    <span class="n">stream</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="s">&#39;html&#39;</span><span class="p">,</span> <span class="n">strip_whitespace</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>

<span class="c"># Django</span>
<span class="k">def</span> <span class="nf">test_django</span><span class="p">():</span>
    <span class="n">context</span> <span class="o">=</span> <span class="n">DjangoContext</span><span class="p">({</span><span class="s">&#39;table&#39;</span><span class="p">:</span> <span class="n">table</span><span class="p">})</span>
    <span class="n">DjangoTemplate</span><span class="p">(</span><span class="s">&quot;&quot;&quot;</span>
<span class="s">    &lt;table&gt;</span>
<span class="s">    {</span><span class="si">% f</span><span class="s">or row in table %}</span>
<span class="s">    &lt;tr&gt;{</span><span class="si">% f</span><span class="s">or col in row.values %}{{ col|escape }}{</span><span class="si">% e</span><span class="s">ndfor %}&lt;/tr&gt;</span>
<span class="s">    {</span><span class="si">% e</span><span class="s">ndfor %}</span>
<span class="s">    &lt;/table&gt;&quot;&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
</pre></div>
<p>QPY makes writing quote-safe 'templates' as easy as writing Python code itself.</p>
<p>One of these days I'm sure to need a code-in-content solution and for raw performance alone the Cheetah inspired <a class="reference" href="http://code.google.com/p/spitfire/">Google Spitfire</a> is sure to be on my list of packages to look at. But I hope that day doesn't come soon as I've grown rather fond of the content-in-code approach of QPY which allows me to write Python code that looks like Python code.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:633</guid>
  <pubDate>Mon, 03 Nov 2008 21:15:54 GMT</pubDate>
</item>
<item>
  <title>QP, lighttpd, SSL and SCGI</title>
  <link>http://mikewatkins.ca/2008/10/22/qp-lighttpd-ssl-and-scgi/</link>
  <description><![CDATA[
<div class="document">
<p>A version of <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> supporting <a class="reference" href="http://python.org/download/">Python</a> 3.0 (and prior Python versions of course) will be released when Python 3.0 soon hits the streets. While doing some testing I created a <a class="reference" href="http://www.lighttpd.net/">lighttpd</a> configuration example illustrating setting up a virtual host with <a class="reference" href="http://python.ca/scgi/">SCGI</a> (QP provides http and scgi servers natively) and SSL support:</p>
<div class="section">
<h2><a id="qp-with-lighttpd" name="qp-with-lighttpd">QP with lighttpd</a></h2>
<p>lighttpd is a lightweight web server that includes support for both SCGI and
SSL. As noted in the Apache configuration example regarding &quot;https_address&quot;
and &quot;as_http_address&quot;, the same caveats apply. If you find it necessary to
create a SSL &quot;pemfile&quot;:</p>
<pre class="literal-block">
$ cat server.key server.crt &gt; server.pem
</pre>
<p>The following are the minimum lighttpd.conf customizations required to
enable SCGI and set up a virtual host for http and https:</p>
<div class="highlight"><pre><span class="n">server</span><span class="o">.</span><span class="n">modules</span> <span class="o">=</span> <span class="p">(</span><span class="s">&quot;mod_access&quot;</span><span class="p">,</span>
                  <span class="s">&quot;mod_scgi&quot;</span><span class="p">,</span>
                  <span class="s">&quot;mod_accesslog&quot;</span><span class="p">)</span>

<span class="nv">$HTTP</span><span class="p">[</span><span class="s">&quot;host&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s">&quot;www.example.com&quot;</span> <span class="p">{</span>
    <span class="nv">$SERVER</span><span class="p">[</span><span class="s">&quot;socket&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s">&quot;0:443&quot;</span> <span class="p">{</span>
        <span class="n">ssl</span><span class="o">.</span><span class="n">engine</span> <span class="o">=</span> <span class="s">&quot;enable&quot;</span>
        <span class="n">ssl</span><span class="o">.</span><span class="n">pemfile</span> <span class="o">=</span> <span class="s">&quot;/my/ssl/server.pem&quot;</span>
        <span class="n">ssl</span><span class="o">.</span><span class="n">ca</span><span class="o">-</span><span class="n">file</span> <span class="o">=</span> <span class="s">&quot;/my/ssl/server.crt&quot;</span>
    <span class="p">}</span>
    <span class="n">scgi</span><span class="o">.</span><span class="n">server</span> <span class="o">=</span> <span class="p">(</span> <span class="s">&quot;&quot;</span> <span class="o">=&gt;</span>
        <span class="p">(</span> <span class="s">&quot;localhost&quot;</span> <span class="o">=&gt;</span>
            <span class="p">(</span> <span class="s">&quot;host&quot;</span> <span class="o">=&gt;</span> <span class="s">&quot;127.0.0.1&quot;</span><span class="p">,</span>
              <span class="s">&quot;port&quot;</span> <span class="o">=&gt;</span> <span class="mi">9000</span><span class="p">,</span>
              <span class="s">&quot;check-local&quot;</span> <span class="o">=&gt;</span> <span class="s">&quot;disable&quot;</span><span class="p">,</span>
              <span class="c1"># default disable-time is 60 seconds</span>
              <span class="s">&quot;disable-time&quot;</span> <span class="o">=&gt;</span> <span class="mi">5</span> <span class="c1"># seconds</span>
            <span class="p">)</span>
        <span class="p">)</span>
    <span class="p">)</span>
<span class="p">}</span>
</pre></div>
<div class="admonition-update admonition">
<p class="first admonition-title">Update</p>
<p>Wednesday November 12: After <a class="reference" href="http://eric.themoritzfamily.com/2008/11/12/wsgi-benchmarking/">noting a post by Eric Mortiz</a> I put together a set of results comparing both Apache and lighttpd proxy and SCGI front ends to a QP web / SCGI server. My results seem different than Eric's:</p>
<pre class="literal-block">
==================================================
QP web server direct    2.38 [ms] (mean)
lighttpd 1.4.20
lighttpd proxy to QP    2.41 [ms] (mean)
lighttpd SCGI           2.31 [ms] (mean)

APACHE 1.3.41
APACHE 1.3 proxy to QP  3.39 [ms] (mean)
APACHE 1.3 mod_scgi     2.30 [ms] (mean)
==================================================
</pre>
<p class="last">Details in the <a class="reference" href="/2008/10/22/qp-lighttpd-ssl-and-scgi/file/41b67fbe251d/">attached file</a></p>
</div>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:618</guid>
  <pubDate>Wed, 22 Oct 2008 18:31:06 GMT</pubDate>
</item>
<item>
  <title>QP and Durus on Nokia N800</title>
  <link>http://mikewatkins.ca/2007/11/27/nokia-n800-runs-qp/</link>
  <description><![CDATA[
<div class="document">
<img alt="/2007/11/27/nokia-n800-runs-qp/file/ce39f11e61fe/thumbnail?100" class="floatright" src="/2007/11/27/nokia-n800-runs-qp/file/ce39f11e61fe/thumbnail?100" />
<p>The attached image is a screen shot of a web browser running on a <a class="reference" href="http://europe.nokia.com/A4305062">Nokia N800</a>, a
Linux-based <em>internet tablet</em>. Weighing only ounces, the wireless device is a
great platform for Python developers as the language has more or less become
the default dynamic language for the device and, it would seem, for Nokia. GUI developers commonly employ pygtk/glade for <a class="reference" href="http://maemo.org/">Maemo</a> applications; I'm not aware of much web development being done on the N series tablets as yet.</p>
<p><a class="reference" href="/2007/11/27/nokia-n800-runs-qp/file/ce39f11e61fe/view">Pictured</a> is the output of a highly Pythonic web framework and object database combination, <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> and <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> - the app is merely a template provided by running <a class="reference" href="/software/files/qp/mkqpapp.py">mkqpapp.py</a>.</p>
<p>I wanted to see what QP and this little tablet, underpowered by
laptop or server standards, could do - here is a benchmark between a fairly fast Unix desktop, across a fairly slow wireless link, generating 10 concurrent request streams to the device</p>
<pre class="literal-block">
frog# /home/mw% siege -b -c10 -t10s http://n800:8000/
Transactions:                164 hits
Availability:             100.00 %
Elapsed time:               9.85 secs
Data transferred:           0.06 MB
Response time:              0.58 secs
Transaction rate:          16.65 trans/sec
Throughput:                 0.01 MB/sec
Concurrency:                9.71
Successful transactions:     164
Failed transactions:           0
Longest transaction:        3.66
Shortest transaction:       0.19
</pre>
<p>Not bad, considering its a full stack web framework and object database running on a little machine weighing only ounces that also is running what amounts to be a Gnome environment, browser, mail and other apps, all powered by a lowly TI 320MHz Armel architecture CPU.</p>
<p>Out of curiosity I added a hit counter to exercise the object database and the transaction rate was a respectable 12.36/second.</p>
<p>Worlds smallest portable web application demo machine!</p>
<p>Once I figure out how to make Debian packages for the armel architecture I'll post a deb link for a one click install of Durus, QP, QPY and Dulcinea.</p>
<p>Incidentally, while sqlite is in common use on Nokia tablets, there's clearly no reason why <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> could not be used. N-series Python developers might find that to be an ideal persistence pairing to go along with their web or GTK apps.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:489</guid>
  <pubDate>Tue, 27 Nov 2007 19:30:06 GMT</pubDate>
</item>
<item>
  <title>Django gets auto-escape</title>
  <link>http://mikewatkins.ca/2007/11/14/django-auto-escape/</link>
  <description><![CDATA[
<div class="document">
<p>According to <a class="reference" href="http://simonwillison.net/2007/Nov/14/changeset/">Simon Willison</a>, Django now has <a class="reference" href="http://code.djangoproject.com/changeset/6671">auto-escaping</a> of variable contents within templates. Good job.</p>
<p>I wouldn't even consider using a framework that didn't provide some sort of sane system for auto-quoting that which has not already been explicitly declared as safe. Both <a class="reference" href="http://www.mems-exchange.org/software/quixote/">Quixote</a> and <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> have had this feature for many years.</p>
<p>Quixote's PTL template approach gained this capability circa 2002; QP's <a class="reference" href="http://www.mems-exchange.org/software/qpy/">QPY</a>, which is a very close cousin to PTL but decoupled from the web framework itself, has always had this ability.</p>
<p>In October 2003 one of Quixote's original authors, <a class="reference" href="http://arctrix.com/nas/">Neil Schemenauer</a>, put forward <a class="reference" href="http://mail.mems-exchange.org/durusmail/quixote-users/811/">a proposal</a> that ultimately led to the implementation of a <em>sane</em> auto-quoting behaviour in PTL templates.  (See also his <a class="reference" href="http://arctrix.com/nas/talks/htmltext.pdf">PyCon 2003 talk</a>)</p>
<p>In the fall of 2005 <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> - a slightly more opinionated version of Quixote - was born of the same development shop, along with <a class="reference" href="http://www.mems-exchange.org/software/qpy/">QPY</a>, an unbundling of the PTL concept with string-like classes that make it easy to work with, virtually interchangeably, quote-aware and plain str / unicode objects.</p>
<p>QPY's smart string-like classes could be used by any templating system or web framework that hasn't solved this problem already.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:486</guid>
  <pubDate>Wed, 14 Nov 2007 23:42:01 GMT</pubDate>
</item>
<item>
  <title>Pybloxsom out, QP Journal in</title>
  <link>http://mikewatkins.ca/2007/10/12/pybloxsom-out-qp-journal-in/</link>
  <description><![CDATA[
<div class="document">
<p>In the summer I was writing a series of articles - a <em>web application diary</em>
of sorts - illustrating web development with Python and the <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> web framework.
After several thousand words and seven installments, the series paused <a class="reference" href="/tags/python/2007-06-20-10-18.html">back
in July</a>.</p>
<p>As I get my obligation stack beaten back to merely mountainous I plan on
continuing with the series and indeed have plans to re-factor the whole lot
and make it available in a subversion repository, step by step. The latter
idea is not mine; I ran across another such article series in the
<a class="reference" href="http://planet.python.org/">Pythonosphere</a> which had used subversion to good purpose.</p>
<p>To help encourage me to write more, I jumped ahead and put a slightly enhanced
version of the Journal application on-line as of the last couple of days; all
the old <a class="reference" href="http://pyblosxom.sourceforge.net/">Pyblosxom</a> hosted articles have been imported.</p>
<p>As contemplated back in June, I did decide to <a class="reference" href="/2007/06/09/avoid-or-embrace-breakage/">embrace breakage</a> - all the
old content has shiny new URL's, although a simple url_mapping dictionary and
a <a class="reference" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">301 Moved Permanently</a> redirect have more or less looked after avoiding
404 errors all over the place. Pretty easy to do, this is part of my
<tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> class:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">_q_traverse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    First check to see if request is hunting for an old URL,</span>
<span class="sd">    redirect if so.</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">uri</span> <span class="o">=</span> <span class="n">get_request</span><span class="p">()</span><span class="o">.</span><span class="n">get_path_query</span><span class="p">()</span>
    <span class="n">dest</span> <span class="o">=</span> <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">get_root</span><span class="p">()[</span><span class="s">&#39;url_mapping&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">dest</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">permanent</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">JournalDirectory</span><span class="o">.</span><span class="n">_q_traverse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
</pre></div>
<p>Incidentally my url_mapping has almost 1700 different combinations pointing to
478 content items and various query strings and other site features. Most of
the values in the mapping were created during the import of the old data;
apparently I was less than consistent in the way I pushed out <a class="reference" href="http://pyblosxom.sourceforge.net/">Pyblosxom</a> over
the years I used it. Now at least there is one URI for each resource and no
more.</p>
<p>Back soon with part eight of the series.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:479</guid>
  <pubDate>Fri, 12 Oct 2007 23:37:26 GMT</pubDate>
</item>
<item>
  <title>Python Database Interfaces</title>
  <link>http://mikewatkins.ca/2007/07/12/python-database-interfaces/</link>
  <description><![CDATA[
<div class="document">
<p><strong>Python object databases need some love too</strong></p>
<p>Flávio Coelho recently performed an examination of various Python database API
and ORM interfaces to MySQL, Postgres, and SQLite, and included a benchmark
for <a class="reference" href="http://docs.python.org/lib/module-cPickle.html">cPickle</a>.</p>
<p>Here's an addition to Flávio's  <a class="reference" href="http://pyinsci.blogspot.com/2007/07/fastest-python-database-interface.html">Fastest Python Database Interface</a> article and
script to include Durus: <a class="reference" href="http://64.21.147.49/durus/performance.py">performance.py</a>.</p>
<p>I also pointed out on Flávio's blog that his cPickle benchmark needed to
include pickling 100,000 &quot;Person&quot; classes, in addition to 100,000 simple
tuples - this to show the overhead of class instantiation and serialization /
deserialization which all of the ORM's and object databases share in some form
or another. An example of both can be found in <a class="reference" href="http://64.21.147.49/durus/performance.py">performance.py</a>.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:478</guid>
  <pubDate>Thu, 12 Jul 2007 16:47:00 GMT</pubDate>
</item>
<item>
  <title>Python Web Application Diary, Part Seven</title>
  <link>http://mikewatkins.ca/2007/06/20/python-web-application-diary-part-seven/</link>
  <description><![CDATA[
<div class="document">
<p><strong>User interface handling in QP</strong></p>
<p>In <a class="reference" href="/tags/python/2007-06-08-08-44.html">part six</a> of <em>Python Web Application Diary</em> we looked at data persistence
using <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> and wrote <a class="reference" href="http://www.mems-exchange.org/software/sancho/">Sancho</a> unit tests and a skeleton <a class="reference" href="http://pyblosxom.sourceforge.net/">Pyblosxom</a> migration
tool. Today we'll extend the basic UI created in <a class="reference" href="/tags/python/2007-06-06-11-51.html">part five</a> for our Entry
object, and we'll create a base UI for the Journal object too. At that point
we'll be able to wire our new <tt class="docutils literal"><span class="pre">JournalDirectory</span></tt> into <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> (also
referenced in <a class="reference" href="/tags/python/2007-06-06-11-51.html">part five</a>) - a QP application's &quot;master controller&quot; - which
will allow us to start publishing previously created journal entries to the
web.</p>
<div class="section">
<h2><a id="journaldirectory" name="journaldirectory">JournalDirectory</a></h2>
<p>I find it useful to map out the URL design for a set of related objects and
then start to work on the UI. In <a class="reference" href="/tags/python/2007-06-06-11-51.html">part five</a> we did this for <tt class="docutils literal"><span class="pre">EntryUI</span></tt>.
Lets get to work on the UI 'container' for our Entries, <tt class="docutils literal"><span class="pre">JournalDirectory</span></tt>.
In this first cut we'll implement enough to display a list of recent entries
and put stubs in place for Atom and RSS feeds.</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">JournalDirectory</span><span class="p">(</span><span class="n">Directory</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    This class provides the functionality to display recent entries,</span>
<span class="sd">    to navigate to a specific entry, and to present RSS and Atom feeds.</span>

<span class="sd">    Our application urls for this UI component will be:</span>

<span class="sd">    ../             &quot;index&quot; or recent entries</span>
<span class="sd">    ../1234/        calls the EntryUI &quot;index&quot; method</span>
<span class="sd">    ../new          create a new Entry</span>
<span class="sd">    ../index.rss</span>
<span class="sd">    ../index.xml</span>
<span class="sd">    &quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">get_exports</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;index.rss&#39;</span><span class="p">,</span> <span class="s">&#39;rss&#39;</span><span class="p">,</span> <span class="s">&#39;RSS&#39;</span><span class="p">,</span> <span class="s">&#39;RSS feed for this journal&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;index.xml&#39;</span><span class="p">,</span> <span class="s">&#39;atom&#39;</span><span class="p">,</span> <span class="s">&#39;Atom&#39;</span><span class="p">,</span> <span class="s">&#39;Atom feed for this journal&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">journal</span><span class="p">):</span>
        <span class="n">require</span><span class="p">(</span><span class="n">journal</span><span class="p">,</span> <span class="n">Journal</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">journal</span> <span class="o">=</span> <span class="n">journal</span>

    <span class="k">def</span> <span class="nf">index</span> <span class="p">[</span><span class="n">html</span><span class="p">]</span> <span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">title</span> <span class="o">=</span> <span class="s">&quot;</span><span class="si">%s</span><span class="s">&#39;s journal&quot;</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">journal</span><span class="o">.</span><span class="n">get_name</span><span class="p">()</span>
        <span class="n">header</span><span class="p">(</span><span class="n">title</span><span class="p">)</span>
        <span class="s">&#39;&lt;p&gt;This is the journal of &#39;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">journal</span><span class="o">.</span><span class="n">get_name</span><span class="p">()</span>
        <span class="s">&#39;&lt;/p&gt;&#39;</span>
        <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">journal</span><span class="o">.</span><span class="n">get_recent_entries</span><span class="p">(</span><span class="n">count</span><span class="o">=</span><span class="mi">10</span><span class="p">):</span>
            <span class="s">&#39;&lt;p&gt;</span><span class="si">%s</span><span class="s"> </span><span class="si">%s</span><span class="s"> </span><span class="si">%s</span><span class="s">&lt;/p&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="n">e</span><span class="o">.</span><span class="n">get_text</span><span class="p">()</span><span class="o">.</span><span class="n">get_format</span><span class="p">(),</span>
                                 <span class="n">href</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">key</span><span class="p">),</span><span class="n">e</span><span class="o">.</span><span class="n">get_title</span><span class="p">()))</span>
        <span class="n">footer</span><span class="p">(</span><span class="n">title</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">new</span> <span class="p">[</span><span class="n">html</span><span class="p">]</span> <span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="s">&#39; not implemented &#39;</span>

    <span class="n">atom</span> <span class="o">=</span> <span class="n">rss</span> <span class="o">=</span> <span class="n">new</span>
</pre></div>
<p>Looking at the code above it you can see that we've provided no way of
navigating to individual entries. In <tt class="docutils literal"><span class="pre">index</span></tt> we've generated links which
look like this:</p>
<pre class="literal-block">
&lt;a href=&quot;./1234&quot;&gt;Some title&lt;/a&gt;
</pre>
<p>What we've not done is provide a way of resolving that part of the URL path.
Lets say for example we have a site:</p>
<pre class="literal-block">
http://mikewatkins.ca/blog
</pre>
<p>With entries:</p>
<pre class="literal-block">
http://mikewatkins.ca/blog/1234
http://mikewatkins.ca/blog/3456
</pre>
<p>How does the QP application resolve these paths?</p>
</div>
<div class="section">
<h2><a id="q-lookup" name="q-lookup">_q_lookup</a></h2>
<p>Unlike some web application frameworks which use regular expressions to map
URL namespaces to objects/methods or functions, QP uses the concept of object
publishing via object traversal. We've seen how a UI object that exposes
methods can be called; <tt class="docutils literal"><span class="pre">_q_lookup</span></tt> provides us the ability to return
&quot;objects&quot;. Lets write a <tt class="docutils literal"><span class="pre">_q_lookup</span></tt> method for <tt class="docutils literal"><span class="pre">JournalDirectory</span></tt> that
returns an <tt class="docutils literal"><span class="pre">EntryUI</span></tt> object for a specific journal <tt class="docutils literal"><span class="pre">Entry</span></tt>.</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">_q_lookup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">component</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">key</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">component</span><span class="p">)</span>
    <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">not_found</span><span class="p">(</span><span class="s">&#39;</span><span class="si">%r</span><span class="s"> is not a valid journal entry identifier.&#39;</span> <span class="o">%</span> <span class="n">component</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">entry</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">journal</span><span class="o">.</span><span class="n">get_entry</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
    <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">not_found</span><span class="p">(</span><span class="s">&#39;</span><span class="si">%r</span><span class="s"> was not found.&#39;</span> <span class="o">%</span> <span class="n">key</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">EntryUI</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>
</pre></div>
<p>At this point we have working <tt class="docutils literal"><span class="pre">JournalDirectory</span></tt> and <tt class="docutils literal"><span class="pre">EntryUI</span></tt> objects.
Lets now replace our temporary demonstration code in the applications master
controller with a connection to our <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> database and real data.</p>
<p>As you might imagine, with this object / UI pattern it would be easy to add
support for multiple journals. Objects and their corresponding UI might look
like:</p>
<pre class="literal-block">
Object              UI
--------------------------------------------
Entry               EntryUI
Journal             JournalDirectory
JournalDatabase     JournalDatabaseDirectory
</pre>
</div>
<div class="section">
<h2><a id="wiring-up-the-application" name="wiring-up-the-application">Wiring up the application</a></h2>
<p>For simplicities sake, we are going to first wire up our <tt class="docutils literal"><span class="pre">JournalDirectory</span></tt>
to <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> which we last talked about in conjunction with the site's
primary &quot;driver&quot;, <tt class="docutils literal"><span class="pre">slash.qpy</span></tt>, last discussed in <a class="reference" href="/tags/python/2007-06-06-11-51.html">part five</a>.</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">SiteDirectory</span><span class="p">(</span><span class="n">Directory</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">get_exports</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="s">&#39;Home&#39;</span><span class="p">,</span> <span class="s">&#39;The Home page of this site.&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;blog&#39;</span><span class="p">,</span> <span class="s">&#39;blog&#39;</span><span class="p">,</span> <span class="s">&#39;Weblog&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">index</span> <span class="p">[</span><span class="n">html</span><span class="p">]</span> <span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">title</span> <span class="o">=</span> <span class="n">get_site</span><span class="p">()</span><span class="o">.</span><span class="n">get_name</span><span class="p">()</span>
        <span class="n">header</span><span class="p">(</span><span class="n">title</span><span class="p">)</span>
        <span class="s">&#39;&lt;p&gt;&lt;strong&gt;It worked!&lt;/strong&gt;&lt;/p&gt;&#39;</span>
        <span class="s">&#39;&lt;p&gt;The &lt;strong&gt;</span><span class="si">%s</span><span class="s">&lt;/strong&gt; application lives at &lt;br /&gt;&lt;tt&gt;&#39;</span> <span class="o">%</span> <span class="n">title</span>
        <span class="n">get_site</span><span class="p">()</span><span class="o">.</span><span class="n">get_package_directory</span><span class="p">()</span>
        <span class="s">&#39;&lt;/tt&gt;&lt;/p&gt;&#39;</span>
        <span class="n">footer</span><span class="p">()</span>

    <span class="n">mw_journal</span> <span class="o">=</span> <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">get_root</span><span class="p">()[</span><span class="s">&#39;mw_journal&#39;</span><span class="p">]</span>
    <span class="n">blog</span> <span class="o">=</span> <span class="n">JournalDirectory</span><span class="p">(</span><span class="n">mw_journal</span><span class="p">)</span>
</pre></div>
<p>Or, we could wire up <tt class="docutils literal"><span class="pre">JournalDirectory</span></tt> as the 'root' of our application
like this:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">SiteDirectory</span><span class="p">(</span><span class="n">JournalDirectory</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c"># I&#39;ve implemented the JournalDatabase object and migrated some</span>
        <span class="c"># data into a Journal.</span>
        <span class="n">mw_journal</span> <span class="o">=</span> <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">get_root</span><span class="p">()[</span><span class="s">&#39;journals&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;mw&#39;</span><span class="p">)</span>
        <span class="n">JournalDirectory</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mw_journal</span><span class="p">)</span>
</pre></div>
<p>Going with the latter example, at this point we can restart the qp application
(<tt class="docutils literal"><span class="pre">qp</span> <span class="pre">blog</span> <span class="pre">restart</span></tt>) and visit <a class="reference" href="http://localhost:8011/">http://localhost:8011/</a> and we should see a
list of available posts displayed which we can navigate to. Clearly we need to
do a bunch more work - implement a site look and feel; convert RST, Markdown,
and Textile formatted posts into HTML, implement RSS and Atom feeds, but the
basics of a journal or weblog application have come together. Fleshing out the
display of existing data will be our next step.</p>
<p>We still can't create, edit, or delete journal entries - we'll discuss that in
a soon to be forthcoming installment in this series.</p>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:477</guid>
  <pubDate>Wed, 20 Jun 2007 17:18:00 GMT</pubDate>
</item>
<item>
  <title>Avoid or Embrace Breakage</title>
  <link>http://mikewatkins.ca/2007/06/09/avoid-or-embrace-breakage/</link>
  <description><![CDATA[
<div class="document">
<p>In <a class="reference" href="/tags/python/2007-06-08-08-44.html">part six</a> of <em>Python Web Application Diary</em> it was noted that soon we'll
have a functioning, if basic, journal application. Since I plan to follow the
<em>release early release often</em> plan for this particular application -- otherwise
a desire for perfectionism will never see the project complete -- now is good
time to think about areas of <em>breakage</em> that may occur.</p>
<p>The number one and two issues that come to mind are:</p>
<ol class="arabic simple">
<li><strong>URL breakage</strong>: Will the &gt; 470 journal entries moving over from
<a class="reference" href="http://pyblosxom.sourceforge.net/">Pyblosxom</a> to the new application lose their existing URLs? I really
dislike the Pyblosxom URL scheme, and will probably vote to go with
breaking it. I'm a bad person. It won't happen again. Promise.</li>
<li><strong>Feed breakage</strong>: New URL's and possibly new entry id's tend to do nasty
things to feeds, or rather, to the consumers of feeds. I'm tempted to
discontinue RSS support while I'm going through this process but need to
review my logs to see what the impact of that decision would be.</li>
</ol>
<p>In fact feed breakage is my larger concern - the last thing I want to do is
subject the official or unofficial Python community feed aggregators and their
readers to dozens of old and now mostly irrelevant posts.</p>
<p>Ironically that same issue is why I've never asked to be included in either
feed, at least not until I moved the data off to another platform.</p>
<p>For now, until I flip the switch, I've disabled the entry / item content in my
existing Atom and RSS feeds by creating empty Pyblosxom <em>flavour</em> files for
story.xml and story.rss -- this will result in no blog entries being published
via either feed. I've also added to the description element in both feeds a
notice:</p>
<pre class="literal-block">
&lt;description&gt;$blog_description
:: Feed off-line while upgrades being done
&lt;/description&gt;
</pre>
<p>Coming up in part seven of the series, accomplishing enough to flip the
switch.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:473</guid>
  <pubDate>Sat, 09 Jun 2007 16:37:00 GMT</pubDate>
</item>
<item>
  <title>Python Web Application Diary, Part Six</title>
  <link>http://mikewatkins.ca/2007/06/08/python-web-application-diary-part-six/</link>
  <description><![CDATA[
<div class="document">
<p>In <a class="reference" href="/tags/python/2007-06-06-11-51.html">part five</a> of this series we dove deep into <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> and looked at the
fundamentals of any QP application - <tt class="docutils literal"><span class="pre">SitePublisher</span></tt> and <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> -
as well as explored the use of <a class="reference" href="http://www.mems-exchange.org/software/qpy/">QPY</a> templating. We also built a rudimentary UI
for our <tt class="docutils literal"><span class="pre">Entry</span></tt> object.</p>
<p>In this installment of our web application diary we'll work more with the
<a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> object database by injecting some data into it; exploring the
interactive interpreter (one of the cool features of Durus to be sure) and
starting the basis for a conversion script to take weblog data in <a class="reference" href="http://pyblosxom.sourceforge.net/">PyBlosxom</a>
format and insert it into our <tt class="docutils literal"><span class="pre">blog</span></tt> application database.</p>
<div class="tip">
<p class="first admonition-title">Tip</p>
<p>Before going further, install <a class="reference" href="http://codespeak.net/pyrepl/">Pyrepl</a> - this is required to support
QP / Durus interactive interpreter features, and adds significant
functionality (optional) to Python's own interactive interpreter.</p>
<p>To see <a class="reference" href="http://codespeak.net/pyrepl/">Pyrepl</a> at work with regular Python launch:</p>
<pre class="last literal-block">
pythoni
</pre>
</div>
<div class="section">
<h2><a id="durus-the-database-you-already-know" name="durus-the-database-you-already-know">Durus, the database you already know</a></h2>
<p>Now I know what you are thinking. I think. Well, that is my theory and it is
mine and I own that theory. My theory is that you are thinking:</p>
<blockquote>
&quot;Object database? what sort of weird and strange alchemy is that? Fear
the unknown! Down with the unknown! Destroy the unknown with DELETE FROM
queries!&quot; -- you</blockquote>
<p>While object databases are not exactly in commonplace use by the IT industry,
within the <a class="reference" href="http://python.org/">Python</a> community, there is a long history of kinship with object
databases with <a class="reference" href="http://www.python.org/pypi/ZODB3">ZODB</a>, the Zope Object Database, arguably being the most well
known example.</p>
<p><a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> is patterned after ZODB, and indeed was written by developers who had
used ZODB extensively. Visit the <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> pages for more information on their
rationale for reinventing this particular wheel; from my own experience I can
only say that Durus is small and easy to read and understand.</p>
<p><strong>What exactly is an object database?</strong> Put simply, Durus and ZODB allow you
to persist your Python objects. Its more than <tt class="docutils literal"><span class="pre">pickle</span></tt> but not unlike pickle
in some respects.</p>
<div class="tip">
<p class="first admonition-title">Tip</p>
<p class="last">Launch a log viewer in another terminal window so you can watch
what happens as we make changes to the Durus database.
qp -l blog</p>
</div>
<div class="section">
<h3><a id="demonstrating-durus-interactively" name="demonstrating-durus-interactively">Demonstrating Durus, Interactively</a></h3>
<p>QP and Durus provide the facility to work directly with the Durus object
database directly. Lets fire up an interactive session to show Durus basics.</p>
<div class="highlight"><pre><span class="o">%</span> <span class="n">qp</span> <span class="o">-</span><span class="n">i</span> <span class="n">blog</span>
<span class="n">Profile</span><span class="p">,</span> <span class="n">connection</span><span class="p">,</span> <span class="n">publisher</span><span class="p">,</span> <span class="n">root</span><span class="p">,</span> <span class="n">sessions</span><span class="p">,</span> <span class="n">site</span><span class="p">,</span> <span class="n">users</span>
<span class="o">-&gt;&gt;</span>
</pre></div>
<p><strong>Working within the interactive session</strong>: <a class="reference" href="http://codespeak.net/pyrepl/">Pyrepl</a> provides very useful
search and command history capabilities. Control-P and Control-N step through
previous lines entered. Control-R starts up reverse history search - start
typing an entry you've made previously (searches substrings within) and
Control-R again to step through the hits, if any.</p>
<p>Term expansion is perhaps my favorite <a class="reference" href="http://codespeak.net/pyrepl/">Pyrepl</a> enhancement - it certainly is
the one that gets used enough. Try it now by entering in a couple letters:</p>
<pre class="literal-block">
-&gt;&gt; pu
</pre>
<p>And press <strong>Tab</strong> - you'll be rewarded with either <tt class="docutils literal"><span class="pre">publisher</span></tt> or a list of
terms in the namespace which match the letters entered so far. A <em>real</em>
timesaver.</p>
<p><strong>Access to objects</strong>: The interactive session provides us with access to QP
objects (connection, site, publisher), application objects (sessions, users),
a Profile testing class, but the most relevant to our discussion right now is
<tt class="docutils literal"><span class="pre">root</span></tt>.</p>
<p>By convention our application data lives under <tt class="docutils literal"><span class="pre">root</span></tt>, which is itself a
persistent object. Changes to <tt class="docutils literal"><span class="pre">root</span></tt> will persist from session to session
provided a call to <tt class="docutils literal"><span class="pre">connection.commit()</span></tt> has been made to commit the changes
to the database. Lets do some simple examples.</p>
<div class="highlight"><pre><span class="o">-&gt;&gt;</span> <span class="k">from</span> <span class="nn">durus.persistent_dict</span> <span class="k">import</span> <span class="n">PersistentDict</span>
<span class="o">-&gt;&gt;</span> <span class="n">mydict</span> <span class="o">=</span> <span class="n">PersistentDict</span><span class="p">()</span>
<span class="o">-&gt;&gt;</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;test&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">mydict</span>
<span class="o">-&gt;&gt;</span> <span class="n">connection</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="o">-&gt;&gt;</span>
<span class="o">%</span>
</pre></div>
<p>Control-D exits the interactive session, as it also exits a standard Python
interpreter. Restart the interpreter to see if our object was 'saved' or
persisted.</p>
<div class="highlight"><pre><span class="o">%</span> <span class="n">qp</span> <span class="o">-</span><span class="n">i</span> <span class="n">blog</span>
<span class="n">Profile</span><span class="p">,</span> <span class="n">connection</span><span class="p">,</span> <span class="n">publisher</span><span class="p">,</span> <span class="n">root</span><span class="p">,</span> <span class="n">sessions</span><span class="p">,</span> <span class="n">site</span><span class="p">,</span> <span class="n">test</span><span class="p">,</span> <span class="n">users</span>
<span class="o">-&gt;&gt;</span>
</pre></div>
<p>Very good, <tt class="docutils literal"><span class="pre">test</span></tt>, now shows up in our display -- objects living at the
<tt class="docutils literal"><span class="pre">root</span></tt> level are conveniently displayed as a reminder when we fire up an
interactive session. Lets put some data in test, but first, what was test?</p>
<div class="highlight"><pre><span class="o">-&gt;&gt;</span> <span class="n">test</span>
<span class="o">&lt;</span><span class="n">PersistentDict</span> <span class="mi">17020</span><span class="o">&gt;</span>
<span class="o">-&gt;&gt;</span> <span class="n">test</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
<span class="p">[]</span>
</pre></div>
<p>Right, now I remember. Ok, add some data.</p>
<div class="highlight"><pre><span class="o">-&gt;&gt;</span> <span class="n">test</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;My first persistent data&#39;</span>
<span class="o">-&gt;&gt;</span> <span class="n">connection</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="o">-&gt;&gt;</span>
</pre></div>
<p>Control-D to quit, and restart again to satisfy any fears that you may have
about your important data.</p>
<div class="highlight"><pre><span class="o">%</span> <span class="n">qp</span> <span class="o">-</span><span class="n">i</span> <span class="n">blog</span>
<span class="n">Profile</span><span class="p">,</span> <span class="n">connection</span><span class="p">,</span> <span class="n">journals</span><span class="p">,</span> <span class="n">publisher</span><span class="p">,</span> <span class="n">root</span><span class="p">,</span> <span class="n">sessions</span><span class="p">,</span> <span class="n">site</span><span class="p">,</span> <span class="n">test</span><span class="p">,</span> <span class="n">users</span>
<span class="o">-&gt;&gt;</span> <span class="n">test</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
<span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s">&#39;My first persistent data&#39;</span><span class="p">)]</span>
<span class="o">-&gt;&gt;</span>
</pre></div>
<p>By now you can see that what we are doing is using Python to manage our data,
and, by virtue of subclassing one of <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> persistent object classes, we can
make our Python objects full partners in the Durus object database.</p>
<p>Durus is the database you already know. No object relational mappers to learn,
no SQL to learn or work around.</p>
</div>
</div>
<div class="section">
<h2><a id="durus-mini-faq" name="durus-mini-faq">Durus Mini FAQ</a></h2>
<dl class="docutils">
<dt>What about performance?</dt>
<dd>This is too difficult a question to answer simply, but its been my
experience that I  have been able to use Durus, instead of a SQL database
(Postgres is my personal favorite among the open source databases), far
more often than not. You won't put an on-line banking system processing
millions of transactions a day on to Durus or ZODB; but you might base on
Durus a complex company inventory system, even if there are hundreds of
thousands of items and related history. Third party solutions marry Durus
with relational databases as a back-end to Durus (transparent to the
application) to extend Durus (ZODB has similar approaches I'm told) even
further.</dd>
<dt>What about SQL / queries? How will I ever live?</dt>
<dd>One of the challenging things for a SQL-oriented developer (that was me,
some time ago) is to start thinking in pure-Python again. Its not hard,
but it does take some realignment of thought before it comes naturally -
at least for me. Being able to dispense with relational thinking in the
SQL sense brings a lot of design freedom.</dd>
<dt>What about sharing data with other systems?</dt>
<dd>My approach has been to export data as CSV or DIF for import into other
systems SQL databases, or to provide APIs such as XML-RPC or REST / JSON
approaches for other applications themselves, or to use RSS or Atom feeds
when it makes sense.</dd>
</dl>
<p><strong>The bottom line</strong>: Durus objects are Python objects. You've already invested
in learning and knowing Python, so you already know Durus, so there is no
time-to-learn downside to spending some time with Durus now. Lets press on.</p>
</div>
<div class="section">
<h2><a id="entries-with-no-home" name="entries-with-no-home">Entries with no home</a></h2>
<p>In <a class="reference" href="/tags/python/2007-06-04-11-33.html">part three</a> of this series we turned a simple <tt class="docutils literal"><span class="pre">Entry</span></tt> object
into a full partner of a Durus database merely by subclassing
<tt class="docutils literal"><span class="pre">PersistentObject</span></tt> instead of the standard Python new-style class
<tt class="docutils literal"><span class="pre">object</span></tt>. In <a class="reference" href="/tags/python/2007-06-04-14-18.html">part four</a> we kicked things up a notch by fleshing out our
<tt class="docutils literal"><span class="pre">Entry</span></tt> object with <em>specifications</em> provided by the <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> module
<tt class="docutils literal"><span class="pre">qp.lib.spec</span></tt>.</p>
<p>What we have not done, yet, is provide a place for our journal entries to
'live'. We need a container for <tt class="docutils literal"><span class="pre">Entry</span></tt>, and early on we decided to call
that container <tt class="docutils literal"><span class="pre">Journal</span></tt>. We are really going to kick things up a notch by
levering off of functionality provided by <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> in <tt class="docutils literal"><span class="pre">qp.lib.keep</span></tt>. A <tt class="docutils literal"><span class="pre">Keep</span></tt>
is a mapping of <tt class="docutils literal"><span class="pre">Keyed</span></tt> items using an integer as a key. Lets enhance
<tt class="docutils literal"><span class="pre">Entry</span></tt> first, then we'll write some unit tests for <tt class="docutils literal"><span class="pre">Journal</span></tt>, and then
write <tt class="docutils literal"><span class="pre">Journal</span></tt> itself.</p>
<p>All the code for the end-result objects will be available at the conclusion of
this series, but for you folks following along at home, lets dive in and
re-edit our <tt class="docutils literal"><span class="pre">journal.py</span></tt> and clean up our <tt class="docutils literal"><span class="pre">Entry</span></tt> object first. For
brevity's sake I have included imports relevant to both <tt class="docutils literal"><span class="pre">Entry</span></tt> and the
<tt class="docutils literal"><span class="pre">Journal</span></tt> object we will be writing.</p>
<div class="highlight"><pre><span class="k">from</span> <span class="nn">dulcinea.base</span> <span class="k">import</span> <span class="n">DulcineaPersistent</span>
<span class="k">from</span> <span class="nn">dulcinea.sort</span> <span class="k">import</span> <span class="n">attr_sort</span>
<span class="k">from</span> <span class="nn">qp.lib.keep</span> <span class="k">import</span> <span class="n">Keep</span><span class="p">,</span> <span class="n">Keyed</span><span class="p">,</span> <span class="n">Stamped</span>
<span class="k">from</span> <span class="nn">qp.lib.spec</span> <span class="k">import</span> <span class="n">add_getters_and_setters</span><span class="p">,</span> <span class="n">boolean</span><span class="p">,</span> <span class="n">both</span><span class="p">,</span> <span class="n">datetime_with_tz</span>
<span class="k">from</span> <span class="nn">qp.lib.spec</span> <span class="k">import</span> <span class="n">init</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">string</span><span class="p">,</span> <span class="n">spec</span>
<span class="k">from</span> <span class="nn">qp.pub.user</span> <span class="k">import</span> <span class="n">User</span>


<span class="k">class</span> <span class="nc">Entry</span><span class="p">(</span><span class="n">DulcineaPersistent</span><span class="p">,</span> <span class="n">Keyed</span><span class="p">,</span> <span class="n">Stamped</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    An entry in a journal.</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">title_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
        <span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="bp">None</span><span class="p">),</span>
        <span class="s">&quot;A string briefly describing the Entry&quot;</span><span class="p">)</span>
    <span class="n">text_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
        <span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="bp">None</span><span class="p">),</span>
        <span class="s">&quot;The entry conten&quot;</span><span class="p">)</span>
    <span class="n">published_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
        <span class="n">boolean</span><span class="p">,</span>
        <span class="s">&quot;Boolean indicating if Entry can be published&quot;</span><span class="p">)</span>
    <span class="n">author_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
        <span class="n">User</span><span class="p">,</span>
        <span class="s">&quot;User responsible for creating entry&quot;</span><span class="p">)</span>
    <span class="n">created_is</span> <span class="o">=</span> <span class="n">datetime_with_tz</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">author</span><span class="p">):</span>
        <span class="n">Keyed</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
        <span class="n">Stamped</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
        <span class="n">init</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">author</span><span class="o">=</span><span class="n">author</span><span class="p">,</span> <span class="n">created</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">stamp</span><span class="p">,</span> <span class="n">published</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>

<span class="n">add_getters_and_setters</span><span class="p">(</span><span class="n">Entry</span><span class="p">)</span>
</pre></div>
<p>Lets now write <tt class="docutils literal"><span class="pre">Journal</span></tt> but before we write it, lets write the tests we
want it to pass, <strong>first</strong>, and then write the object. Typically you might
write only some of these tests, at least until you become familiar with the
various features of the <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> and <a class="reference" href="http://www.mems-exchange.org/software/dulcinea/">Dulcinea</a> libraries. In our
<tt class="docutils literal"><span class="pre">./test/utest_journal.py</span></tt> we'll add another test.</p>
<div class="highlight"><pre><span class="k">from</span> <span class="nn">parlez.journal</span> <span class="k">import</span> <span class="n">Journal</span>

<span class="k">class</span> <span class="nc">JournalTest</span><span class="p">(</span><span class="n">UTest</span><span class="p">):</span>
    <span class="c"># we&#39;ll write this first, and then write Journal</span>

    <span class="k">def</span> <span class="nf">_pre</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c"># set up a journal which we&#39;ll use for most tests.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">j</span> <span class="o">=</span> <span class="n">Journal</span><span class="p">(</span><span class="s">&#39;science&#39;</span><span class="p">,</span> <span class="n">User</span><span class="p">(</span><span class="s">&#39;einstein&#39;</span><span class="p">))</span>
        <span class="c"># it is automatically taken down following each individual test</span>

    <span class="k">def</span> <span class="nf">init_test</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c"># we want Journal to have a URL name and an owner, so force it</span>
        <span class="n">Journal</span><span class="p">(</span><span class="s">&#39;musings&#39;</span><span class="p">,</span> <span class="n">User</span><span class="p">(</span><span class="s">&#39;joe&#39;</span><span class="p">))</span>

    <span class="k">def</span> <span class="nf">create_entry_test</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">create_entry</span><span class="p">(),</span> <span class="n">Entry</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">add_test</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">e</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">create_entry</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">e</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_all_entries</span><span class="p">()</span>
        <span class="k">assert</span> <span class="n">e</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_entry</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">only_published_test</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c"># nothing in</span>
        <span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_all_entries</span><span class="p">()</span> <span class="o">==</span> <span class="p">[]</span>
        <span class="n">e</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">create_entry</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
        <span class="n">e_published</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">create_entry</span><span class="p">()</span>
        <span class="n">e_published</span><span class="o">.</span><span class="n">set_published</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">e_published</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">e</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_entries</span><span class="p">()</span>
        <span class="k">assert</span> <span class="n">e_published</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_entries</span><span class="p">()</span>
        <span class="c"># publish e now</span>
        <span class="n">e</span><span class="o">.</span><span class="n">set_published</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">e</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_entries</span><span class="p">()</span>
        <span class="c"># both should be in reverse sorted result, e last</span>
        <span class="k">assert</span> <span class="p">[</span><span class="n">e_published</span><span class="p">,</span> <span class="n">e</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">j</span><span class="o">.</span><span class="n">get_recent_entries</span><span class="p">()</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">EntryTest</span><span class="p">()</span>
    <span class="n">JournalTest</span><span class="p">()</span>
</pre></div>
<p>I've kept this briefer than I'd like it to be, as there are some other tests
we need to write to completely cover our Journal object, but these tests
of primary functionality - add, retrieve, retrieve all and sort - should give
you the spirit of what we are trying to achieve here.</p>
</div>
<div class="section">
<h2><a id="pyblosxom-to-journal-conversion" name="pyblosxom-to-journal-conversion">PyBlosxom to Journal Conversion</a></h2>
<p>A common challenge: you've got data in one system and need to move it into a
Durus database. A script to perform this task will be included in full at the
end of this series. For now lets sketch out what we need to do, and look at
how to access an application's <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> database from a script.</p>
<p>Pyblosxom maintains its files in a hierarchy that looks like something like
this:</p>
<pre class="literal-block">
../entries/categoryname/file1.txt
../entries/categoryname/someotherfile.rst
../entries/python/2007-06-08-08-44.rst
</pre>
<p>And so on. My particular installation uses a plugin which parses the entry
date from the file name if it is formatted as a datetime in the form of
yyyy-mm-dd-hh-mm.ext, so for files formatted like that I can set Entry.created
to a datetime parsed from the filename. Otherwise, I need to <tt class="docutils literal"><span class="pre">stat</span></tt> the file
and get its creation date from the operating system, which isn't always
reliable (in the case of edits and hapless administrators).</p>
<p>The file contents are simple for me to parse - content is either plain text,
or in my instance, mostly <a class="reference" href="http://www.python.org/pypi/textile/">Textile</a> formatted with a sprinkling of <a class="reference" href="http://docutils.sourceforge.net/rst.html">reST</a> and
<a class="reference" href="http://www.freewisdom.org/projects/python-markdown/">Markdown</a>.:</p>
<pre class="literal-block">
Some article title
#author Mike Watkins
The article content.

.h2 A subtitle

More content. Etc.
</pre>
<p>I never used the #author directive; some files use the #parser directive to
indicate which formatter should be used; most rely on file extensions (.rst,
.txt, .mkd).</p>
<p>Ultimately my script needs to deliver to me:</p>
<ul class="simple">
<li>Entry date</li>
<li>Format</li>
<li>Title</li>
<li>Content</li>
</ul>
<p>And, if I intend to preserve the URLs (am debating this now... I really
dislike the existing bloxsom / Pyblosxom URL design) I'll need to carry that
information forward too. For now, lets assume we have a mapping containing
file paths as keys and a list with the four above noted data elements to work
with, and write a script to import that information into Durus.</p>
<div class="section">
<h3><a id="importing-data-to-durus" name="importing-data-to-durus">Importing data to Durus</a></h3>
<p>Working with a QP application's Durus database is easy - remember, its just
Python.</p>
<div class="highlight"><pre><span class="k">from</span> <span class="nn">qp.lib.site</span> <span class="k">import</span> <span class="n">Site</span>
<span class="k">from</span> <span class="nn">parlez.journal</span> <span class="k">import</span> <span class="n">Entry</span><span class="p">,</span> <span class="n">Journal</span>

<span class="k">def</span> <span class="nf">bloxsom_to_mapping</span><span class="p">(</span><span class="n">entrypath</span><span class="p">):</span>
    <span class="c"># here you&#39;ll deal with the specifics - see a future article</span>
    <span class="n">data</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="c"># ...</span>
    <span class="k">return</span> <span class="n">data</span>

<span class="k">def</span> <span class="nf">add_journal_entries</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">journal</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">path</span><span class="p">,</span> <span class="n">entry_data</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="c"># path I might store, or some component of it, in the Entry</span>
        <span class="c"># object to facilitate mapping old to new URLs in the future.</span>
        <span class="c"># for now, just ignoring it</span>
        <span class="n">created</span><span class="p">,</span> <span class="n">format</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">content</span> <span class="o">=</span> <span class="n">entry_data</span>
        <span class="n">entry</span> <span class="o">=</span> <span class="n">journal</span><span class="o">.</span><span class="n">create_entry</span><span class="p">()</span>
        <span class="n">entry</span><span class="o">.</span><span class="n">set_format</span><span class="p">(</span><span class="n">format</span><span class="p">)</span>
        <span class="n">entry</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="n">title</span><span class="p">)</span>
        <span class="n">entry</span><span class="o">.</span><span class="n">set_text</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
        <span class="c"># normally we don&#39;t bypass getters/setters</span>
        <span class="n">entry</span><span class="o">.</span><span class="n">created</span> <span class="o">=</span> <span class="n">created</span>
        <span class="n">entry</span><span class="o">.</span><span class="n">stamp</span> <span class="o">=</span> <span class="n">created</span>
        <span class="n">journal</span><span class="o">.</span><span class="n">add_entry</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">BLOXSOM_ENTRY_PATH</span> <span class="o">=</span> <span class="s">&#39;/home/mw/bloxsom/entries&#39;</span>
    <span class="n">APP_NAME</span> <span class="o">=</span> <span class="s">&#39;blog&#39;</span>
    <span class="n">JOURNAL_NAME</span> <span class="o">=</span> <span class="s">&#39;mw&#39;</span>
    <span class="n">USER_ID</span> <span class="o">=</span> <span class="s">&#39;mw&#39;</span>

    <span class="c"># the Site object gives us the ability to access</span>
    <span class="c"># configuration information and live objects</span>
    <span class="n">site</span> <span class="o">=</span> <span class="n">Site</span><span class="p">(</span><span class="n">APP_NAME</span><span class="p">)</span>
    <span class="n">pub</span> <span class="o">=</span> <span class="n">site</span><span class="o">.</span><span class="n">get_publisher</span><span class="p">()</span>
    <span class="n">root</span> <span class="o">=</span> <span class="n">pub</span><span class="o">.</span><span class="n">get_root</span><span class="p">()</span>
    <span class="n">users</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;users&#39;</span><span class="p">]</span>
    <span class="c"># make sure I exist in Users</span>
    <span class="k">if</span> <span class="n">USER_ID</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">users</span><span class="p">:</span>
       <span class="n">user</span> <span class="o">=</span> <span class="n">pub</span><span class="o">.</span><span class="n">create_user</span><span class="p">(</span><span class="n">USER_ID</span><span class="p">)</span>
       <span class="n">users</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="k">if</span> <span class="s">&#39;journal&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">root</span><span class="p">:</span>
        <span class="n">journal</span> <span class="o">=</span> <span class="n">Journal</span><span class="p">(</span><span class="n">JOURNAL_NAME</span><span class="p">,</span> <span class="n">user</span><span class="p">)</span>
        <span class="n">root</span><span class="p">[</span><span class="s">&#39;journal&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">journal</span>
    <span class="c"># move bloxsom data into Entry/Journal</span>
    <span class="n">add_journal_entries</span><span class="p">(</span><span class="n">bloxsom_to_mapping</span><span class="p">(</span><span class="n">BLOXSOM_ENTRY_PATH</span><span class="p">),</span>
                        <span class="n">journal</span><span class="p">)</span>
    <span class="c"># made it here, commit everything to the database</span>
    <span class="n">pub</span><span class="o">.</span><span class="n">get_connection</span><span class="p">()</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
    <span class="c"># that&#39;s it!</span>
</pre></div>
</div>
</div>
<div class="section">
<h2><a id="next-installment" name="next-installment">Next Installment</a></h2>
<p>When we return in part seven of this series we will further flesh out our UI
objects for <tt class="docutils literal"><span class="pre">Entry</span></tt> and <tt class="docutils literal"><span class="pre">Journal</span></tt>, adding methods for creating and editing
objects. At that point we'll have a basic journal or weblog application ready
to deploy to the world. Subsequent articles will add more functionality.</p>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:472</guid>
  <pubDate>Fri, 08 Jun 2007 15:44:00 GMT</pubDate>
</item>
</channel></rss>