<?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/qp/</link>
  <atom:link href="http://mikewatkins.ca/tags/qp/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>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 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>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>
<item>
  <title>Python Web Application Diary, Part Five</title>
  <link>http://mikewatkins.ca/2007/06/06/python-web-application-diary-part-five/</link>
  <description><![CDATA[
<div class="document">
<p>In <a class="reference" href="/tags/python/2007-06-04-14-18.html">part four</a> of this series we looked at the <tt class="docutils literal"><span class="pre">spec</span></tt> module from <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a>, and
created some basic <a class="reference" href="http://www.mems-exchange.org/software/sancho/">Sancho</a> test cases. In this installment we'll write a few
lines of code for the web UI of the application, our first real exposure to
the web workings of QP.</p>
<div class="section">
<h2><a id="qp-applications" name="qp-applications">QP Applications</a></h2>
<p>In <a class="reference" href="/tags/python/2007-06-02-23-23.html">part two</a> we created file system hierarchies for both a library and our
project application. Since we plan on reusing our journal objects and related
UI, those components will live in our library and our application itself will
import them.</p>
<p>The fundamental requirements of any QP application are:</p>
<ol class="arabic simple">
<li>The application(s) must live somewhere in the QP search path. Issue <tt class="docutils literal"><span class="pre">qp</span>
<span class="pre">-h</span></tt> at the command line for details.</li>
<li>Applications must define a module called either <tt class="docutils literal"><span class="pre">slash.py</span></tt> or
<tt class="docutils literal"><span class="pre">slash.qpy</span></tt>. Both contain Python code, <tt class="docutils literal"><span class="pre">qpy</span></tt> files are QP
template-aware Python modules which we'll talk about more shortly.</li>
<li>The <tt class="docutils literal"><span class="pre">slash</span></tt> module must expose two objects, a <tt class="docutils literal"><span class="pre">SitePublisher</span></tt> which
defines the publishing environment and any application specific
customizations, as well as a <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> which defines the &quot;root&quot;
directory of the application.</li>
</ol>
<p>We generated a basic application skeleton using a tool cooked up just for this
article series, <a class="reference" href="/software/files/qp/mkqpapp.py">mkqpapp.py</a>. This created in ~/qp_apps/blog the following
hierarchy:</p>
<pre class="literal-block">
CHANGES
README
__init__.py
bin/
doc/
slash.qpy
test/
ui/
    test/
var/
</pre>
<p>Ignoring the obvious or already discussed, lets explore files of
significance:</p>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal"><span class="pre">slash.qpy</span></tt> as discussed earlier can be thought of as the <em>driver</em> or
root application directory of our new application.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">__init__.py</span></tt> defines the directory as a package; however should there be
any <tt class="docutils literal"><span class="pre">qpy</span></tt> modules in the given directory, __init__.py must include two
lines of code:</p>
<pre class="literal-block">
from qpy.compile import compile_qpy_files
compile_qpy_files(__path__[0])
</pre>
<p><a class="reference" href="http://www.mems-exchange.org/software/qpy/">QPY</a>, which was installed in <a class="reference" href="/tags/python/2007-05-31-10-49.html">part one</a> of this series, provides unicode and
quote-safe Python <em>templating</em>, in a manner quite different than all other
Python templating approaches. Diligent use of <tt class="docutils literal"><span class="pre">qpy</span></tt> can reduce or eliminate
your application's risk of being exploited by <em>cross site scripting</em> attacks,
and SQL injection attacks, if your application uses SQL.</p>
<p>No magic or import hooks are required to integrate QPY with regular Python
code; the two lines in <tt class="docutils literal"><span class="pre">__init__.py</span></tt> ensure that QPY files are compiled as
regular Python code. Its a small price to pay for much flexibility as we'll
see soon.</p>
</li>
</ol>
</div>
<div class="section">
<h2><a id="exposing-objects-methods-or-functions-on-the-web" name="exposing-objects-methods-or-functions-on-the-web">Exposing Objects, Methods or Functions on the Web</a></h2>
<p>Lets now open the template <tt class="docutils literal"><span class="pre">slash.qpy</span></tt> module, explore, and make some
changes. If you've not already started the template application created in
<a class="reference" href="/tags/python/2007-06-02-23-23.html">part two</a>, launch it now:</p>
<pre class="literal-block">
qp blog start
</pre>
<p>Visit the application at <a class="reference" href="http://localhost:8011/">http://localhost:8011/</a> and you'll see:</p>
<blockquote>
<p><strong>It worked!</strong></p>
<p>The <strong>blog</strong> application lives at <tt class="docutils literal"><span class="pre">/usr/home/yourhomedir/qp_sites/blog</span></tt>.</p>
</blockquote>
<p>Lets edit <tt class="docutils literal"><span class="pre">slash.qpy</span></tt> and make some changes. First of all, since we've not
discussed <tt class="docutils literal"><span class="pre">https</span></tt> secure http communications at all, yet, lets comment out
some of the configuration:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">SitePublisher</span> <span class="p">(</span><span class="n">DurusPublisher</span><span class="p">):</span>

    <span class="n">configuration</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span>
        <span class="n">durus_address</span><span class="o">=</span><span class="p">(</span><span class="s">&#39;localhost&#39;</span><span class="p">,</span> <span class="mi">7011</span><span class="p">),</span>
        <span class="n">http_address</span><span class="o">=</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="mi">8011</span><span class="p">),</span>
        <span class="c"># as_https_address=(&#39;localhost&#39;, 9011),</span>
        <span class="c"># https_address=(&#39;&#39;, 10011),</span>
        <span class="p">)</span>
</pre></div>
<p>The <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> README contains a recipe for using <tt class="docutils literal"><span class="pre">stunnel</span></tt> for providing <tt class="docutils literal"><span class="pre">https</span></tt>
SSL abilities to your QP applications.</p>
<div class="section">
<h3><a id="sitedirectory-our-master-controller" name="sitedirectory-our-master-controller">SiteDirectory, Our Master Controller</a></h3>
<p>Lets have a look at <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt>, a required class which defines in
essence the root of our web application. All paths lead from here, as we'll
see now.</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">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>
</pre></div>
<p>Two items of note:</p>
<p><tt class="docutils literal"><span class="pre">get_exports()</span></tt> explicitly defines what methods or functions we are willing
to expose to the World Wide Web (or to other applications via REST or other
RPC methods). Nothing is shared with the world by default, everything is
explicitly enabled, if at all.</p>
<p><tt class="docutils literal"><span class="pre">get_exports()</span></tt> returns a four element tuple containing:</p>
<blockquote>
<ol class="arabic simple">
<li>The current directory 'pathname' to be exposed to the web, a null string
if the path is to be '/'.</li>
<li>The name of the Python callable that the pathname refers to. The
publisher 'translates' pathnames into callables, making it easy to expose
deeply nested hierarchies of functionality.</li>
<li>A short name describing the callable; this is optionally used for menus.</li>
<li>A longer descriptive phrase describing the callable; this is optionally
used for anchor titles / tooltips.</li>
</ol>
</blockquote>
<p>Thus <tt class="docutils literal"><span class="pre">get_exports</span></tt> currently defines only a single exposed callable, a null
path component (''), which translates to the <tt class="docutils literal"><span class="pre">index</span></tt> of the class.</p>
<p>The <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> method <tt class="docutils literal"><span class="pre">def</span> <span class="pre">index</span> <span class="pre">[html]</span> <span class="pre">(self):</span></tt> introduces another
significant component of the <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> web application framework and family, <a class="reference" href="http://www.mems-exchange.org/software/qpy/">QPY</a>,
which provides almost fully transparent  source-code transformation of an
&quot;html template&quot; into pure Python. QPY delivers other capabilities which
deserve some looking at now.</p>
</div>
<div class="section">
<h3><a id="html-template-basics-hello-world" name="html-template-basics-hello-world">HTML Template Basics - Hello, World</a></h3>
<p>Lets add a new method to our <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> class and expose it to the
web. First the method:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">hello</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;&lt;p&gt;Hello, world&lt;/p&gt;&#39;</span>
</pre></div>
<p>If you restart your application, and visit: <a class="reference" href="http://localhost:8011/hello">http://localhost:8011/hello</a>:</p>
<pre class="literal-block">
qp blog restart
</pre>
<p>You'll be rewarded with the default <cite>404</cite> response (which we can easily
replace). Why? Because we forgot to expose <tt class="docutils literal"><span class="pre">hello()</span></tt> to the web:</p>
<blockquote>
'hello'?</blockquote>
<p>Don't believe that was a <tt class="docutils literal"><span class="pre">404</span></tt> response? Lets also run the QP log watcher in
another terminal window:</p>
<pre class="literal-block">
qp -l blog
</pre>
<p>You'll discover your last log entry looks something like:</p>
<pre class="literal-block">
2007-06-06 19:45:04 404 .007931 127.0.0.1 - - 3005 http GET /hello - Mozilla/5.0_
</pre>
<p>By now you know what must be done. Yes, that's right, we need to add an entry
to <tt class="docutils literal"><span class="pre">get_exports()</span></tt>:</p>
<div class="highlight"><pre><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;hello&#39;</span><span class="p">,</span> <span class="s">&#39;hello&#39;</span><span class="p">,</span> <span class="s">&#39;Greetings&#39;</span><span class="p">,</span> <span class="s">&#39;A page for salutations&#39;</span><span class="p">)</span>
</pre></div>
<p>Restart the application -- I map a key in <tt class="docutils literal"><span class="pre">vim</span></tt> to restart the current project
I am editing -- and the revisit <a class="reference" href="http://localhost:8011/hello">http://localhost:8011/hello</a> and you'll now
see returned:</p>
<blockquote>
Hello, world</blockquote>
<p>That's not very exciting I admit. Lets alter hello() and supply the new method
with some user-provided input to our hello method. For the purposes of this
example, in order to keep things simple at this stage, lets pretend <tt class="docutils literal"><span class="pre">hello</span></tt>
is asked to process the results of a form submission. We'll define, outside of our
hello() method, a dictionary called form as such:</p>
<div class="highlight"><pre><span class="n">form</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span>
    <span class="n">title</span><span class="o">=</span><span class="s">&#39;User supplied title&#39;</span><span class="p">,</span>
    <span class="n">text</span><span class="o">=</span><span class="s">&#39;Malicious user supplied &lt;script&gt;destroy_you_all()&lt;/script&gt;&#39;</span><span class="p">)</span>
