<?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/durus/</link>
  <atom:link href="http://mikewatkins.ca/tags/durus/feeds/rss" type="application/rss+xml" rel="self"/>
  <lastBuildDate>Tue, 25 Aug 2009 19:25:20 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>QP and Durus Updated</title>
  <link>http://mikewatkins.ca/2009/08/25/qp-and-durus-updated/</link>
  <description><![CDATA[
<div class="document">
<p>The folks at <a class="reference external" href="http://www.mems-exchange.org/software/">mems-exchange.org</a> released a <a class="reference external" href="http://mail.mems-exchange.org/durusmail/qp/453/">new version of the Python web application / site-management framework QP</a> and supporting packages.</p>
<p>All of today's released packages support Python &gt;= 2.4, which includes Python 3.1.</p>
<p>They also released an <a class="reference external" href="http://mail.mems-exchange.org/durusmail/durus-users/989/">update to Durus</a>, a compact and mature Python object database (which at its core operates like a minimal ZODB/ZEO work-a-like). The API to a Python object db is simply Python.</p>
<p>Key-value databases appear to be in vogue these days. Python developers with an interest in key-value databases may want to check out Durus (or ZODB): the key-value database you already know, <em>and more</em>.</p>
<p><em>More</em> means more than key-value pairs and simple types. More means virtually any Python object / type. More means more than simple string or integer keys  and simple values and offers not only persistent dictionaries but also persistent lists and sets, and persistent objects of most any design you may wish to implement.</p>
<p>Durus has no other package dependencies and is compact. Weighing in with less than 5000 lines of code it small enough to read in one sitting if you want to see how things tick. Or <a class="reference external" href="http://www.mems-exchange.org/software/durus/Durus-3.9.tar.gz/Durus-3.9/README.txt">you can just dive in</a> - start a server <tt class="docutils literal"><span class="pre">durus</span> <span class="pre">-s</span></tt> and a client <tt class="docutils literal"><span class="pre">durus</span> <span class="pre">-c</span></tt> and play.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:715</guid>
  <pubDate>Tue, 25 Aug 2009 19:25:20 GMT</pubDate>
  <category>durus</category>
  <category>python</category>
  <category>qp</category>
</item>
<item>
  <title>Tyrannical Databases</title>
  <link>http://mikewatkins.ca/2009/03/12/tyrannical-databases/</link>
  <description><![CDATA[
<div class="document">
<p>Inspired by a series of slides <a class="reference external" href="http://michael.susens-schurter.com/blog/2009/03/11/tokyo-cabinet-pytyrant-talk/">Michael Schurter published</a> on <em>Tokyo Cabinet</em> and <em>PyTyrant</em>, I thought I'd code up his examples using another database which can use a key-value approach, <a class="reference external" href="http://www.mems-exchange.org/software/durus/">Durus</a>.</p>
<p>Durus is a ZODB work-a-like which allows for easy persistence of <em>Python objects</em>, not just values. It's simple, fast, and useful.</p>
<p>Here's the baseline Tokyo Cabinet db example Michael published, using the pytc interface:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">pytc</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">pytc</span><span class="o">.</span><span class="n">HDB</span><span class="p">()</span>
<span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s">&#39;test.tch&#39;</span><span class="p">,</span>  <span class="n">pytc</span><span class="o">.</span><span class="n">BDBOWRITER</span> <span class="o">|</span> <span class="n">pytc</span><span class="o">.</span><span class="n">BDBOREADER</span> <span class="o">|</span> <span class="n">pytc</span><span class="o">.</span><span class="n">BDBOCREAT</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">db</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">v</span><span class="p">)</span>
        <span class="n">db</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
</pre></div>
<p>Running it:</p>
<pre class="literal-block">
$ time python test.py
real    0m0.168s
user    0m0.157s
sys     0m0.010s
</pre>
<p>And here is a Durus example, accessing a local file-based storage:</p>
<div class="highlight"><pre><span class="c"># Durus example 1 - File-based persistent dictionary</span>
<span class="kn">from</span> <span class="nn">durus.file_storage</span> <span class="kn">import</span> <span class="n">FileStorage</span>
<span class="kn">from</span> <span class="nn">durus.connection</span> <span class="kn">import</span> <span class="n">Connection</span>

<span class="n">conn</span> <span class="o">=</span> <span class="n">Connection</span><span class="p">(</span><span class="n">FileStorage</span><span class="p">(</span><span class="s">&#39;test.durus&#39;</span><span class="p">))</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">get_root</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="o">=</span> <span class="n">v</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
<p>Running it:</p>
<pre class="literal-block">
$ time python durus-test.py
real        0m0.197s
user        0m0.187s
sys         0m0.008s
</pre>
<p>Now lets change to client-server operation, delivering more or less the same abilities as PyTyrant/Tokyo cabinet. A minor change to <tt class="docutils literal"><span class="pre">durus-test.py</span></tt> gives us a client:</p>
<div class="highlight"><pre><span class="c"># Durus example 2 - Remote access to a File-based persistent dictionary</span>
<span class="kn">from</span> <span class="nn">durus.client_storage</span> <span class="kn">import</span> <span class="n">ClientStorage</span>
<span class="kn">from</span> <span class="nn">durus.connection</span> <span class="kn">import</span> <span class="n">Connection</span>

<span class="n">conn</span> <span class="o">=</span> <span class="n">Connection</span><span class="p">(</span><span class="n">ClientStorage</span><span class="p">())</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">get_root</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="o">=</span> <span class="n">v</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
<p>In between each run we'll remove the database file. We'll need a server running, so in another terminal lets fire one up:</p>
<pre class="literal-block">
$ rm test.durus
$ durus -s --file test.durus
</pre>
<p>Run the second example:</p>
<pre class="literal-block">
$ time python durus-remote-test.py
real        0m0.204s
user        0m0.189s
sys         0m0.013s
</pre>
<p>Lets use a more advanced container than a persistent dictionary, a BTree. First Tokyo Cabinet/pytc:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">pytc</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">pytc</span><span class="o">.</span><span class="n">BDB</span><span class="p">()</span>
<span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s">&#39;test.db&#39;</span><span class="p">,</span>  <span class="n">pytc</span><span class="o">.</span><span class="n">BDBOWRITER</span> <span class="o">|</span> <span class="n">pytc</span><span class="o">.</span><span class="n">BDBOREADER</span> <span class="o">|</span> <span class="n">pytc</span><span class="o">.</span><span class="n">BDBOCREAT</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">db</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">v</span><span class="p">)</span>
        <span class="n">db</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
</pre></div>
<p>Running pytc with the BTree:</p>
<pre class="literal-block">
$ time python test.py

real    0m0.169s
user    0m0.157s
sys     0m0.011s
</pre>
<p>Nice and fast - its all C-based.</p>
<p>Now the Durus BTree code:</p>
<div class="highlight"><pre><span class="c"># Durus example 3 - File-based persistent BTree</span>
<span class="kn">from</span> <span class="nn">durus.file_storage</span> <span class="kn">import</span> <span class="n">FileStorage</span>
<span class="kn">from</span> <span class="nn">durus.connection</span> <span class="kn">import</span> <span class="n">Connection</span>
<span class="kn">from</span> <span class="nn">durus.btree</span> <span class="kn">import</span> <span class="n">BTree</span>

<span class="n">conn</span> <span class="o">=</span> <span class="n">Connection</span><span class="p">(</span><span class="n">FileStorage</span><span class="p">(</span><span class="s">&#39;test.durus&#39;</span><span class="p">))</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">get_root</span><span class="p">()</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">BTree</span><span class="p">()</span>
<span class="n">root</span><span class="p">[</span><span class="s">&#39;db&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">db</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="o">=</span> <span class="n">v</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
<p>Running this we see a significant performance delta compared to the C-based pytc/Tokyo Cabinet:</p>
<pre class="literal-block">
$ time python durus-btree.py
real        0m1.319s
user        0m1.308s
sys         0m0.011s
</pre>
<p>The delta will tip back into Durus's favour in the next two examples.</p>
<div class="highlight"><pre><span class="c"># Durus example 4 - client-server access to a persistent BTree</span>
<span class="kn">from</span> <span class="nn">durus.client_storage</span> <span class="kn">import</span> <span class="n">ClientStorage</span>
<span class="kn">from</span> <span class="nn">durus.connection</span> <span class="kn">import</span> <span class="n">Connection</span>
<span class="kn">from</span> <span class="nn">durus.btree</span> <span class="kn">import</span> <span class="n">BTree</span>

<span class="n">conn</span> <span class="o">=</span> <span class="n">Connection</span><span class="p">(</span><span class="n">ClientStorage</span><span class="p">())</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">get_root</span><span class="p">()</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">BTree</span><span class="p">()</span>
<span class="n">root</span><span class="p">[</span><span class="s">&#39;db&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">db</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="o">=</span> <span class="n">v</span>
        <span class="n">db</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
<p>First, the access the BTree-based &quot;db&quot; via client-server:</p>
<pre class="literal-block">
$ time python durus-remote-btree-adding.py
real        0m1.691s
user        0m1.681s
sys         0m0.010s
</pre>
<p>Next we see that read only access, remote or local, remains fast, even with the BTree structure:</p>
<pre class="literal-block">
$ time python durus-remote-btree-ro.py
real        0m0.054s
user        0m0.040s
sys         0m0.012s
</pre>
<p>PyTyrant / TokyoCabinet has a nice simple API to accessing the remote server:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">pytyrant</span>

<span class="n">t</span> <span class="o">=</span> <span class="n">pytyrant</span><span class="o">.</span><span class="n">PyTyrant</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">1978</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">256</span><span class="p">):</span>
    <span class="n">v</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</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="mf">256</span><span class="p">):</span>
        <span class="n">t</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="o">=</span> <span class="n">v</span>
        <span class="n">t</span><span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
</pre></div>
<p>PyTyrant client-server access to a BTree structure suggests future room for improvement:</p>
<pre class="literal-block">
$ time python pyt-test.py

real    0m11.151s
user    0m1.317s
sys     0m1.653s
</pre>
<p>Of course raw throughput isn't everything. Durus has persistent container types including Dictionary,
BTree, Set and Lists. Keys in mappings can be any hashable object; values can
be any pickleable object. Durus objects are Python objects, not merely strings or values.</p>
<p>Consider the following:</p>
<div class="highlight"><pre><span class="err">$</span> <span class="n">durus</span> <span class="o">-</span><span class="n">c</span>
<span class="n">Durus</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span><span class="p">:</span><span class="mf">2972</span>
    <span class="n">connection</span> <span class="o">-&gt;</span> <span class="n">the</span> <span class="n">Connection</span>
    <span class="n">root</span>       <span class="o">-&gt;</span> <span class="n">the</span> <span class="n">root</span> <span class="n">instance</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">durus.persistent_dict</span> <span class="kn">import</span> <span class="n">PersistentDict</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="n">PersistentDict</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;names&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">names</span>
<span class="o">&gt;&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;&gt;</span> <span class="n">mike</span> <span class="o">=</span> <span class="s">&#39;Mike Watkins&#39;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">fred</span> <span class="o">=</span> <span class="s">&#39;Fred Astaire&#39;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">ringo</span> <span class="o">=</span> <span class="s">&#39;Ringo Starr&#39;</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">mike</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span><span class="p">[</span><span class="mf">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">fred</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span><span class="p">[</span><span class="mf">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">ringo</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span><span class="p">[</span><span class="mf">22</span><span class="p">]</span> <span class="o">=</span> <span class="n">fred</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">id</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span>
<span class="mf">3082202976</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">id</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="mf">22</span><span class="p">])</span>
<span class="mf">3082202976</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">connection</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
<p>When we reconnect, we should expect the values within the mapping at keys 2 and 22 to be the same <em>object</em>:</p>
<div class="highlight"><pre><span class="err">$</span> <span class="n">durus</span> <span class="o">-</span><span class="n">c</span>
<span class="n">Durus</span> <span class="mf">127.0</span><span class="o">.</span><span class="mf">0.1</span><span class="p">:</span><span class="mf">2972</span>
    <span class="n">connection</span> <span class="o">-&gt;</span> <span class="n">the</span> <span class="n">Connection</span>
    <span class="n">root</span>       <span class="o">-&gt;</span> <span class="n">the</span> <span class="n">root</span> <span class="n">instance</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;names&#39;</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">id</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span>
<span class="mf">3081790720</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">id</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="mf">22</span><span class="p">])</span>
<span class="mf">3081790720</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">id</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span> <span class="o">==</span> <span class="nb">id</span><span class="p">(</span><span class="n">names</span><span class="p">[</span><span class="mf">22</span><span class="p">])</span>
<span class="bp">True</span>
</pre></div>
<p>Of late there seems to be plenty of interest in non-SQL database architectures -- CouchDB, Tokyo Cabinet among others getting attention, in part because they offer a language agnostic solution.</p>
<p>For those many other times when a project will benefit from a persistence layer tightly coupled with the language, object databases like Durus or ZODB are worthy of consideration.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:701</guid>
  <pubDate>Thu, 12 Mar 2009 04:54:48 GMT</pubDate>
  <category>durus</category>
  <category>python</category>
</item>
<item>
  <title>First Python 3 Web Application Framework?</title>
  <link>http://mikewatkins.ca/2008/12/03/first-python-3-web-application-framework/</link>
  <description><![CDATA[
<div class="document">
<p>From the <a class="reference external" href="http://mail.mems-exchange.org/durusmail/qp/439/">QP mailing list, Wednesday December 3 2008</a>:</p>
<blockquote>
<p>Today the MEMS Exchange released updates of 5 software packages: Durus, QP, Qpy, Sancho, and Dulcinea.</p>
<p>You can find details and downloads at the usual location: <a class="reference external" href="http://www.mems-exchange.org/software/">http://www.mems-exchange.org/software/</a></p>
<p>These packages require Python 2.4 or higher, and yes, they even work with Python 3.0.</p>
</blockquote>
<p>It does seem that perhaps <tt class="docutils literal"><span class="pre">QP</span> <span class="pre">2.1</span></tt> and friends is among the first if not actually the first web and database development packages available on <a class="reference external" href="http://python.org/download/releases/3.0/">Python 3.0</a> which was released today.</p>
<p>(Previously I've written about QP's templating system, Qpy, and a performance <a class="reference external" href="http://mikewatkins.ca/2008/11/02/python-30-templating-observations/">increase</a> moving from Python 2.5 to 3.0.)</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:671</guid>
  <pubDate>Thu, 04 Dec 2008 01:44:48 GMT</pubDate>
  <category>durus</category>
  <category>python</category>
  <category>qp</category>
</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">
<p>The attached image is a screen shot of a web browser running on a <a class="reference external" 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 external" 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 external" 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 external" href="http://www.mems-exchange.org/software/qp/">QP</a> and <a class="reference external" href="http://www.mems-exchange.org/software/durus/">Durus</a> - the app is merely a template provided by running <a class="reference external" 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 external" 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>
  <category>python</category>
  <category>qp</category>
  <category>tablet</category>
  <category>durus</category>
</item>
<item>
  <title>Python Database Interfaces</title>
  <link>http://mikewatkins.ca/2007/07/12/python-database-interfaces/</link>
  <description><![CDATA[
<div class="document">
<p><strong>Python object databases need some love too</strong></p>
<p>Flávio Coelho recently performed an examination of various Python database API
and ORM interfaces to MySQL, Postgres, and SQLite, and included a benchmark
for <a class="reference external" href="http://docs.python.org/lib/module-cPickle.html">cPickle</a>.</p>
<p>Here's an addition to Flávio's  <a class="reference external" href="http://pyinsci.blogspot.com/2007/07/fastest-python-database-interface.html">Fastest Python Database Interface</a> article and
script to include Durus: <a class="reference external" href="http://64.21.147.49/durus/performance.py">performance.py</a>.</p>
<p>I also pointed out on Flávio's blog that his cPickle benchmark needed to
include pickling 100,000 &quot;Person&quot; classes, in addition to 100,000 simple
tuples - this to show the overhead of class instantiation and serialization /
deserialization which all of the ORM's and object databases share in some form
or another. An example of both can be found in <a class="reference external" href="http://64.21.147.49/durus/performance.py">performance.py</a>.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:478</guid>
  <pubDate>Thu, 12 Jul 2007 16:47:00 GMT</pubDate>
  <category>durus</category>
  <category>python</category>
</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 external" href="/tags/python/2007-06-06-11-51.html">part five</a> of this series we dove deep into <a class="reference external" 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 external" 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 external" 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 external" 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 external" 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 external" 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" id="durus-the-database-you-already-know">
<h2>Durus, the database you already know</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 external" href="http://python.org/">Python</a> community, there is a long history of kinship with object
databases with <a class="reference external" 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 external" 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 external" 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" id="demonstrating-durus-interactively">
<h3>Demonstrating Durus, Interactively</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 external" 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 external" 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="kn">from</span> <span class="nn">durus.persistent_dict</span> <span class="kn">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="mf">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="mf">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="mf">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 external" 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" id="durus-mini-faq">
<h2>Durus Mini FAQ</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" id="entries-with-no-home">
<h2>Entries with no home</h2>
<p>In <a class="reference external" 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 external" 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 external" 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 external" 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="kn">from</span> <span class="nn">dulcinea.base</span> <span class="kn">import</span> <span class="n">DulcineaPersistent</span>
<span class="kn">from</span> <span class="nn">dulcinea.sort</span> <span class="kn">import</span> <span class="n">attr_sort</span>
<span class="kn">from</span> <span class="nn">qp.lib.keep</span> <span class="kn">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="kn">from</span> <span class="nn">qp.lib.spec</span> <span class="kn">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="kn">from</span> <span class="nn">qp.lib.spec</span> <span class="kn">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="kn">from</span> <span class="nn">qp.pub.user</span> <span class="kn">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 external" href="http://www.mems-exchange.org/software/qp/">QP</a> and <a class="reference external" 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="kn">from</span> <span class="nn">parlez.journal</span> <span class="kn">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="mf">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" id="pyblosxom-to-journal-conversion">
<h2>PyBlosxom to Journal Conversion</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 external" 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 external" href="http://www.python.org/pypi/textile/">Textile</a> formatted with a sprinkling of <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reST</a> and
<a class="reference external" 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" id="importing-data-to-durus">
<h3>Importing data to Durus</h3>
<p>Working with a QP application's Durus database is easy - remember, its just
Python.</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">qp.lib.site</span> <span class="kn">import</span> <span class="n">Site</span>
<span class="kn">from</span> <span class="nn">parlez.journal</span> <span class="kn">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" id="next-installment">
<h2>Next Installment</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>
  <category>durus</category>
  <category>python</category>
  <category>qp</category>
  <category>tutorial</category>
</item>
</channel></rss>
