<?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/rest/</link>
  <atom:link href="http://mikewatkins.ca/tags/rest/feeds/rss" type="application/rss+xml" rel="self"/>
  <lastBuildDate>Thu, 22 Jan 2009 23:07:58 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>Resty applications in QP, Part III</title>
  <link>http://mikewatkins.ca/2009/01/22/resty-applications-in-qp-part-iii/</link>
  <description><![CDATA[
<div class="document">
<p><strong>Traversing the URI Path in QP Applications</strong></p>
<p>In order to complete the Songs application we should understand how QP's
default traversal mechanism makes it easy to provide a navigable UI for our
objects we want to expose to the web.</p>
<p>In the last article in this series we saw through a <em>hello, world</em> example
that a QP application has two key classes, a <tt class="docutils literal"><span class="pre">SitePublisher</span></tt> and a
<tt class="docutils literal"><span class="pre">SiteDirectory</span></tt>, and that SiteDirectory exposes the <em>root</em> UI of a QP
application. Let's flesh out a HTML interface to go along with the REST API
offered by the Songs application, to include:</p>
<pre class="literal-block">
URI segment     Name    Returns to web browser
(export)
-------------------------------------------------------------
class SiteDirectory
/               index   HTML listing of songs
/new            new     HTML form / creation of new song
/delete         clear   HTML form allowing deletion of all songs
</pre>
<p>The functionality at these URI segments will be provided by SiteDirectory:</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;Song List&#39;</span><span class="p">,</span> <span class="s">&#39;This is the root / page of this site.&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;api&#39;</span><span class="p">,</span> <span class="s">&#39;api&#39;</span><span class="p">,</span> <span class="s">&#39;API&#39;</span><span class="p">,</span> <span class="s">&#39;API documentation and entry point&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="s">&#39;New&#39;</span><span class="p">,</span> <span class="s">&#39;Add a song to collection&#39;</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;clear&#39;</span><span class="p">,</span> <span class="s">&#39;Delete&#39;</span><span class="p">,</span> <span class="s">&#39;Delete all songs from collection&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">index</span><span class="p">:</span><span class="n">xml</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">header</span><span class="p">(</span><span class="s">&#39;Song Database&#39;</span><span class="p">)</span>
        <span class="s">&#39;&lt;h1&gt;Songs&lt;/h1&gt;&#39;</span>
        <span class="s">&#39;&lt;ul&gt;&#39;</span>
        <span class="k">for</span> <span class="n">song</span> <span class="ow">in</span> <span class="n">get_song_db</span><span class="p">()</span><span class="o">.</span><span class="n">itervalues</span><span class="p">():</span>
            <span class="s">&#39;&lt;li&gt;</span><span class="si">%s</span><span class="s">: &lt;a href=&quot;</span><span class="si">%s</span><span class="s">/&quot;&gt;</span><span class="si">%s</span><span class="s">&lt;/a&gt;&lt;/li&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span>
                <span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="n">song</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
        <span class="s">&#39;&lt;/ul&gt;&#39;</span>
        <span class="n">footer</span><span class="p">()</span>

    <span class="c"># ... for the forms examples provided by new/delete</span>
    <span class="c"># read the SongDirectory class at the end of  this article</span>
</pre></div>
<p>What of the API? Thanks to content-negotiation capability provided by the
<tt class="docutils literal"><span class="pre">Resource</span></tt> subclass of <tt class="docutils literal"><span class="pre">Directory</span></tt>, we <em>could</em> intermingle our API
functionality at the same URI as our HTML UI. Emphasis is on the word <em>could</em>
because from this point forward, purely for the sake of clarity in this and
subsequent articles on this theme, I'm going to risk angering the RESTafarian
gods and instead <em>host</em> the entire REST API under the <tt class="docutils literal"><span class="pre">/api/</span></tt> url segment.
To the SiteDirectory class above we'll merely add an attribute and an entry in
<tt class="docutils literal"><span class="pre">get_exports()</span></tt>:</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;Song List&#39;</span><span class="p">,</span> <span class="s">&#39;This is the root / page of this site.&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="s">&#39;New&#39;</span><span class="p">,</span> <span class="s">&#39;Add a song to collection&#39;</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;clear&#39;</span><span class="p">,</span> <span class="s">&#39;Delete&#39;</span><span class="p">,</span> <span class="s">&#39;Delete all songs from collection&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;api&#39;</span><span class="p">,</span> <span class="s">&#39;api&#39;</span><span class="p">,</span> <span class="s">&#39;API&#39;</span><span class="p">,</span> <span class="s">&#39;API documentation and entry point&#39;</span><span class="p">)</span>

    <span class="n">api</span> <span class="o">=</span> <span class="n">SongDatabaseResource</span><span class="p">()</span>

    <span class="c"># ...</span>
</pre></div>
<p>Therefore our API will be accessible via the <tt class="docutils literal"><span class="pre">SongDatabaseResource</span></tt> subclass
(<a class="reference external" href="http://mikewatkins.ca/2009/01/18/resty-applications-in-qp/">first discussed here</a>) of <tt class="docutils literal"><span class="pre">Resource</span></tt> and will expose the following:</p>
<pre class="literal-block">
URI      method  accept              Return
-------------------------------------------------------------
class SongDatabaseResource
/api/    GET     application/json    json dict of all songs
/api/    DELETE  application/json    json {'result': 'true'} on success
/api/    POST    application/json    json dict of new song data
</pre>
<p>Architectural style purity aside, there are reasons in the practical world why
one might wish to bury an API, even a REST API, behind a mount point as we've
done. The busy developer with security (or marketing) concerns might want to
only expose the API to authenticated users. Let's do that:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">SongDatabaseResource</span><span class="p">(</span><span class="n">Resource</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">get_resources</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">ensure_signed_in</span><span class="p">()</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;__api__&#39;</span><span class="p">)</span> <span class="c"># docs for web browsers</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;dump&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s">&#39;application/json&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;POST&#39;</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s">&#39;application/json&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;clear&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;DELETE&#39;</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s">&#39;application/json&#39;</span><span class="p">)</span>

    <span class="c"># ...</span>
</pre></div>
<p>OK, we sketched out the first <tt class="docutils literal"><span class="pre">/</span></tt> of the HTML UI. How do we traverse to
individual songs?</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;Song List&#39;</span><span class="p">,</span> <span class="s">&#39;This is the root / page of this site.&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;api&#39;</span><span class="p">,</span> <span class="s">&#39;api&#39;</span><span class="p">,</span> <span class="s">&#39;API&#39;</span><span class="p">,</span> <span class="s">&#39;API documentation and entry point&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="s">&#39;New&#39;</span><span class="p">,</span> <span class="s">&#39;Add a song to collection&#39;</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;clear&#39;</span><span class="p">,</span> <span class="s">&#39;Delete&#39;</span><span class="p">,</span> <span class="s">&#39;Delete all songs from collection&#39;</span><span class="p">)</span>

    <span class="c"># ...</span>

    <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">component</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="n">song</span> <span class="o">=</span> <span class="n">get_song_db</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">component</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">song</span><span class="p">:</span>
                <span class="k">return</span> <span class="n">SongDirectory</span><span class="p">(</span><span class="n">song</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
            <span class="c"># None / an empty &#39;&#39; response being returned from _q_lookup</span>
            <span class="c"># will result in 404 not_found being delivered as the response.</span>
            <span class="k">return</span> <span class="bp">None</span>

    <span class="n">api</span> <span class="o">=</span> <span class="n">SongDatabaseResource</span><span class="p">()</span>
</pre></div>
<p>The important mechanism to note here is <tt class="docutils literal"><span class="pre">_q_lookup</span></tt>. The default QP
traversal mechanism (which may easily be changed) traverses Directory
subclasses looking for callables or objects. One can create arbitrarily complex
URI schemes with levels nested many deep, if that turns your crank.</p>
<p>The object traversal approach is a flexible mechanism, quite different than
mapping callables or objects to URIs via regular expressions.  But one could
do that in QP too, if you found a need to. If you need even more flexibility
you can override the <tt class="docutils literal"><span class="pre">Directory</span></tt> class <tt class="docutils literal"><span class="pre">_q_traverse</span></tt> method or dive into
the <tt class="docutils literal"><span class="pre">Publisher</span></tt> itself.</p>
<p>One benefit of this approach is others who may use these classes generally do
not need to concern themselves at all about where in their URI namespace they
deploy these UI components.</p>
<p>The attached file: <a class="reference external" href="http://mikewatkins.ca/2009/01/22/resty-applications-in-qp-part-iii/file/746a54037f9b/">song.qpy</a>, is a QPY template file containing a quick draft of the HTML UI.</p>
<p>This RESTy Songs database example has morphed into something of a QP
<em>howto</em>. For those who just want to see the end result, fear not, I see that
coming around the bend soon. It is time to tie it all together... tomorrow.</p>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:690</guid>
  <pubDate>Thu, 22 Jan 2009 23:07:58 GMT</pubDate>
  <category>python</category>
  <category>qp</category>
  <category>rest</category>
  <category>tutorial</category>
</item>
<item>
  <title>Resty applications in QP, Part II</title>
  <link>http://mikewatkins.ca/2009/01/21/resty-applications-in-qp-part-ii/</link>
  <description><![CDATA[
<div class="document">
<p>In this installment we'll take a break from the implementation started in the <a class="reference external" href="http://mikewatkins.ca/2009/01/18/resty-applications-in-qp/">post
previous</a> of a <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> based <em>RESTy</em> song database application
loosely following the meme which has arisen from Eric Florenzano's <a class="reference external" href="http://www.eflorenzano.com/blog/post/writing-blazing-fast-infinitely-scalable-pure-wsgi/">example
bare-metal WSGI implementation</a> of same. Today I want to point out some QP
features which are useful and perhaps a little unusual.</p>
<div class="contents topic" id="inside">
<p class="topic-title first">Inside</p>
<ul class="simple">
<li><a class="reference internal" href="#basics" id="id3">Basics</a></li>
<li><a class="reference internal" href="#qp-hello-world-prototype" id="id4">QP Hello World Prototype</a></li>
<li><a class="reference internal" href="#qpy-templates" id="id5">QPY Templates</a></li>
<li><a class="reference internal" href="#authentication" id="id6">Authentication</a></li>
</ul>
</div>
<div class="section" id="basics">
<h2><a class="toc-backref" href="#id3">Basics</a></h2>
<p>While QP may not be a <em>bare-metal</em> framework, it also isn't an
<em>mega-framework</em> encapsulating a ton of functionality. The designer's ethos
might well be extracted from the last line in QP's README:</p>
<blockquote class="pull-quote">
The abbreviation &quot;qp&quot; stands for &quot;quantum placet&quot;, the Latin phrase
meaning &quot;as much as you please&quot;.</blockquote>
<p>QP provides basic but solid building blocks for web applications out of the box,
including via its partner package <a class="reference external" href="http://www.mems-exchange.org/software/qpy/">QPY</a> high level protection against XSS
(cross site scripting) attacks and a novel and very fast Python templating
system. You'll also get a user authentication system including form, HTTP
Basic and HTTP Digest authentication.</p>
</div>
<div class="section" id="qp-hello-world-prototype">
<h2><a class="toc-backref" href="#id4">QP Hello World Prototype</a></h2>
<p>QP application's typically minimally consist of a SitePublisher and
SiteDirectory class. SitePublisher contains a configuration dictionary which
can be used for both the framework and your applications. Since we are using
the built-in Durus object database-backed User database for the Songs
application, we'll employ a <tt class="docutils literal"><span class="pre">DurusPublisher</span></tt> and instruct the <tt class="docutils literal"><span class="pre">qp</span></tt> site
management command line utility to start a web and Durus daemon:</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="mf">7023</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="mf">8023</span><span class="p">),</span>
        <span class="p">)</span>
</pre></div>
<p>The &quot;root directory&quot; of a typical QP application is a class named
SiteDirectory. A <em>hello world</em> application driven by the above publisher would
look like this:</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="c"># URI segment exported, method name, crumb, title</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;Home page of the site&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&#39;hello, world.&#39;</span>
</pre></div>
<p>That's it.</p>
<p>Starting a QP application using the built-in site management tool
is simple:</p>
<div class="highlight"><pre>% qp -u songs
</pre></div>
<p>QP includes all the facilities for starting and stopping web and other
processes. It uses a multi-process rather than multi-thread model and spawns
new worker processes (configurable) as needed. Multi-CPU / multi-core machines
can eek out significant performance with nothing but the base QP servers; for
a large site typically I'll put the QP application behind an Apache or
lighttpd web daemon (using either a proxy or SCGI configuration), and serve
the static content with those servers. QP can also play in the WSGI space -
look for a quick how-to in the next post.</p>
</div>
<div class="section" id="qpy-templates">
<h2><a class="toc-backref" href="#id5">QPY Templates</a></h2>
<p>Let us now demonstrate one of the cooler or unusual features of QP / QPY -
Python centric templating. Whether this model works for you or not depends a
lot on how you look at code and (x)HTML / text templates. Programming teams
who are also responsible for the site layout may find refreshing the QPY
approach of putting content-in-Python, as opposed to most templating packages
which focus on enabling Python-like languages within text / HTML / XMl
templates.</p>
<p>An example should make things clear. We'll write a method to say hello back to
us n-times based on url parameters <tt class="docutils literal"><span class="pre">age</span></tt> and <tt class="docutils literal"><span class="pre">name</span></tt>, to respond to a query
like:</p>
<pre class="literal-block">
curl http://localhost:8023/?age=3&amp;name=Mary
</pre>
<p>Instead of using <tt class="docutils literal"><span class="pre">return</span></tt> to send a string back to the Publisher, we use a
special notation on our method - <tt class="docutils literal"><span class="pre">:xml</span></tt>:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">qp.pub.common</span> <span class="kn">import</span> <span class="n">get_request</span>

<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">index</span><span class="p">:</span><span class="n">xml</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>

        <span class="n">context</span> <span class="o">=</span> <span class="n">get_request</span><span class="p">()</span><span class="o">.</span><span class="n">fields</span>
        <span class="sd">&#39;&#39;&#39;</span>
<span class="sd">        &lt;html&gt;</span>
<span class="sd">        &lt;head&gt;</span>
<span class="sd">            &lt;title&gt;Hello&lt;/title&gt;</span>
<span class="sd">        &lt;/head&gt;</span>
<span class="sd">        &lt;body&gt;</span>
<span class="sd">            &lt;ol&gt;</span>
<span class="sd">        &#39;&#39;&#39;</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="nb">int</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;age&#39;</span><span class="p">,</span> <span class="mf">1</span><span class="p">))):</span>
            <span class="s">&#39;&lt;li&gt;</span><span class="si">%(name)s</span><span class="s">, </span><span class="si">%(age)s</span><span class="s"> years old&lt;/li&gt;&#39;</span> <span class="o">%</span> <span class="n">context</span>
        <span class="sd">&#39;&#39;&#39;</span>
<span class="sd">            &lt;/ol&gt;</span>
<span class="sd">        &lt;/body&gt;</span>
<span class="sd">        &lt;/html&gt;</span>
<span class="sd">        &#39;&#39;&#39;</span>
</pre></div>
<p>When <tt class="docutils literal"><span class="pre">index</span></tt> is called the string literal will be completed and passed back
as a return value. Its a much cleaner looking approach than building up a
string.</p>
<p>The method descriptor <tt class="docutils literal"><span class="pre">:xml</span></tt> is enabled by QPY. There are two such
descriptors provided by QPY, <tt class="docutils literal"><span class="pre">:xml</span></tt> and <tt class="docutils literal"><span class="pre">:str</span></tt>. String literals within
<tt class="docutils literal"><span class="pre">:xml</span></tt> templates are cast as a <em>quote no more</em> type, while variable data passed
to the template (such as the <tt class="docutils literal"><span class="pre">context</span></tt> dictionary in the example) will be
escaped or quoted for safety once and only once as it is included in the template.
<tt class="docutils literal"><span class="pre">:str</span></tt> templates provide no automatic escaping and quoting.</p>
<p>The QPY templating system provides four major benefits:</p>
<ol class="arabic simple">
<li>You already know the language because it is pure Python.</li>
<li>Automatic protection from cross site scripting (XSS) attacks and
other malicious value injection.</li>
<li>Unicode sane and safe, like the rest of the QP stack.</li>
<li>QPY is uncomplicated and very fast.</li>
</ol>
<p>Speaking of Unicode, QP applications treat all text data internally as
Unicode. The default character set is <tt class="docutils literal"><span class="pre">utf-8</span></tt>. Writing QP / QPY applications
to run on either Python 2.x or 3.x requires no special Unicode gymnastics.</p>
<p>In addition, all parts of the QP stack - Durus, QPY and QP itself - are Python
3 compatible, today. QPY is packaged separately from QP to make it accessible
for other templating package authors to employ features such as its quote no
more class and Python extensions.</p>
</div>
<div class="section" id="authentication">
<h2><a class="toc-backref" href="#id6">Authentication</a></h2>
<p>QP's default authentication method is HTTP Digest; this can be changed by
trivially overriding the <tt class="docutils literal"><span class="pre">ensure_signed_in()</span></tt> method of Publisher. For the
SongDatabase API we won't alter the default since <tt class="docutils literal"><span class="pre">Form</span></tt> isn't workable for
a RESTful API, and using <tt class="docutils literal"><span class="pre">Digest</span></tt> over <tt class="docutils literal"><span class="pre">Basic</span></tt> gives us the ability to
offer at least authentication security without using <tt class="docutils literal"><span class="pre">HTTPS</span></tt>.</p>
<p>If we extended our <em>hello, world</em> prototype to provide some more functionality
if you are logged on user, and even more if you are an administrator, it would
look like this:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">qp.pub.common</span> <span class="kn">import</span> <span class="n">get_publisher</span><span class="p">,</span> <span class="n">get_user</span>

<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="c"># URI segment exported, method name, crumb, title</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;Home page of the site&#39;</span><span class="p">)</span>
        <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">ensure_signed_in</span><span class="p">()</span>
        <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;users&#39;</span><span class="p">,</span> <span class="s">&#39;users&#39;</span><span class="p">,</span> <span class="s">&#39;Users&#39;</span><span class="p">,</span> <span class="s">&#39;Pithy title here&#39;</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">get_user</span><span class="p">()</span><span class="o">.</span><span class="n">is_admin</span><span class="p">():</span>
            <span class="k">yield</span> <span class="p">(</span><span class="s">&#39;secrets&#39;</span><span class="p">,</span> <span class="s">&#39;top_secret&#39;</span><span class="p">,</span> <span class="s">&#39;Home&#39;</span><span class="p">,</span> <span class="s">&#39;Secrets&#39;</span><span class="p">,</span> <span class="s">&#39;Something intriguing&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&#39;hello, world.&#39;</span>

    <span class="k">def</span> <span class="nf">users</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&#39;hello, real user </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="n">get_user</span><span class="p">()</span><span class="o">.</span><span class="n">id</span>

    <span class="k">def</span> <span class="nf">top_secret</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&#39;the password is &quot;orange&quot;. Sssh!&#39;</span>
</pre></div>
<p>In the foregoing example an unauthenticated user will not even be able to
navigate to <tt class="docutils literal"><span class="pre">/users</span></tt> or <tt class="docutils literal"><span class="pre">/secrets</span></tt>. An authenticated user who isn't
also a site administrator will not be able to navigate to <tt class="docutils literal"><span class="pre">/secrets</span></tt>.</p>
<p>One could put the authentication checks within an exported method instead, but
the nice thing about using the <tt class="docutils literal"><span class="pre">get_exports</span></tt> mechanism is menu and crumb
generation routines also use the data yielded, so putting auth checks there is
a benefit if you prefer not to show your site users menus they don't currently
have authorization to access.</p>
<p>Coming up next, a quick note on running QP as a WSGI application, and we'll
get back to the Song application.</p>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:688</guid>
  <pubDate>Wed, 21 Jan 2009 19:42:32 GMT</pubDate>
  <category>python</category>
  <category>qp</category>
  <category>rest</category>
  <category>tutorial</category>
</item>
<item>
  <title>Resty applications in QP</title>
  <link>http://mikewatkins.ca/2009/01/18/resty-applications-in-qp/</link>
  <description><![CDATA[
<div class="document">
<p>There have of late been a number of articles posted to the Pythonosphere
describing approaches to delivering a REST-like or RESTful API. In keeping
with the meme Eric Florenzano started by posting his bare-metal no-framework
WSGI approach, to the growing list I'll add an entry for the <a class="reference external" href="http://www.mems-exchange.org/software/qp/">QP web
framework</a> and <a class="reference external" href="http://www.mems-exchange.org/software/durus/">Durus object database</a> tag-team.</p>
<p>Eric Florenzano's <a class="reference external" href="http://www.eflorenzano.com/blog/post/writing-blazing-fast-infinitely-scalable-pure-wsgi/">deliberately minimalist WSGI application</a> illustrating a
bare metal WSGI application with RESTful behaviour has drawn out from other
Python web application framework communities a number of useful comparisons:</p>
<ol class="arabic simple">
<li>Christian Wyglendowski's entry using <a class="reference external" href="http://blog.dowski.com/2009/01/08/http-utilities-with-cherrypy/">cherrypy</a></li>
<li>Tim Parkin's <a class="reference external" href="http://dev.timparkin.co.uk/2009/01/happy-medium-from-wsgi-to-cherrypy.html">restish approach</a></li>
<li><a class="reference external" href="http://faassen.n--tree.net/blog/view/weblog/2009/01/10/0">Grok showing real object persistence</a> by Martijn Faasen.</li>
<li>A <a class="reference external" href="http://dev.timparkin.co.uk/2009/01/werkzeug-and-werkzeugish-example.html">Werkzeug solution</a> also by Tim Parkin.</li>
<li>Carlos de la Guardia <a class="reference external" href="http://blog.delaguardia.com.mx/index.php?op=ViewArticle&amp;articleId=119&amp;blogId=1">provides a solution via repoze.bfg</a>,
which, like Grok, shows how Zope3 technologies can be used.</li>
</ol>
<p>I particularly identify with the CherryPy solution because QP's default
traversal mechanism feels similar - both use an object publishing metaphor that relies on traversal of classes rather than a <tt class="docutils literal"><span class="pre">routes</span></tt> or url regular expression approach.</p>
<p>QP inherited its traversal approach from its older cousin <a class="reference external" href="http://quixote.ca/">Quixote</a>, which in turn borrowed the object publishing metaphor from Zope oh so many years ago. Quixote was always a minimalistic framework without strong opinions; QP is a little more opinionated. While you can quite easily break away from QP's intentional affinity for Durus for object persistence, out of the box QP provides user and session handling all backed by the Durus object database. Despite offering much more capability than Quixote by way of the defaults and conventions chosen, QP, QPY (the modern equivalent to Quixote's PTL templating system) and Durus are small enough to be read through in a single sitting with ease.</p>
<p>Martijn's solution (and any Zope-solution by inference) is also somewhat
familiar to me because QP's default object persistence method is the Durus
Python object database. Durus can best be thought of as a simpler ZODB
(and ZEO) work-a-like.</p>
<p>Since Eric's goal was to see how many requests he could churn out of a
bare-metal WSGI application, here are some <em>relative</em> numbers on my
workstation, an old desktop running FreeBSD on a single CPU single-core. Even
on this suboptimal box the QP <tt class="docutils literal"><span class="pre">songs</span></tt> app can respond to several hundred
requests per second.</p>
<p>Relative Performance:</p>
<pre class="literal-block">
%       Application
---     -----------
100     Eric's bare metal WSGI dict-driven example as baseline
58      CherryPy read from dict-driven example, Spawning server
61      QP, read from dict-driven &quot;database&quot; example, QP native server
60      QP, read from Durus persistent object database, QP native server
42      QP, write to Durus persistent object database, QP native server
65      QP, same app/Durus, read, simple WSGI server
50      QP, same app/Durus, read, Spawning server
59      Repoze.bfd, Spawning server, based on numbers published by Carlos
??      Grok, identified by Martijn as being appox. 50% of Eric's app on
        his machine
</pre>
<p>Certainly in my own experience raw performance has never been an issue thus I doubt I would ever want to trade away a framework I was comfortable with to
write bare metal WSGI apps. Following along with the &quot;song&quot; meme,
I'll show in the next couple of posts one approach to structuring a
QP application, and some background information on QP and Durus.</p>
<div class="section" id="song-and-song-database-objects">
<h2>Song and Song Database Objects</h2>
<p>Let's see if we can't kill two birds with one stone and show a slightly
beefier version of Eric's simple dict-based &quot;database&quot; using nothing but plain
Python. I've added a Counter object for use in counting &quot;I heard it&quot; events
but also for use as a key generator for our &quot;database&quot;, much like an identity
column in a SQL database.</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">Counter</span><span class="p">(</span><span class="nb">object</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="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">=</span> <span class="mf">0</span>

    <span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">+=</span> <span class="mf">1</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">count</span>

<span class="k">class</span> <span class="nc">Song</span><span class="p">(</span><span class="nb">object</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">title</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">title</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>

<span class="k">class</span> <span class="nc">SongDatabase</span><span class="p">(</span><span class="nb">dict</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="bp">self</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">song</span><span class="p">):</span>
        <span class="n">song</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">counter</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">[</span><span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">song</span>
</pre></div>
<p>Later in this article with barely any additional code we'll convert these to be
full-fledged persistent Python objects. Peek ahead and you'll find: two import lines, class inheritance changes, and one trivial new line of actual code.</p>
<p>With this &quot;database&quot; in place lets write a class to expose our
database as a web service. Later we'll add some other features like
self-documentation and an HTML UI for humans.</p>
<p>I don't want to do my dispatching based on either the exported
<tt class="docutils literal"><span class="pre">name</span></tt> or the request method alone, so with a few lines of code I
implemented a more generalized Resource class, extending QP's default
<tt class="docutils literal"><span class="pre">Directory</span></tt> class.  QP's default traversal mechanism is being bent
slightly to take into account the HTTP request method and accept
header.</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">SongDatabaseResource</span><span class="p">(</span><span class="n">Resource</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">get_resources</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;dump&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s">&#39;application/json&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;new&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;POST&#39;</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s">&#39;application/json&#39;</span><span class="p">)</span>
        <span class="k">yield</span> <span class="n">export</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="s">&#39;clear&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;DELETE&#39;</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s">&#39;application/json&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">dump</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        curl  -X GET -H &#39;Accept: application/json&#39; http://127.0.0.1:8023/api/</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">data</span> <span class="o">=</span> <span class="p">{}</span>
        <span class="k">for</span> <span class="n">song</span> <span class="ow">in</span> <span class="n">get_song_db</span><span class="p">()</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
            <span class="n">data</span><span class="p">[</span><span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
                               <span class="n">title</span><span class="o">=</span><span class="n">song</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
                               <span class="n">count</span><span class="o">=</span><span class="n">song</span><span class="o">.</span><span class="n">counter</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        curl  -X DELETE -H &#39;Accept: application/json&#39; http://127.0.0.1:8023/api/</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">songs</span> <span class="o">=</span> <span class="n">get_song_db</span><span class="p">()</span>
        <span class="n">songs</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
        <span class="n">songs</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">new</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Example:</span>
<span class="sd">            curl  -X POST -d &#39;{&quot;title&quot;:&quot;Walking Contradiction&quot;}&#39; \</span>
<span class="sd">                  -H &quot;Content-type: application/json&quot; \</span>
<span class="sd">                  -H &#39;Accept: application/json&#39; http://127.0.0.1:8023/api/</span>
<span class="sd">            Returns:</span>
<span class="sd">                {&quot;count&quot;: 0, &quot;id&quot;: 3, &quot;title&quot;: &quot;Walking Contradiction&quot;}</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">get_request</span><span class="p">()</span><span class="o">.</span><span class="n">read_body</span><span class="p">()</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
            <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">respond</span><span class="p">(</span>
                <span class="s">&quot;Bad Request&quot;</span><span class="p">,</span> <span class="s">&quot;Malformed json data </span><span class="si">%r</span><span class="s">&quot;</span> <span class="o">%</span> <span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="mf">400</span><span class="p">)</span>
        <span class="n">song</span> <span class="o">=</span> <span class="n">Song</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;title&#39;</span><span class="p">))</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">get_song_db</span><span class="p">()</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">song</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">ValueError</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
            <span class="n">get_publisher</span><span class="p">()</span><span class="o">.</span><span class="n">respond</span><span class="p">(</span><span class="s">&quot;Bad Request&quot;</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">),</span> <span class="n">status</span><span class="o">=</span><span class="mf">400</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="nb">dict</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
                               <span class="n">title</span><span class="o">=</span><span class="n">song</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
                               <span class="n">count</span><span class="o">=</span><span class="n">song</span><span class="o">.</span><span class="n">counter</span><span class="o">.</span><span class="n">count</span><span class="p">))</span>
</pre></div>
<p>I've added a little more meat to the basic application meme but
hopefully not so much that the plot is lost.</p>
<p>So far we have a web service or api exposed at the logial root of our
web application. The &quot;database&quot; isn't persistent as yet, so lets fix
that now with a couple of imports, one line of new code, and a change
from the default Python <tt class="docutils literal"><span class="pre">object</span></tt> base class to persistent versions
of same. With these minor changes our objects are now full partners
in the Durus object database. Those familiar with ZODB will certainly
recognize this pattern right away.</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">durus.persistent</span> <span class="kn">import</span> <span class="n">PersistentObject</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="c"># persistent Python object              # regular volatile Python objects</span>
<span class="k">class</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">PersistentObject</span><span class="p">):</span>        <span class="c">#   class Counter(object):</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">#       def __init__(self):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">=</span> <span class="mf">0</span>                  <span class="c">#           self.count = 0</span>

    <span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>                     <span class="c">#       def next(self):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">+=</span> <span class="mf">1</span>                 <span class="c">#           self.count += 1</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">count</span>               <span class="c">#           return self.count</span>

<span class="k">class</span> <span class="nc">Song</span><span class="p">(</span><span class="n">PersistentObject</span><span class="p">):</span>           <span class="c">#   class Song(object):</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">title</span><span class="p">):</span>          <span class="c">#       def __init__(self, title):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="bp">None</span>                 <span class="c">#           self.key = None</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">title</span>              <span class="c">#           self.title = title</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>        <span class="c">#           self.counter = Counter()</span>

<span class="k">class</span> <span class="nc">SongDatabase</span><span class="p">(</span><span class="n">PersistentDict</span><span class="p">):</span>     <span class="c">#   class SongDatabase(dict):</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">#       def __init__(self):</span>
        <span class="n">PersistentDict</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="c">#           self.counter = Counter()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>
                                        <span class="c">#       def add(self, song):</span>
    <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">song</span><span class="p">):</span>                <span class="c">#           song.key = self.counter.next()</span>
        <span class="n">song</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">counter</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>  <span class="c">#           self[song.key] = song</span>
        <span class="bp">self</span><span class="p">[</span><span class="n">song</span><span class="o">.</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">song</span>
</pre></div>
<p>In my next post on this theme we'll implement the rest of the
SongDatabaseResource / SongResource RESTful API and some HTML UI for
humans, and I'll put all the code up and a live instance for a
while too.</p>
</div>
</div>

]]></description>
  <guid isPermaLink="false">tag:mikewatkins.ca,2007-10-10:journal:mw:entry:686</guid>
  <pubDate>Sun, 18 Jan 2009 23:01:02 GMT</pubDate>
  <category>python</category>
  <category>qp</category>
  <category>rest</category>
  <category>tutorial</category>
</item>
</channel></rss>