</pre></div>
<p>Lets update <tt class="docutils literal"><span class="pre">hello</span></tt>:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">hello</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;&lt;p&gt;Hello, world, I&#39;</span><span class="n">m</span> <span class="n">a</span> <span class="n">malicious</span> <span class="n">user</span> <span class="ow">and</span> <span class="n">posted</span> <span class="n">this</span><span class="p">:</span><span class="o">&lt;/</span><span class="n">p</span><span class="o">&gt;</span><span class="s">&#39;</span>
    <span class="s">&#39;&lt;h2&gt;</span><span class="si">%(title)s</span><span class="s">&lt;/h2&gt;&#39;</span> <span class="o">%</span> <span class="n">form</span>
    <span class="s">&#39;&lt;p&gt;</span><span class="si">%(text)s</span><span class="s">&lt;/p&gt;&#39;</span> <span class="o">%</span> <span class="n">form</span>
</pre></div>
<p>The result:</p>
<pre class="literal-block">
Hello, world, I am a malicious user and posted this:

User supplied title

Malicious user supplied &lt;script&gt;destroy_you_all()&lt;/script&gt;
</pre>
<p>View source and you'll see that the content has been fully escaped and quoted:</p>
<blockquote>
[snip]
Malicious user supplied &amp;lt;script&amp;gt;destroy_you_all()&amp;lt;/script&amp;gt;</blockquote>
<p>This demonstrates one of the core features of QPY - safe quoting of content
not explicitly declared as safe beforehand.</p>
<p>With this lesson in mind, its time now to write a basic shell of a user
interface for our Entry and Journal objects. The interface code won't go
directly into our application but into our library of useful reusable routines
- but of course you could simply create the objects and related UI in the
<tt class="docutils literal"><span class="pre">blog</span></tt> application hierarchy.</p>
</div>
</div>
<div class="section">
<h2><a id="entry-ui" name="entry-ui">Entry UI</a></h2>
<p>Our last task for this installment is to flesh out the basic UI for the
<tt class="docutils literal"><span class="pre">Entry</span></tt> object:</p>
<div class="highlight"><pre><span class="k">from</span> <span class="nn">qp.fill.directory</span> <span class="k">import</span> <span class="n">Directory</span>
<span class="k">from</span> <span class="nn">qp.lib.spec</span> <span class="k">import</span> <span class="n">require</span>
<span class="k">from</span> <span class="nn">qp.pub.common</span> <span class="k">import</span> <span class="n">header</span><span class="p">,</span> <span class="n">footer</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="p">,</span> <span class="n">JournalDatabase</span>

<span class="c"># this is for demonstration only.</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="n">test_entry</span> <span class="o">=</span> <span class="n">Entry</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="s">&#39;foo&#39;</span><span class="p">))</span>
<span class="n">test_entry</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">&#39;An example journal entry&#39;</span><span class="p">)</span>
<span class="n">test_entry</span><span class="o">.</span><span class="n">set_text</span><span class="p">(</span><span class="s">&quot;To QPY, or not to QPY, &lt;that&gt; is the question &amp; what&#39;s for dinner?&quot;</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">EntryUI</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 a single entry,</span>
<span class="sd">    edit a new or existing entry, delete an entry, and to represent</span>
<span class="sd">    an entry in different ways including RSS or Atom.</span>

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

<span class="sd">        ../1234/</span>
<span class="sd">        ../1234/edit</span>
<span class="sd">        ../1234/delete</span>
<span class="sd">        ../1234/index.rss</span>
<span class="sd">        ../1234/index.xml</span>
<span class="sd">    &quot;&quot;&quot;</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">component</span><span class="o">=</span><span class="n">test_entry</span><span class="p">):</span>
        <span class="n">require</span><span class="p">(</span><span class="n">component</span><span class="p">,</span> <span class="n">Entry</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">entry</span> <span class="o">=</span> <span class="n">component</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;edit&#39;</span><span class="p">,</span> <span class="s">&#39;edit&#39;</span><span class="p">,</span> <span class="s">&#39;Edit Entry&#39;</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;delete&#39;</span><span class="p">,</span> <span class="s">&#39;delete&#39;</span><span class="p">,</span> <span class="s">&#39;Delete Entry&#39;</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 entry&#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 entry&#39;</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="c"># python docstrings can&#39;t be included in [html] templates</span>
        <span class="c"># this template could also deliver the output of other</span>
        <span class="c"># template engines such as Genshi, Cheetah, Mako, etc.</span>
        <span class="c"># lets stick with QPY for now.</span>
        <span class="n">title</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">entry</span><span class="o">.</span><span class="n">get_title</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;h1&gt;</span><span class="si">%s</span><span class="s">&lt;/h1&gt;&#39;</span> <span class="o">%</span> <span class="n">title</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">entry</span><span class="o">.</span><span class="n">get_text</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">atom</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="c"># lets just point all these methods to atom / not implemented for now until</span>
    <span class="c"># we implement each</span>
    <span class="n">edit</span> <span class="o">=</span> <span class="n">delete</span> <span class="o">=</span> <span class="n">rss</span> <span class="o">=</span> <span class="n">atom</span>
</pre></div>
<p>If you want to race ahead and see what this looks like from the browser side,
update <tt class="docutils literal"><span class="pre">slash.qpy</span></tt> by adding an import, changing <tt class="docutils literal"><span class="pre">get_exports</span></tt> and <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> as
follows (don't forget to restart the application afterwards):</p>
<div class="highlight"><pre><span class="c"># import your UI object from wherever you put it.</span>
<span class="k">from</span> <span class="nn">parlez.ui.journal</span> <span class="k">import</span> <span class="n">EntryUI</span>
</pre></div>
<p>Modify <tt class="docutils literal"><span class="pre">get_exports</span></tt> as such:</p>
<div class="highlight"><pre><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;hello&#39;</span><span class="p">,</span> <span class="s">&#39;hello&#39;</span><span class="p">,</span> <span class="s">&#39;Greetings&#39;</span><span class="p">,</span> <span class="s">&#39;A page for salutations&#39;</span><span class="p">)</span>
    <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;test_entry&#39;</span><span class="p">,</span> <span class="s">&#39;test_entry&#39;</span><span class="p">,</span> <span class="s">&#39;EntryUI Test&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
</pre></div>
<p>Modify <tt class="docutils literal"><span class="pre">SiteDirectory</span></tt> by adding an attribute which points to an instance of
EntryUI:</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="c"># snip</span>

    <span class="n">test_entry</span> <span class="o">=</span> <span class="n">EntryUI</span><span class="p">()</span>
</pre></div>
<p>And then restart the app (<tt class="docutils literal"><span class="pre">qp</span> <span class="pre">blog</span> <span class="pre">restart</span></tt>) and visit the following URL's:</p>
<blockquote>
<ul class="simple">
<li><a class="reference" href="http://localhost:8011/test_entry">http://localhost:8011/test_entry</a> (note what happens)</li>
<li><a class="reference" href="http://localhost:8011/test_entry/">http://localhost:8011/test_entry/</a></li>
<li><a class="reference" href="http://localhost:8011/test_entry/edit">http://localhost:8011/test_entry/edit</a></li>
<li><a class="reference" href="http://localhost:8011/test_entry/delete">http://localhost:8011/test_entry/delete</a></li>
<li><a class="reference" href="http://localhost:8011/test_entry/index.rss">http://localhost:8011/test_entry/index.rss</a></li>
<li><a class="reference" href="http://localhost:8011/test_entry/index.xml">http://localhost:8011/test_entry/index.xml</a></li>
</ul>
</blockquote>
</div>
<div class="section">
<h2><a id="next-installment" name="next-installment">Next Installment</a></h2>
<p>When we return in part six of this series we will inject some real <tt class="docutils literal"><span class="pre">Entry</span></tt>
data into our <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> database and explore how we will work with Durus objects
interactively, from our application, and from scripts.</p>
<p>After that we'll turn our application shell into a functioning application by
stripping out the quick and dirty demonstration data hacks and completing our
&quot;controller&quot; logic for EntryUI and Journal user interface classes.</p>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:471</guid>
  <pubDate>Wed, 06 Jun 2007 18:51:00 GMT</pubDate>
</item>
<item>
  <title>Python Web Application Diary, Part Four</title>
  <link>http://mikewatkins.ca/2007/06/04/python-web-application-diary-part-four/</link>
  <description><![CDATA[
<div class="document">
<p>In <a class="reference" href="/tags/python/2007-06-04-11-33.html">part three</a> of this series we determined that <tt class="docutils literal"><span class="pre">Entry</span></tt> and <tt class="docutils literal"><span class="pre">Journal</span></tt>
classes will be required, and we started to look at how straight Python
classes could become full database participants in a <a class="reference" href="http://www.mems-exchange.org/software/durus/">Durus</a> object database.</p>
<p>Our object model so far is very simplistic; lets add a healthy dose of
constraints to aid both in testing and also prevent unintended (mis)use down
the road.</p>
<p>I promise we'll get to <em>webby</em> things soon enough, we are just waiting for the
chorus to come around again on the guitar. (All apologies to Arlo Guthrie)</p>
<div class="section">
<h2><a id="specifications-contracts-for-busy-developers" name="specifications-contracts-for-busy-developers">Specifications: Contracts for Busy Developers</a></h2>
<p>The <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> package has a wonderful module, <tt class="docutils literal"><span class="pre">qp.lib.spec</span></tt> which deserves to get
more attention whether QP does or not.</p>
<p><tt class="docutils literal"><span class="pre">spec</span></tt> provides an easy way to declare or <em>specify</em> what various object
attributes should contain, without littering your code with miles of asserts
and other test. Examples will make things clear - lets take our too-simple
<tt class="docutils literal"><span class="pre">Entry</span></tt> object and beef it up.  First the original database-aware object:</p>
<div class="highlight"><pre><span class="k">from</span> <span class="nn">durus.persistent</span> <span class="k">import</span> <span class="n">PersistentObject</span>

<span class="k">class</span> <span class="nc">Entry</span><span class="p">(</span><span class="n">PersistentObject</span><span class="p">):</span>
    <span class="n">title</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">text</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">author</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">created</span> <span class="o">=</span> <span class="bp">None</span>
</pre></div>
<p><strong>Out of control</strong>: Clearly the object as its described presents a problem -
its just an empty 'bag' with no constraints on what a user/developer might
attempt to do with it. For example, whether intended or not, our dumb object
allows for all sorts of questionable attribute assignments, as shown in this
interactive session:</p>
<div class="highlight"><pre><span class="o">-&gt;&gt;</span> <span class="n">e</span> <span class="o">=</span> <span class="n">Entry</span><span class="p">()</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s">&#39;my title&#39;</span>  <span class="c"># so far so good</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="bp">None</span>        <span class="c"># probably reasonable</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="mf">123.456</span>     <span class="c"># not at all what we want</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">created</span> <span class="o">=</span> <span class="mf">3.1415</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">some_new_attribute</span> <span class="o">=</span> <span class="s">&quot;foo&quot;</span>
</pre></div>
<p>The dynamic nature of Python is both blessing and curse at times; the above
object requires lots of additional code to ensure that data it manages is what
the developer intended, leading to significant code expansion via tests and
assertions, reducing readability along the way.</p>
<p><strong>In control</strong>: There is another way.  Lets use <tt class="docutils literal"><span class="pre">spec</span></tt> and apply
specifications and some helper methods:</p>
<div class="highlight"><pre><span class="k">from</span> <span class="nn">durus.persistent</span> <span class="k">import</span> <span class="n">PersistentObject</span>
<span class="k">from</span> <span class="nn">qp.lib.spec</span> <span class="k">import</span> <span class="n">datetime_with_tz</span><span class="p">,</span> <span class="n">spec</span><span class="p">,</span> <span class="n">string</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="k">class</span> <span class="nc">Entry</span><span class="p">(</span><span class="n">PersistentObject</span><span class="p">):</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">&#39;A short description of the entry&#39;</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">&#39;The full text of the entry&#39;</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">&#39;The individual responsible for the content of the entry&#39;</span><span class="p">)</span>
    <span class="n">created_is</span> <span class="o">=</span> <span class="n">datetime_with_tz</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>A spec can be a simple type assignment (see <tt class="docutils literal"><span class="pre">created_is</span></tt> above) or make use
of the spec function which allows for a certain amount of self-documentation
which can be very helpful. Arguments supplied in tuple imply the specification
<tt class="docutils literal"><span class="pre">either</span></tt>, or you can spell it out: <tt class="docutils literal"><span class="pre">either(string,</span> <span class="pre">None)</span></tt>.</p>
<p>Now lets run through the same interactive session as before:</p>
<div class="highlight"><pre><span class="o">-&gt;&gt;</span> <span class="n">e</span> <span class="o">=</span> <span class="n">Entry</span><span class="p">()</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">&#39;my title&#39;</span><span class="p">)</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
<span class="o">-&gt;&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="mf">123.456</span><span class="p">)</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
  <span class="n">File</span> <span class="s">&quot;&lt;input&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
  <span class="n">File</span> <span class="s">&quot;/usr/local/lib/python2.5/site-packages/qp/lib/spec.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">725</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span>
    <span class="n">require</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="n">name</span> <span class="o">+</span> <span class="s">&#39;_is&#39;</span><span class="p">))</span>
  <span class="n">File</span> <span class="s">&quot;/usr/local/lib/python2.5/site-packages/qp/lib/spec.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">171</span><span class="p">,</span> <span class="ow">in</span> <span class="n">require</span>
    <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
<span class="ne">TypeError</span><span class="p">:</span>
  <span class="n">Expected</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="n">A</span> <span class="n">short</span> <span class="n">description</span> <span class="n">of</span> <span class="n">the</span> <span class="n">entry</span>
  <span class="n">Got</span><span class="p">:</span> <span class="mf">123.456</span>
</pre></div>
<p>Aha, for the cost of a pair of get and set methods (no moaning please, we
didn't even have to write the getter and setter ourselves and they don't
clutter the code), we've effectively constrained what type of data can be
assigned to the <tt class="docutils literal"><span class="pre">title</span></tt> attribute of Entry, while also preserving the easy
to read nature of the original, simple, code.</p>
<p>To demonstrate the utility of <tt class="docutils literal"><span class="pre">spec</span></tt>, I've pulled a number of examples from
<a class="reference" href="http://www.mems-exchange.org/software/dulcinea/">Dulcinea</a> and my own code. As you can see, there is great flexibility provided:</p>
<div class="highlight"><pre><span class="n">date_is</span> <span class="o">=</span> <span class="n">datetime</span>

<span class="n">approvals_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
    <span class="n">sequence</span><span class="p">(</span><span class="n">DulcineaUser</span><span class="p">,</span> <span class="n">set</span><span class="p">),</span>
    <span class="s">&quot;The users who agree the issue is resolved.&quot;</span><span class="p">)</span>

<span class="n">issues_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
    <span class="n">mapping</span><span class="p">({</span><span class="n">string</span><span class="p">:</span><span class="n">Issue</span><span class="p">},</span> <span class="n">PersistentDict</span><span class="p">),</span>
    <span class="s">&quot;Mapping of issue IDs to issues.&quot;</span><span class="p">)</span>

<span class="n">id_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
    <span class="n">pattern</span><span class="p">(</span><span class="s">&#39;^[-A-Za-z0-9_@.]*$&#39;</span><span class="p">),</span>
    <span class="s">&quot;unique among users here&quot;</span><span class="p">)</span>

<span class="c"># some specs are referred to over and over again</span>

<span class="n">datetime_without_tz</span> <span class="o">=</span> <span class="n">both</span><span class="p">(</span><span class="n">datetime</span><span class="p">,</span> <span class="n">with_attribute</span><span class="p">(</span><span class="n">tzinfo</span><span class="o">=</span><span class="bp">None</span><span class="p">))</span>
<span class="n">datetime_with_tz</span> <span class="o">=</span> <span class="n">both</span><span class="p">(</span><span class="n">datetime</span><span class="p">,</span> <span class="n">with_attribute</span><span class="p">(</span><span class="n">tzinfo</span><span class="o">=</span><span class="n">no</span><span class="p">(</span><span class="bp">None</span><span class="p">)))</span>
<span class="n">email_pattern</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">(</span><span class="s">&quot;^.+@.+\..{2,4}$&quot;</span><span class="p">)</span>
<span class="n">existing_user</span> <span class="o">=</span> <span class="n">both</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">with_attribute</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">no</span><span class="p">(</span><span class="bp">None</span><span class="p">)))</span>
<span class="n">hex_pattern</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">(</span><span class="s">&#39;[a-fA-F0-9]*$&#39;</span><span class="p">)</span>

<span class="c"># reuse them</span>

<span class="n">email_is</span> <span class="o">=</span> <span class="n">email_pattern</span>
<span class="n">id_is</span> <span class="o">=</span> <span class="n">spec</span><span class="p">(</span>
    <span class="n">hex_pattern</span><span class="p">,</span>
    <span class="s">&#39;a lower case alphanumeric pattern&#39;</span><span class="p">)</span>

<span class="c"># use the specifications in tests (more powerful and cleaner than</span>
<span class="c"># testing for type and instance alone)</span>

<span class="n">require</span><span class="p">(</span><span class="n">thing</span><span class="p">,</span> <span class="n">either</span><span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">))</span>
<span class="n">match</span><span class="p">(</span><span class="n">a_user</span><span class="p">,</span> <span class="n">existing_user</span><span class="p">)</span> <span class="c"># returns boolean</span>
</pre></div>
<p>My experience is that you can create fairly complex specifications that remain
very readable. Have a complex object that has to be &quot;just so&quot; before it is
committed to a database in a transaction? Specify everything, and check it for
sanity with a one line assertion: <tt class="docutils literal"><span class="pre">assert</span>
<span class="pre">get_spec_problems(theobject_instance)</span> <span class="pre">==</span> <span class="pre">[]</span></tt> and you are done.</p>
</div>
<div class="section">
<h2><a id="testing-testing-one-two-three" name="testing-testing-one-two-three">Testing, Testing, One Two Three</a></h2>
<p>As you might imagine, it becomes easier to write unit tests when our objects
are so highly specified. <a class="reference" href="http://www.mems-exchange.org/software/sancho/">Sancho</a>, a unit testing framework also from the same
development shop from which <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a> originates, is designed for projects and teams
who prefer to leave code in a working state, all or most of the time.</p>
<p>Tests live in ./test, one level down from our objects being tested, and there
is no <tt class="docutils literal"><span class="pre">__init__.py</span></tt>. A utility, urun.py, will execute one test supplied on
the command line, or all tests in the <tt class="docutils literal"><span class="pre">test</span></tt> subdirectories in the current
directory and below. Lets write one for <tt class="docutils literal"><span class="pre">Entry</span></tt>:</p>
<div class="highlight"><pre><span class="c"># /www/lib/parlez/test/utest_journal.py</span>
<span class="k">from</span> <span class="nn">parlez.journal</span> <span class="k">import</span> <span class="n">Entry</span>
<span class="k">from</span> <span class="nn">sancho.utest</span> <span class="k">import</span> <span class="n">UTest</span><span class="p">,</span> <span class="n">raises</span>

<span class="n">entry_text</span> <span class="o">=</span> <span class="s">&#39;&#39;&#39;This is a blog entry.</span><span class="se">\n\n</span><span class="s">*We hope you like it*.&#39;&#39;&#39;</span>

<span class="k">class</span> <span class="nc">EntryTest</span><span class="p">(</span><span class="n">UTest</span><span class="p">):</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="n">Entry</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">entry_test</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">joe</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s">&#39;joe&#39;</span><span class="p">)</span>
        <span class="n">e</span> <span class="o">=</span> <span class="n">Entry</span><span class="p">()</span>
        <span class="n">e</span><span class="o">.</span><span class="n">set_author</span><span class="p">(</span><span class="n">joe</span><span class="p">)</span>
        <span class="c"># a string causes a TypeError, authors must be User instances</span>
        <span class="n">raises</span><span class="p">(</span><span class="ne">TypeError</span><span class="p">,</span> <span class="n">e</span><span class="o">.</span><span class="n">set_author</span><span class="p">,</span> <span class="s">&#39;Joe&#39;</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">e</span><span class="o">.</span><span class="n">get_author</span><span class="p">()</span> <span class="o">==</span> <span class="n">joe</span>
        <span class="k">assert</span> <span class="n">e</span><span class="o">.</span><span class="n">get_created</span><span class="p">()</span> <span class="o">==</span> <span class="n">e</span><span class="o">.</span><span class="n">get_stamp</span><span class="p">()</span>
        <span class="n">e</span><span class="o">.</span><span class="n">set_text</span><span class="p">(</span><span class="n">entry_text</span><span class="p">)</span>
        <span class="k">assert</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">entry_text</span>
        <span class="n">e</span><span class="o">.</span><span class="n">set_stamp</span><span class="p">()</span>
        <span class="k">assert</span> <span class="n">e</span><span class="o">.</span><span class="n">get_created</span><span class="p">()</span> <span class="o">!=</span> <span class="n">e</span><span class="o">.</span><span class="n">get_stamp</span><span class="p">()</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 shortly, before Journal!</span>
    <span class="k">pass</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>Run <tt class="docutils literal"><span class="pre">urun.py</span></tt> from the command line or from your editor and the result:</p>
<pre class="literal-block">
# /www/lib/parlez/test% urun.py
./utest_journal.py: EntryTest:
</pre>
<p>No tracebacks indicates successful test(s).</p>
<p>In part five of this series we'll start to write the HTML (remember, this
article series is apparently about web development with <a class="reference" href="http://www.mems-exchange.org/software/qp/">QP</a>) and other user
interfaces for our Entry object.</p>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:469</guid>
  <pubDate>Mon, 04 Jun 2007 21:18:00 GMT</pubDate>
</item>
<item>
  <title>Python Web Application Diary, Part Three</title>
  <link>http://mikewatkins.ca/2007/06/04/python-web-application-diary-part-three/</link>
  <description><![CDATA[
<div class="document">
<p>In <a class="reference" href="/tags/python/2007-06-02-23-23.html">part two</a> of this series we created a location and file system hierarchy
for application library objects, UI and other components, and did the same for
an actual application by using a script <a class="reference" href="/software/files/qp/mkqpapp.py">mkqpapp.py</a> that automates those
tasks.</p>
<p>Today lets start writing code -- we'll begin by defining basic objects for
managing weblog or journal <em>entries</em>, and then we'll move on to showing how
<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> make defining and publishing your Python objects as easy as,
well, py.</p>
<div class="section">
<h2><a id="basic-data-elements" name="basic-data-elements">Basic Data Elements</a></h2>
<p>As discussed in <a class="reference" href="/tags/python/2007-05-31-10-49.html">part one</a>, this tutorial / web application project will
result in a basic weblog or on-line journal application. Lets break down a
weblog into its most basic data elements:</p>
<ol class="arabic simple">
<li>A weblog is a collection of writing, generally presented in chronological
fashion. A weblog could be considered a diary or journal, so lets use the
term <strong>Journal</strong> to describe its function.</li>
<li>A journal usually, but not always, represents the thoughts and opinions of
a single author.</li>
<li>Each item in a journal can be considered an article or a post -
lets use a more generic term and call each item in the journal an
<strong>Entry</strong>.  Entries are typically short bits of text so lets enter and
store them as such. Each entry may have a title, and may include other
information including dates relating to when the Entry was created, made
available to readers, or changed -- but the principal information is the
entry itself.</li>
</ol>
</div>
<div class="section">
<h2><a id="our-first-classes" name="our-first-classes">Our first classes</a></h2>
<p>Turning to Python then, we could easily represent <strong>Entry</strong> as:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">Entry</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="n">title</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">text</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">created</span> <span class="o">=</span> <span class="bp">None</span>
</pre></div>
<p>We could then use the class:</p>
<div class="highlight"><pre><span class="n">e</span> <span class="o">=</span> <span class="n">Entry</span><span class="p">()</span>
<span class="n">e</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s">&#39;Python Web Application Diary, Part Three&#39;</span>
<span class="n">e</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">&#39;Hello, Bruce, my name is Bruce.&#39;</span>
<span class="n">created</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</pre></div>
<p>That was pretty simple, no? Simplicity can be both a boon and a pain in the
butt, and experienced developers will recognize at least two significant
problems with our still too-simple <tt class="docutils literal"><span class="pre">Entry</span></tt> object:</p>
<ol class="arabic simple">
<li>There is no way of easily persisting this data (saving it so that its
available later when we need it)</li>
<li>The current design doesn't warn or otherwise prevent someone from
intentionally or accide