<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Fazal Majid&#039;s low intensity weblog &#187; Python</title>
	<atom:link href="http://majid.info/blog/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://majid.info/blog</link>
	<description>Sporadic pontification</description>
	<lastBuildDate>Sat, 24 Mar 2012 16:49:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Clearing custom crop aspect ratios in Lightroom</title>
		<link>http://majid.info/blog/lightroom-crops/</link>
		<comments>http://majid.info/blog/lightroom-crops/#comments</comments>
		<pubDate>Sat, 04 Jun 2011 17:04:31 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Photo]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://majid.info/blog/?p=141242538</guid>
		<description><![CDATA[Lightroom&#8217;s crop tool allows you to constrain the aspect ratio to a proportion of your choice, e.g. to 4:3, defaulting to the same aspect ratio as the original. The last 5 or so custom crop aspect ratios are saved, but &#8230; <a href="http://majid.info/blog/lightroom-crops/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Lightroom&#8217;s crop tool allows you to constrain the aspect ratio to a proportion of your choice, e.g. to 4:3, defaulting to the same aspect ratio as the original. The last 5 or so custom crop aspect ratios are saved, but a minor annoyance is you are unable to clear the list.</p>
<p>Python on the Mac and SQLite to the rescue: this simple script  <a href="http://majid.info/blog/wp-content/uploads/2011/06/lraspect.zip">lraspect.zip</a> will reset them. If you use a non-default name for your Lightroom catalog, you will need to edit it. To run it, quit Lightroom and run the script. It will back up your catalog for you just in case.</p>
<p>Needless to say, I cannot be held liable if this script corrupts your catalog or eats your dog (who ate your homework), use at your own risk.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>, <span style="color: #dc143c;">os</span>, sqlite3
&nbsp;
<span style="color: #808080; font-style: italic;"># edit this to point to your LR3 catalog if you do not use the default location</span>
lrcat = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">expanduser</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'~/Pictures/Lightroom/Lightroom 3 Catalog.lrcat'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #dc143c;">os</span>.<span style="color: black;">system</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'cp -i &quot;%s&quot; &quot;%s.bak&quot;'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>lrcat, lrcat<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
db = sqlite3.<span style="color: black;">connect</span><span style="color: black;">&#40;</span>lrcat<span style="color: black;">&#41;</span>
c = db.<span style="color: black;">cursor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
c.<span style="color: black;">execute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;&quot;&quot;select value from Adobe_variablesTable
where name='Adobe_customCropAspects'&quot;&quot;&quot;</span><span style="color: black;">&#41;</span>
crops = c.<span style="color: black;">fetchone</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'aspect ratios:'</span>, crops
c.<span style="color: black;">execute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;&quot;&quot;update Adobe_variablesTable
set value='{}'
where name='Adobe_customCropAspects'&quot;&quot;&quot;</span><span style="color: black;">&#41;</span>
db.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Custom crop aspect ratios reset successfully'</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/lightroom-crops/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Just enough Weave</title>
		<link>http://majid.info/blog/just-enough-weave/</link>
		<comments>http://majid.info/blog/just-enough-weave/#comments</comments>
		<pubDate>Thu, 10 Sep 2009 00:36:07 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2009/09/09-1.html</guid>
		<description><![CDATA[Note: I am keeping this code around for historical purposes, but it has not worked since Weave 1.0 RC2. I created this because Mozilla&#8217;s public sync servers were initially quite unreliable, but they have remedied the situation and performance problems &#8230; <a href="http://majid.info/blog/just-enough-weave/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="background-color: yellow;">Note: I am keeping this code around for historical purposes, but it has not worked since Weave 1.0 RC2. I created this because Mozilla&#8217;s public sync servers were initially quite unreliable, but they have remedied the situation and performance problems are a thing of the past. I also learned the inner workings of Weave/Firefox Sync in the process, and am satisfied as to the security of the system. Since I no longer use Firefox myself, I do not expect to ever revive this project. Feel free to take it over, otherwise you are best served by using Mozilla&#8217;s cloud.</p>
<p>Like most of my readers, I use multiple computers: my Mac Pro at home, my MacBook Air when on the road, 3 desktop PCs at work, a number of virtual machines, and so on. I have Firefox installed on all of them. The <a href="http://labs.mozilla.com/weave/">Mozilla Weave</a> extension allows me to sync bookmarks, passwords et al between them. Weave encrypts this data before uploading it to the server, but I do not like to rely on third-party web services for mission-critical functions (my Mozilla server was down last Monday, for instance, due to the surge of traffic from people returning to work and performing a full sync against 0.5). Through Weave 0.5, I ran <a href="http://majid.info/blog/?p=334">my own instance</a> of the Mozilla public Weave server version 0.3. Unfortunately, Weave 0.6 requires server version 0.5 and I had to upgrade.</p>
<p>The open-source Weave server is implemented in PHP. It doesn&#8217;t require Apache compiled with mod_dav as early versions did (I prefer to run <a href="http://wiki.nginx.org/Main">nginx</a>), but it is still a fairly gnarly piece of code that is anything but plug-and-play. Somehow I had managed to get version 0.3 running on my home server, but no amount of blundering around got me to a usable state with 0.5. I ended up deciding to implement a minimalist Weave server in Python, as it seemed less painful than continuing to struggle with the Mozilla spaghetti code, which confusingly features multiple pieces of code that appear to do exactly the same thing in three different places. Famous last words&#8230;</p>
<p>Three days of hacking later, I managed to get it working. 200 or so lines of Python code replaced approximately 12,000 lines of PHP. Of course, I am not trying to reproduce an entire public cloud infrastructure like Mozilla&#8217;s, just enough for my own needs, using the &#8220;simplest thing that works&#8221; principle. Interestingly, the Mozilla code includes a vestigial Python reference implementation of a Weave server for testing purposes. It does not seem to have been working for a while, though. I used it as a starting point but ended up rewriting almost everything. Here are the simplifying hypotheses:</p>
<ul>
<li>My weave server is meant for a single user (my wife prefers Safari)</li>
<li>It does not implement authentication, logging or SSL encryption — it is meant to be used behind a nginx (or Apache) reverse proxy that will perform these functions.</li>
<li>It has no configuration file. There are just three variables to set at the top of the source file.</li>
<li>It does not implement the full server protocol, just the parts that are actually used by the extension today.</li>
<li>More controversially, it does not even implement persistence, keeping all data in RAM instead. Python running on Solaris is very reliable, and the expected uptime of the server is likely months on end. If the server fails, the Firefoxes will just have to perform a full sync and reconciliation. Fortunately, that has been much improved in Weave 0.6, so the cost is minimal. This could even be construed as a security feature, since there is no data on disk to be misplaced. It would take catastrophically losing all my browsers simultaneously to risk data loss. Short of California falling into the ocean, that&#8217;s not going to happen, and if it does, I probably have more pressing concerns&#8230;</li>
</ul>
<p>The code could be extended fairly easily to lift these hypotheses, e.g. adding persistence or multiple user support using SQLite, PostgreSQL or MySQL.</p>
<p>Here is the server itself, <a href="http://majid.info/blog/wp-content/uploads/2009/09/weave_server.py">weave_server.py</a>:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/local/bin/python</span>
<span style="color: #483d8b;">&quot;&quot;&quot;
  Based on tools/scripts/weave_server.py from
  http://hg.mozilla.org/labs/weave/
&nbsp;
  do the Simplest Thing That Can Work: just enough to get by with Weave 0.6
  - SSL, authentication and loggin are done by nginx or other reverse proxy
  - no persistence, in case of process failure do a full resync
  - only one user. If you need more, create multiple instances on different
    ports and use rewrite rules to route traffic to the right one
&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>, <span style="color: #dc143c;">time</span>, <span style="color: #dc143c;">logging</span>, <span style="color: #dc143c;">socket</span>, <span style="color: #dc143c;">urlparse</span>, <span style="color: #dc143c;">httplib</span>, <span style="color: #dc143c;">pprint</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
  <span style="color: #ff7700;font-weight:bold;">import</span> simplejson <span style="color: #ff7700;font-weight:bold;">as</span> json
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>:
  <span style="color: #ff7700;font-weight:bold;">import</span> json
<span style="color: #ff7700;font-weight:bold;">import</span> wsgiref.<span style="color: black;">simple_server</span>
&nbsp;
URL_BASE = <span style="color: #483d8b;">'https://your.server.name/'</span>
<span style="color: #808080; font-style: italic;">#BIND_IP = ''</span>
BIND_IP = <span style="color: #483d8b;">'127.0.0.1'</span>
DEFAULT_PORT = <span style="color: #ff4500;">8000</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> HttpResponse:
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #dc143c;">code</span>, content=<span style="color: #483d8b;">''</span>, content_type=<span style="color: #483d8b;">'text/plain'</span><span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: black;">status</span> = <span style="color: #483d8b;">'%s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #dc143c;">code</span>, <span style="color: #dc143c;">httplib</span>.<span style="color: black;">responses</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">code</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">headers</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Content-type'</span>, content_type<span style="color: black;">&#41;</span>,
                    <span style="color: black;">&#40;</span><span style="color: #483d8b;">'X-Weave-Timestamp'</span>, <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">content</span> = content <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">self</span>.<span style="color: black;">status</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> JsonResponse<span style="color: black;">&#40;</span>value<span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, value, content_type=<span style="color: #483d8b;">'application/json'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> HttpRequest:
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, environ<span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: black;">environ</span> = environ
    content_length = environ.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'CONTENT_LENGTH'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> content_length:
      stream = environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'wsgi.input'</span><span style="color: black;">&#93;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">contents</span> = stream.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>content_length<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">contents</span> = <span style="color: #483d8b;">''</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  <span style="color: #808080; font-style: italic;"># Weave rounds to 2 digits and so must we, otherwise rounding errors will</span>
  <span style="color: #808080; font-style: italic;"># influence the &quot;newer&quot; and &quot;older&quot; modifiers</span>
  <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">round</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> WeaveApp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  <span style="color: #483d8b;">&quot;&quot;&quot;WSGI app for the Weave server&quot;&quot;&quot;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span> = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> url_base<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;XXX should derive this automagically from self.request.environ&quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> URL_BASE
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> ts_col<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, col<span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'timestamps'</span>, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>col<span style="color: black;">&#93;</span> = <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> parse_url<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #ff7700;font-weight:bold;">not</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span>
    command, args = path.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, <span style="color: #ff4500;">4</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">3</span>:<span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> command, args
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> opts_test<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, opts<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'older'</span> <span style="color: #ff7700;font-weight:bold;">in</span> opts:
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">float</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#91;</span><span style="color: #483d8b;">'older'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__ge__</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #483d8b;">'newer'</span> <span style="color: #ff7700;font-weight:bold;">in</span> opts:
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">float</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#91;</span><span style="color: #483d8b;">'newer'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__le__</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff7700;font-weight:bold;">lambda</span> x: <span style="color: #008000;">True</span>
&nbsp;
  <span style="color: #808080; font-style: italic;"># HTTP method handlers</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_PUT<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
    col, key = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">assert</span> command == <span style="color: #483d8b;">'storage'</span>
    val = <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">contents</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> val<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'{'</span>:
      val = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
      val<span style="color: black;">&#91;</span><span style="color: #483d8b;">'modified'</span><span style="color: black;">&#93;</span> = timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      val = json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>val, sort_keys=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span> = val
    <span style="color: #008000;">self</span>.<span style="color: black;">ts_col</span><span style="color: black;">&#40;</span>col<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_POST<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
      status = <span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span>
      <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
        command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
        col = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        vals = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">contents</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> vals:
          val<span style="color: black;">&#91;</span><span style="color: #483d8b;">'modified'</span><span style="color: black;">&#93;</span> = timestamp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
          <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>val<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id'</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span> = json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">ts_col</span><span style="color: black;">&#40;</span>col<span style="color: black;">&#41;</span>
        status = <span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>
    <span style="color: #ff7700;font-weight:bold;">finally</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span>status<span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_DELETE<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">assert</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>
    response = HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">endswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/storage/0'</span><span style="color: black;">&#41;</span>:
      <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">clear</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
      command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
      col, key = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> key:
        opts = <span style="color: #dc143c;">urlparse</span>.<span style="color: black;">parse_qs</span><span style="color: black;">&#40;</span>environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'QUERY_STRING'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">test</span> = <span style="color: #008000;">self</span>.<span style="color: black;">opts_test</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#41;</span>
        col = <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> key <span style="color: #ff7700;font-weight:bold;">in</span> col.<span style="color: black;">keys</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
          <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">test</span><span style="color: black;">&#40;</span>json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>col<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'modified'</span>, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
            <span style="color: #dc143c;">logging</span>.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'DELETE %s key %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>path, key<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">del</span> col<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span>
      <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">try</span>:
          <span style="color: #ff7700;font-weight:bold;">del</span> <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span><span style="color: black;">&#91;</span>col<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">KeyError</span>:
          <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> response
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> _handle_GET<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/0.5/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1.0/'</span><span style="color: black;">&#41;</span>:
      command, args = <span style="color: #008000;">self</span>.<span style="color: black;">parse_url</span><span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">handle_storage</span><span style="color: black;">&#40;</span>command, args, path, environ<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/1/'</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, <span style="color: #008000;">self</span>.<span style="color: black;">url_base</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> path.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/state'</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, <span style="color: #dc143c;">pprint</span>.<span style="color: black;">pformat</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> handle_storage<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, command, args, path, environ<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> command == <span style="color: #483d8b;">'info'</span>:
      <span style="color: #ff7700;font-weight:bold;">if</span> args == <span style="color: #483d8b;">'collections'</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> JsonResponse<span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'timestamps'</span>, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> command == <span style="color: #483d8b;">'storage'</span>:
      <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'/'</span> <span style="color: #ff7700;font-weight:bold;">in</span> args:
        col, key = args.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">else</span>:
        col, key = args, <span style="color: #008000;">None</span>
      <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> key: <span style="color: #808080; font-style: italic;"># list output requested</span>
          opts = <span style="color: #dc143c;">urlparse</span>.<span style="color: black;">parse_qs</span><span style="color: black;">&#40;</span>environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'QUERY_STRING'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
          <span style="color: #dc143c;">test</span> = <span style="color: #008000;">self</span>.<span style="color: black;">opts_test</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#41;</span>
          result = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
          <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>.<span style="color: black;">itervalues</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
            val = json.<span style="color: black;">loads</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">test</span><span style="color: black;">&#40;</span>val.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'modified'</span>, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
              result.<span style="color: black;">append</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span>
          result = <span style="color: #008000;">sorted</span><span style="color: black;">&#40;</span>result,
                          key=<span style="color: #ff7700;font-weight:bold;">lambda</span> val: <span style="color: black;">&#40;</span>val.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'sortindex'</span><span style="color: black;">&#41;</span>,
                                           val.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'modified'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>,
                          reverse=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
          <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'limit'</span> <span style="color: #ff7700;font-weight:bold;">in</span> opts:
            result = result<span style="color: black;">&#91;</span>:<span style="color: #008000;">int</span><span style="color: black;">&#40;</span>opts<span style="color: black;">&#91;</span><span style="color: #483d8b;">'limit'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
          <span style="color: #dc143c;">logging</span>.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'result set len = %d'</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
          <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'application/newlines'</span> <span style="color: #ff7700;font-weight:bold;">in</span> environ.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'HTTP_ACCEPT'</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>:
            value = <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> result<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">OK</span>, value,
                                content_type=<span style="color: #483d8b;">'application/text'</span><span style="color: black;">&#41;</span>
          <span style="color: #ff7700;font-weight:bold;">else</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> JsonResponse<span style="color: black;">&#40;</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
          <span style="color: #ff7700;font-weight:bold;">return</span> JsonResponse<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">collections</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>col, <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">KeyError</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> key: <span style="color: #ff7700;font-weight:bold;">raise</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">NOT_FOUND</span>, <span style="color: #483d8b;">'&quot;record not found&quot;'</span>,
                            content_type=<span style="color: #483d8b;">'application/json'</span><span style="color: black;">&#41;</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> __process_handler<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, handler<span style="color: black;">&#41;</span>:
    path = <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">environ</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'PATH_INFO'</span><span style="color: black;">&#93;</span>
    response = handler<span style="color: black;">&#40;</span>path, <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">environ</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> response
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, environ, start_response<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Main WSGI application method&quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">request</span> = HttpRequest<span style="color: black;">&#40;</span>environ<span style="color: black;">&#41;</span>
    method = <span style="color: #483d8b;">'_handle_%s'</span> <span style="color: #66cc66;">%</span> environ<span style="color: black;">&#91;</span><span style="color: #483d8b;">'REQUEST_METHOD'</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># See if we have a method called 'handle_METHOD', where</span>
    <span style="color: #808080; font-style: italic;"># METHOD is the name of the HTTP method to call.  If we do,</span>
    <span style="color: #808080; font-style: italic;"># then call it.</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">hasattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, method<span style="color: black;">&#41;</span>:
      handler = <span style="color: #008000;">getattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, method<span style="color: black;">&#41;</span>
      response = <span style="color: #008000;">self</span>.__process_handler<span style="color: black;">&#40;</span>handler<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      response = HttpResponse<span style="color: black;">&#40;</span><span style="color: #dc143c;">httplib</span>.<span style="color: black;">METHOD_NOT_ALLOWED</span>,
                              <span style="color: #483d8b;">'Method %s is not yet implemented.'</span> <span style="color: #66cc66;">%</span> method<span style="color: black;">&#41;</span>
&nbsp;
    start_response<span style="color: black;">&#40;</span>response.<span style="color: black;">status</span>, response.<span style="color: black;">headers</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#91;</span>response.<span style="color: black;">content</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> NoLogging<span style="color: black;">&#40;</span>wsgiref.<span style="color: black;">simple_server</span>.<span style="color: black;">WSGIRequestHandler</span><span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">def</span> log_request<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #66cc66;">*</span>args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
  <span style="color: #dc143c;">socket</span>.<span style="color: black;">setdefaulttimeout</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">300</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #483d8b;">'-v'</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">DEBUG</span><span style="color: black;">&#41;</span>
    handler_class = wsgiref.<span style="color: black;">simple_server</span>.<span style="color: black;">WSGIRequestHandler</span>
  <span style="color: #ff7700;font-weight:bold;">else</span>:
    <span style="color: #dc143c;">logging</span>.<span style="color: black;">basicConfig</span><span style="color: black;">&#40;</span>level=<span style="color: #dc143c;">logging</span>.<span style="color: black;">ERROR</span><span style="color: black;">&#41;</span>
    handler_class = NoLogging
  <span style="color: #dc143c;">logging</span>.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Serving on port %d.'</span> <span style="color: #66cc66;">%</span> DEFAULT_PORT<span style="color: black;">&#41;</span>
  app = WeaveApp<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  httpd = wsgiref.<span style="color: black;">simple_server</span>.<span style="color: black;">make_server</span><span style="color: black;">&#40;</span>BIND_IP, DEFAULT_PORT, app,
                                            handler_class=handler_class<span style="color: black;">&#41;</span>
  httpd.<span style="color: black;">serve_forever</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Here is the relevant fragment from my nginx configuration file:</p>

<div class="wp_syntax"><div class="code"><pre class="sh" style="font-family:monospace;"># Mozilla Weave
location /0.5 {
  auth_basic            &quot;Weave&quot;;
  auth_basic_user_file  /home/majid/web/conf/htpasswd.weave;
  proxy_pass            http://localhost:8000;
  proxy_set_header      Host $http_host;
}
location /1.0 {
  auth_basic            &quot;Weave&quot;;
  auth_basic_user_file  /home/majid/web/conf/htpasswd.weave;
  proxy_pass            http://localhost:8000;
  proxy_set_header      Host $http_host;
}
location /1/ {
  auth_basic            &quot;Weave&quot;;
  auth_basic_user_file  /home/majid/web/conf/htpasswd.weave;
  proxy_pass            http://localhost:8000;
  proxy_set_header      Host $http_host;
}</pre></div></div>

<p>This code is hereby released into the public domain. You are welcome to use it as you wish. Just keep in mind that since it is reverse-engineered, it may well break with future releases of the Weave extension, or if Mozilla changes the server protocol.</p>
<p>Update (2009-10-03):</p>
<p>I implemented some minor changes for compatibility with Weave 0.7. The diff with the previous version is as follows:</p>
<pre>--- weave_server.py~	Thu Sep  3 17:46:44 2009
+++ weave_server.py	Sat Oct  3 02:59:19 2009
@@ -65,8 +65,7 @@
     command, args = path.split('/', 4)[3:]
     return command, args

-  def opts_test(self, environ):
-    opts = urlparse.parse_qs(environ['QUERY_STRING'])
+  def opts_test(self, opts):
     if 'older' in opts:
       return float(opts['older'][0]).__ge__
     elif 'newer' in opts:
@@ -92,7 +91,7 @@
   def _handle_POST(self, path, environ):
     try:
       status = httplib.NOT_FOUND
-      if path.startswith('/0.5/') and path.endswith('/'):
+      if path.startswith('/0.5/'):
         command, args = self.parse_url(path)
         col = args.split('/')[0]
         vals = json.loads(self.request.contents)
@@ -113,7 +112,8 @@
       command, args = self.parse_url(path)
       col, key = args.split('/', 1)
       if not key:
-        test = self.opts_test(environ)
+        opts = urlparse.parse_qs(environ['QUERY_STRING'])
+        test = self.opts_test(opts)
         col = self.collections.setdefault(col, {})
         for key in col.keys():
           if test(json.loads(col[key]).get('modified', 0)):
@@ -142,10 +142,14 @@
       if args == 'collections':
         return JsonResponse(json.dumps(self.collections.get('timestamps', {})))
     if command == 'storage':
-      col, key = args.split('/')
+      if '/' in args:
+        col, key = args.split('/')
+      else:
+        col, key = args, None
       try:
         if not key: # list output requested
-          test = self.opts_test(environ)
+          opts = urlparse.parse_qs(environ['QUERY_STRING'])
+          test = self.opts_test(opts)
           result = []
           for val in self.collections.setdefault(col, {}).itervalues():
             val = json.loads(val)
@@ -155,6 +159,8 @@
                           key=lambda val: (val.get('sortindex'),
                                            val.get('modified')),
                           reverse=True)
+          if 'limit' in opts:
+            result = result[:int(opts['limit'][0])]
           logging.info('result set len = %d' % len(result))
           if 'application/newlines' in environ.get('HTTP_ACCEPT', ''):
             value = '\n'.join(json.dumps(val) for val in result)</pre>
<p>Update (2009-11-17):</p>
<p>Weave 1.0b1 uses 1.0 as the protocol version string instead of 0.5 but is otherwise unchanged. I updated the script and <tt>nginx</tt> configuration accordingly.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/just-enough-weave/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Inserting graphviz diagrams in a CVStrac wiki</title>
		<link>http://majid.info/blog/inserting-graphviz-diagrams-in-a-cvstrac-wiki/</link>
		<comments>http://majid.info/blog/inserting-graphviz-diagrams-in-a-cvstrac-wiki/#comments</comments>
		<pubDate>Wed, 22 Nov 2006 02:43:11 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[IT]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2006/11/21-1.html</guid>
		<description><![CDATA[CVStrac is an amazing productivity booster for any software development group. This simple tool, built around a SQLite database (indeed, by the author of SQLite) combines a bug-tracking database, a CVS browser and a wiki. The three components are fully &#8230; <a href="http://majid.info/blog/inserting-graphviz-diagrams-in-a-cvstrac-wiki/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.cvstrac.org/">CVStrac</a> is an amazing productivity booster for any software development group. This simple tool, built around a <a href="http://www.sqlite.org/">SQLite</a> database (indeed, by the author of SQLite) combines a bug-tracking database, a CVS browser and a wiki. The three components are fully cross-referenced and build off the strengths of each other. You can handle almost all aspects of the software development process in it, and since it is built on an open database with a radically simple schema, it is trivial to extend. I use CVStrac for <a href="http://www.temboz.com/temboz/">Temboz</a> to track bugs, but also to trace changes in the code base to requirements or to bugs, and last but not least, the wiki makes documentation a snap.</p>
<p>For historical reasons, my company uses <a href="http://twiki.org/">TWiki</a> for its wiki needs. We configured Apache with mod_rewrite so that the wiki links from CVStrac lead to the corresponding TWiki entry instead of the one in CVStrac itself, which is unused. TWiki is very messy (not surprising, as it is written in Perl), but it has a number of good features like excellent search (it even handles stemming) and a <a href="http://twiki.org/cgi-bin/view/Plugins/DirectedGraphPlugin">directed graph plug-in</a> that makes it easy to design complex graphs using Bell Labs&#8217; <a href="http://www.graphviz.org/">graphviz</a>, without having to deal with the tedious pixel-pushing of GUI tools like Visio or OmniGraffle. The plug-in makes it easy to document UML or E-R graphs, document software dependencies, map process flows and the like. </p>
<p>CVStrac 2.0 introduced extensibility in the wiki syntax via external programs. This allowed me to implement similar functionality in the CVStrac native wiki. To use it, you need to:</p>
<ol>
<li>Download the Python script <a href="http://majid.info/blog/wp-content/uploads/2006/11/dot.py">dot.py</a> and install it somewhere in your path. The sole dependency is graphviz itself, as well as either <a href="http://www.pysqlite.org/">pysqlite2</a> or the built-in version bundled with Python 2.5</li>
<li>create a custom wiki markup in the CVStrac setup, of type &#8220;Program Block&#8221;, with the formatter command-line:<br /> <em>path</em><tt>/dot.py --db </tt><em>CVStrac_database_file</em><tt> --name '%m'</tt>
<li>Insert the graphs using standard <tt>dot</tt> syntax, bracketed between CVStrac <tt>{dot}</tt> and <tt>{enddot}</tt> tags.</li>
</ol>
<p>For examples of the plugin at work, here is the graph corresponding to this markup:</p>
<pre>
{dot}
digraph sw_dependencies {
style=bold;
dpi=72;

temboz [fontcolor=white,style=filled,shape=box,fillcolor=red];
python [fontcolor=white,style=filled,fillcolor=blue];
cheetah [fontcolor=white,style=filled,fillcolor=blue];
sqlite [fontcolor=white,style=filled,fillcolor=blue];

temboz -> cheetah -> python;
temboz -> python -> sqlite -> gawk;
temboz -> cvstrac -> sqlite;
python -> readline;
python -> db4;
python -> openssl;
python -> tk -> tcl;

cvstrac -> "dot.py" -> graphviz -> tk;
"dot.py" -> python;
"dot.py" -> sqlite;
graphviz -> gdpng;
graphviz -> fontconfig -> freetype2;
fontconfig -> expat;
graphviz -> perl;
graphviz -> python;
gdpng -> libpng -> zlib;
gdpng -> freetype2;
}
{enddot}
</pre>
<p>  <center><img alt="Dot" src="http://majid.info/blog/wp-content/uploads/2006/11/239cb7dc22272e5969df34e5a526153f.png"></center>
<p>Another useful plug-in for CVStrac I wrote is one that highlights source code in the CVS browser using the <a href="http://pygments.pocoo.org/">Pygments</a> library. Simply download <a href="http://majid.info/blog/wp-content/uploads/2006/11/pygmentize.py">pygmentize.py</a>, install it Setup/Diff &amp; Filter Programs/File Filter, using the string <em>path_to</em><tt>/pygmentize.py %F</tt>. Here is an example of Pygment applied to <tt>pygmentize.py</tt> itself:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">&nbsp;
<span style="color: #808080; font-style: italic;">#!/usr/bin/env python</span>
<span style="color: #808080; font-style: italic;"># $Log: pygmentize.py,v $</span>
<span style="color: #808080; font-style: italic;"># Revision 1.3  2007/07/04 19:54:26  majid</span>
<span style="color: #808080; font-style: italic;"># cope with Unicode characters in source</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #808080; font-style: italic;"># Revision 1.2  2006/12/23 03:51:03  majid</span>
<span style="color: #808080; font-style: italic;"># import pygments.lexers and pygments.formatters explicitly due to Pygments 0.6</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #808080; font-style: italic;"># Revision 1.1  2006/12/05 20:19:57  majid</span>
<span style="color: #808080; font-style: italic;"># Initial revision</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #483d8b;">&quot;&quot;&quot;
CVStrac plugin to Pygmentize source code
&quot;&quot;&quot;</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>, pygments, pygments.<span style="color: black;">lexers</span>, pygments.<span style="color: black;">formatters</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#41;</span> == <span style="color: #ff4500;">2</span>
  block = <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdin</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">try</span>:
    lexer = pygments.<span style="color: black;">lexers</span>.<span style="color: black;">get_lexer_for_filename</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
    out = pygments.<span style="color: black;">highlight</span>
    block = pygments.<span style="color: black;">highlight</span><span style="color: black;">&#40;</span>
      block, lexer, pygments.<span style="color: black;">formatters</span>.<span style="color: black;">HtmlFormatter</span><span style="color: black;">&#40;</span>
      style=<span style="color: #483d8b;">'colorful'</span>, linenos=<span style="color: #008000;">True</span>, full=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ValueError</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">unicode</span><span style="color: black;">&#40;</span>block<span style="color: black;">&#41;</span>.<span style="color: black;">encode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ascii'</span>, <span style="color: #483d8b;">'xmlcharrefreplace'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
  main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/inserting-graphviz-diagrams-in-a-cvstrac-wiki/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Python driver for the Symbol CS 1504 bar code scanner</title>
		<link>http://majid.info/blog/a-python-driver-for-the-symbol-cs-1504-bar-code-scanner/</link>
		<comments>http://majid.info/blog/a-python-driver-for-the-symbol-cs-1504-bar-code-scanner/#comments</comments>
		<pubDate>Mon, 27 Mar 2006 09:15:24 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2006/03/27-1.html</guid>
		<description><![CDATA[One of my cousins works for Symbol, the world&#8217;s largest bar code reader manufacturer. The fashionable action today is in RFID, but the humble bar code is relatively untapped at the consumer level. The unexpected success of Delicious Library shows &#8230; <a href="http://majid.info/blog/a-python-driver-for-the-symbol-cs-1504-bar-code-scanner/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>One of my cousins works for Symbol, the world&#8217;s largest bar code reader manufacturer. The fashionable action today is in RFID, but the humble bar code is relatively untapped at the consumer level. The unexpected success of <a href="http://www.delicious-monster.com/">Delicious Library</a> shows people want to manage their collection of books, CDs and DVDs, and as with businesses, scanning bar codes is the fastest and least error-prone way to do so. Delicious Library supports scanning bar codes with an Apple iSight camera, but you have to wonder how reliable that is.</p>
<p>If you want something more reliable, you need a dedicated bar code scanner. They come in a bewildering array of sizes and shapes, from thin wands to pistol-like models or flat ones like those used at your supermarket checkout counter. For some reason, the bar code scanner world seems stuck in the era of serial ports (or worse, PS/2 keyboard wedges), but USB models are available, starting at $70 or so. They emulate a keyboard &#8211; when you scan a bar code, they will type in the code (as printed on the label), character by character so as to not overwhelm the application, and follow with a carriage return, which means they can work with almost anything from terminal-based applications to web pages. Ingeniously, most will allow you to program the reader&#8217;s settings using a booklet of special bar codes that perform changes like enabling or disabling ISBN decoding, and so on.</p>
<p>The problem with tethered bar code readers is, they are not very convenient if you are trying to catalog items on a bookshelf or read in UPC codes in a supermarket. Symbol has a unit buried deep inside its product catalog, the <a href="http://www.symbol.com/products/consumer_systems/consumer_cs1504.html">CS 1504 consumer scanner</a>. This tiny unit (shown below with a canister of 35mm film for size comparison) can be worn on a key chain, although I would worry about damaging the plastic window. Most bar code readers are hulking beasts in comparison. It has a laser bar code scanner: just align the line it projects with the bar code and it will chirp once it has read and memorized the code. The memory capacity is up to 150 bar code scans with timestamps, or 300 without timestamps. The 4 silver button batteries (included) are rated for 5000 scans &mdash; AAA would have been preferable, but I guess the unit wouldn&#8217;t be so compact, but it is clear this scanner was not intended for heavy-duty commercial inventory tracking purposes.</p>
<p>I bought one to simplify the process of listing books with <a href="http://www.bookcrossing.com/">BookCrossing</a> (even though their site is not optimized for bar code readers), but you have other interesting uses like finding out more about your daily purchases such as nutritional information or whether the company behind them engages in objectionable business practices. I can also imagine sticking preprinted bar-coded asset tracking tags on inventory (e.g. computers in the case of an IT department), and keeping track of them with this gizmo. People who sell a lot of books or used records through Amazon.com can also benefit as Amazon has a bulk listing service to which you can upload a file with barcodes. An interesting related service is the free <a href="http://www.upcdatabase.com/">UPC database</a>.</p>
<p> <center><img border="0" width="500" height="296" alt="Symbol CS 1504" src="http://majid.info/blog/wp-content/uploads/2006/03/cs1504.jpg" /></center>
<p>You can order the scanner in either serial ($100) or USB ($110) versions, significantly cheaper than the competition like Intelliscanner (and much smaller to boot). I highly recommend the USB version, even if you have a serial port today &mdash; serial ports seem to be going the way of the dodo and your next computer may not have one. The USB version costs slightly more, but that&#8217;s because they include a USB-Serial adapter, and you can&#8217;t get one retailing for a mere $10. The one shipped with my unit is the newer PN50 cable which uses a Prolific 2303 chipset rather than the older Digi adapter. Wonder of wonders, they even have a <a href="http://www.prolific.com.tw/eng/downloads.asp?ID=31">Mac OS X driver</a> available.</p>
<p>The scanner ships without any software. Symbol mostly sells through integrators to corporations that buy hundreds or thousands of bar code scanners for inventory or point of sale purposes, and they are not really geared to be a direct to consumer business with all the customer support hassles that entails. There are a number of programs available, mostly for Windows, but they don&#8217;t seem to have that much by way of functionality to justify their high prices, often as expensive as the scanner itself.</p>
<p>Symbol does make available a SDK to access the scanner, including complete documentation of the protocol used for the device. While you do have to register, they do not make you go through the ridiculous hoops you have to pass to access to the Photoshop plug-in SDK or the Canon RAW decoding SDK. The supplied libraries are Windows-only, however, so I wrote a <a href="http://www.majid.info/mylos/weblog/2006/03/cs1504.py">Python script</a> that works on both Windows and Mac OS X (and probably most UNIX implementations as well, although you will have to use a serial port). The only dependency is the <a href="http://pyserial.sourceforge.net/">pySerial</a> module.</p>
<p>By default, it will set the clock on the scanner, retrieve the recorded bar codes, correct the timestamps for any drift between the CS 1504&#8242;s internal clock and that of the host computer, and if successful clear the unit&#8217;s memory and dump the acquired bar codes in CSV format to standard output. The script will also decode ISBN codes (the CS 1504 does not appear to do this by itself in its default configuration). As it is written in Python, it can easily be extended, although it is probably easier to work off the CSV file.</p>
<p>The only configuration you have to do is set the serial port to use at the top of the script (it should do the right thing on a Mac using the Prolific driver, and the Windows driver seems to always use COM8 but I have no way of knowing if this is by design or coincidence). The program is still very rough, specially as concerns error recovery, and I appreciate any feedback.</p>
<p>A sample session follows:</p>
<pre>
ormag ~>python cs1504.py > barcodes.csv
Using device /dev/cu.usbserial...  connected
serial# 000100000003be95
SW version NBRIKAAE
reading clock for drift
clock drift 0:00:01.309451
resetting scanner clock... done
reading barcodes... done (2 read)
clearing barcodes... done
powering down... done

ormag ~>cat barcodes.csv
UPCA,034571575179,2006-03-27 01:08:48
ISBN,1892391198,2006-03-27 01:08:52
</pre>
<p>Update (2006-07-21):</p>
<p>At the prompting of some Windows users, I made a <a href="http://majid.info/blog/wp-content/uploads/2006/03/win_cs1504.py">slightly modified version</a> that will copy the barcodes to the clipboard, and also insert the symbology, barcode and timestamp starting on the first free line in the active Excel spreadsheet (creating one if necessary).</p>
<p>Update (2007-01-20):</p>
<p>Just to make it clear: I hereby place this code in the public domain.</p>
<p>Update (2009-11-06):</p>
<p>For Windows users, I have put up videos describing how to install the Prolific USB to serial driver, Python and requisite extensions, and how to use the program itself.</p>
<ul>
<li><a href="http://majid.info/blog/wp-content/uploads/2006/03/cs1504_win_prerequisites.mov">Installing prerequisites</a></li>
<li><a href="http://majid.info/blog/wp-content/uploads/2006/03/cs1504_win_serial.mov">Setting up the USB-Serial adapter</a></li>
<li><a href="http://majid.info/blog/wp-content/uploads/2006/03/cs1504_win_using.mov">Using the script</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/a-python-driver-for-the-symbol-cs-1504-bar-code-scanner/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>A reader-writer lock for Python</title>
		<link>http://majid.info/blog/a-reader-writer-lock-for-python/</link>
		<comments>http://majid.info/blog/a-reader-writer-lock-for-python/#comments</comments>
		<pubDate>Fri, 05 Nov 2004 05:22:25 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2004/11/04-1.html</guid>
		<description><![CDATA[Python offers a number of useful synchronization primitives in the threading and Queue modules. One that is missing, however, is a simple reader-writer lock (RWLock). A RWLock allows improved concurrency over a simple mutex, and is useful for objects that &#8230; <a href="http://majid.info/blog/a-reader-writer-lock-for-python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Python offers a number of useful synchronization primitives in the <a href="http://docs.python.org/lib/module-threading.html">threading</a> and <a href="http://docs.python.org/lib/module-Queue.html">Queue</a> modules. One that is missing, however, is a simple reader-writer lock (RWLock). A RWLock allows improved concurrency over a simple mutex, and is useful for objects that have high read-to-write ratios like database caches.</p>
<p>Surprisingly, I haven&#8217;t been able to find any implementation of these semantics, so I rolled my own in a module <a href="http://majid.info/blog/wp-content/uploads/2004/11/rwlock.py">rwlock.py</a> to implement a RWLock class, along with lock promotion/demotion. Hopefully it can be added to the standard library threading module. This code is hereby placed in the public domain.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">&nbsp;
<span style="color: #483d8b;">&quot;&quot;&quot;Simple reader-writer locks in Python
Many readers can hold the lock XOR one and only one writer&quot;&quot;&quot;</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">threading</span>
&nbsp;
version = <span style="color: #483d8b;">&quot;&quot;&quot;$Id: 04-1.html,v 1.3 2006/12/05 17:45:12 majid Exp $&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> RWLock:
  <span style="color: #483d8b;">&quot;&quot;&quot;
A simple reader-writer lock Several readers can hold the lock
simultaneously, XOR one writer. Write locks have priority over reads to
prevent write starvation.
&quot;&quot;&quot;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> = <span style="color: #ff4500;">0</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> = <span style="color: #ff4500;">0</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span> = <span style="color: #dc143c;">threading</span>.<span style="color: black;">Lock</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">readers_ok</span> = <span style="color: #dc143c;">threading</span>.<span style="color: black;">Condition</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">monitor</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">writers_ok</span> = <span style="color: #dc143c;">threading</span>.<span style="color: black;">Condition</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">monitor</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> acquire_read<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Acquire a read lock. Several threads can hold this typeof lock.
It is exclusive with write locks.&quot;&quot;&quot;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">readers_ok</span>.<span style="color: black;">wait</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> += <span style="color: #ff4500;">1</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> acquire_write<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Acquire a write lock. Only one thread can hold this lock, and
only when no read locks are also held.&quot;&quot;&quot;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">0</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> += <span style="color: #ff4500;">1</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_ok</span>.<span style="color: black;">wait</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> -= <span style="color: #ff4500;">1</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> = -<span style="color: #ff4500;">1</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> promote<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Promote an already-acquired read lock to a write lock
    WARNING: it is very easy to deadlock with this method&quot;&quot;&quot;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> -= <span style="color: #ff4500;">1</span>
    <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">0</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> += <span style="color: #ff4500;">1</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_ok</span>.<span style="color: black;">wait</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> -= <span style="color: #ff4500;">1</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> = -<span style="color: #ff4500;">1</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> demote<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Demote an already-acquired write lock to a read lock&quot;&quot;&quot;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> = <span style="color: #ff4500;">1</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">readers_ok</span>.<span style="color: black;">notifyAll</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">def</span> release<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Release a lock, whether read or write.&quot;&quot;&quot;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">0</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> = <span style="color: #ff4500;">0</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> -= <span style="color: #ff4500;">1</span>
    wake_writers = <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #008000;">self</span>.<span style="color: black;">rwlock</span> == <span style="color: #ff4500;">0</span>
    wake_readers = <span style="color: #008000;">self</span>.<span style="color: black;">writers_waiting</span> == <span style="color: #ff4500;">0</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">monitor</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> wake_writers:
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_ok</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_ok</span>.<span style="color: black;">notify</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">writers_ok</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> wake_readers:
      <span style="color: #008000;">self</span>.<span style="color: black;">readers_ok</span>.<span style="color: black;">acquire</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">readers_ok</span>.<span style="color: black;">notifyAll</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">readers_ok</span>.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
  <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">time</span>
  rwl = RWLock<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">class</span> Reader<span style="color: black;">&#40;</span><span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'start'</span>
      rwl.<span style="color: black;">acquire_read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'acquired'</span>
      <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'stop'</span>
      rwl.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">class</span> Writer<span style="color: black;">&#40;</span><span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'start'</span>
      rwl.<span style="color: black;">acquire_write</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'acquired'</span>
      <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'stop'</span>
      rwl.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">class</span> ReaderWriter<span style="color: black;">&#40;</span><span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'start'</span>
      rwl.<span style="color: black;">acquire_read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'acquired'</span>
      <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span>
      rwl.<span style="color: black;">promote</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'promoted'</span>
      <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'stop'</span>
      rwl.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">class</span> WriterReader<span style="color: black;">&#40;</span><span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'start'</span>
      rwl.<span style="color: black;">acquire_write</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'acquired'</span>
      <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'demoted'</span>
      rwl.<span style="color: black;">demote</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
      <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">self</span>, <span style="color: #483d8b;">'stop'</span>
      rwl.<span style="color: black;">release</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  Reader<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
  Reader<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
  ReaderWriter<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
  WriterReader<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
  Reader<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/a-reader-writer-lock-for-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Threadframe module version 0.2</title>
		<link>http://majid.info/blog/threadframe-module-version-0-2/</link>
		<comments>http://majid.info/blog/threadframe-module-version-0-2/#comments</comments>
		<pubDate>Fri, 11 Jun 2004 02:54:02 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2004/06/10-1.html</guid>
		<description><![CDATA[After a comments thread on a request in Wilfredo Sanchez&#8217; blog, including valuable contributions by Bob Ippolito, I have updated my threadframe module to leverage new thread tracking functionality added in Python 2.3. The module now allows Python 2.3 users &#8230; <a href="http://majid.info/blog/threadframe-module-version-0-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>After a <a href="http://www.wsanchez.net/MovableType/mt-comments.cgi?entry_id=69">comments thread</a> on a <a href="http://www.wsanchez.net/blog/archives/2004_06.html#000069">request</a> in Wilfredo Sanchez&#8217; blog, including valuable contributions by Bob Ippolito, I have updated my <a href="/blog/threadframe-multithreaded-stack-frame-extraction-for-python/">threadframe module</a> to leverage new thread tracking functionality added in Python 2.3. The module now allows Python 2.3 users to extract the stack frame of any given thread using its thread ID.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/threadframe-module-version-0-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Threadframe: multithreaded stack frame extraction for Python</title>
		<link>http://majid.info/blog/threadframe-multithreaded-stack-frame-extraction-for-python/</link>
		<comments>http://majid.info/blog/threadframe-multithreaded-stack-frame-extraction-for-python/#comments</comments>
		<pubDate>Fri, 11 Jun 2004 00:47:47 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>
		<category><![CDATA[Mylos longer articles]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/stories/2004/06/10/threadframe.html</guid>
		<description><![CDATA[Note: threadframe is obsolete. Python 2.5 and later include a function sys._current_frames() that does the same thing. Threadframe is only useful for Python 2.2 through 2.4. Rationale I was encountering deadlocks in a multi-threaded CORBA server (implemented using omniORB). Debugging &#8230; <a href="http://majid.info/blog/threadframe-multithreaded-stack-frame-extraction-for-python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="background-color: yellow">Note: threadframe is obsolete. Python 2.5 and later include a function <tt>sys._current_frames()</tt> that does the same thing. Threadframe is only useful for Python 2.2 through 2.4.</p>
<h2>Rationale</h2>
<p>I was encountering deadlocks in a multi-threaded CORBA server (implemented using omniORB). Debugging using GDB gave me too low-level information, and what I needed was an equivalent of the GDB command &#8220;info threads&#8221;. There was no such facility available from within Python&#8217;s standard library, so I rolled my own.</p>
<p>David Beazley added <a href="http://sourceforge.net/tracker/?func=detail&amp;atid=305470&amp;aid=436376&amp;group_id=5470"> advanced debugging functions</a> to the Python interpreter, and they have been folded into the 2.2 release.</p>
<p>I used these hooks to build a debugging module that is useful when you are looking for deadlocks in a multithreaded application. It basically has a single function that will return a list of the stack frames for all Python interpreter threads in the process.</p>
<p>Guido van Rossum <a href="http://cvs.sourceforge.net/viewcvs.py/python/python/dist/src/Include/pystate.h?r1=2.24&amp;r2=2.25">added in Python 2.3</a> the thread ID to the interpreter state structure, and this allows us to produce a dictionary mapping thread IDs to frames.</p>
<p>This functionality is now integrated in Python 2.5&#8242;s batteries-included <tt>sys._current_frames()</tt> function.</p>
<p>Of course, I disclaim any liability if this code should crash your system, erase your homework, eat your dog (who also ate your homework) or otherwise have any undesirable effect.</p>
<h2>Building and installing</h2>
<p>Python 2.2 or later is required. Thread ID to frame dictionary extraction is only available in Python 2.3 and later, and will generate a NotImplementedError if used from 2.2.</p>
<p> Download the source tarball <a href="http://www.majid.info/python/threadframe/threadframe-0.2.tar.gz">threadframe-0.2.tar.gz</a>. You can use the <a href="http://www.majid.info/python/threadframe/threadframe-0.2/Makefile">Makefile</a> or directly with the setup.py script.  I have built and tested this only on Solaris 8/x86 and Windows 2000, but the code should be pretty portable. There is a small test program <a href="http://www.majid.info/python/threadframe/threadframe-0.2/test.py">test.py</a> that illustrates how to use this module to dump stack frames of all the Python interpreter threads. A <a href="http://www.majid.info/python/threadframe/threadframe-0.2/sample.txt">sample run</a> is available for your perusal.</p>
<p>For Windows users, I have available pre-compiled binaries, built using <a href="http://www.mingw.org/">Mingw32</a> and GCC 2.95.2. Just copy the file threadframe.pyd in any location in your Python path and you should be able to run the test script <a href="http://www.majid.info/python/threadframe/threadframe-0.2/test.py">test.py</a>.</p>
<div align="center">
<table class="figure" summary="Windows binaries">
<caption>Windows binaries</caption>
<tr>
<th>Python version</th>
<th>Download</th>
</tr>
<tr>
<td>2.2.1</td>
<td><a href="http://www.majid.info/python/threadframe/win32/2.2/threadframe.pyd">threadframe.pyd</a></td>
</tr>
<tr>
<td>2.3.4</td>
<td><a href="http://www.majid.info/python/threadframe/win32/2.3/threadframe.pyd">threadframe.pyd</a></td>
</tr>
<tr>
<td>2.4.x</td>
<td><a href="http://www.majid.info/python/threadframe/win32/2.4/threadframe.pyd">threadframe.pyd</a></td>
</tr>
</table></div>
<h2>License</h2>
<p>This code is licensed under the <a href="http://www.python.org/psf/license/">same terms</a> as Python itself.</p>
<h2>Change history</h2>
<h3>Release 0.2 (2004-06-10)</h3>
<p>Distutils based setup.py contributed by <a href="http://bob.pycs.net/">Bob Ippolito</a>. Bob also noticed that thread_id was added to the Python interpreter state, and contributed a patch to get a dictionary mapping thread_ids to frames instead of a list.</p>
<h3>Release 0.1 (2002-10-11)</h3>
<p>Initial release for Python 2.2: <a href="http://www.majid.info/python/threadframe/threadframe-0.1.tar.gz">threadframe-0.1.tar.gz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/threadframe-multithreaded-stack-frame-extraction-for-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Temboz RSS aggregator</title>
		<link>http://majid.info/blog/the-temboz-rss-aggregator/</link>
		<comments>http://majid.info/blog/the-temboz-rss-aggregator/#comments</comments>
		<pubDate>Mon, 29 Mar 2004 07:42:02 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Temboz]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Mylos]]></category>
		<category><![CDATA[Mylos longer articles]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/stories/2004/03/29/temboz.html</guid>
		<description><![CDATA[Contents Introduction Features History Screen shots Known bugs Credits Download Updates Post scriptum Introduction Temboz is a RSS aggregator. It is inspired by FeedOnFeeds (web-based personal aggregator), Google News (two column layout) and TiVo (thumbs up and down). I have &#8230; <a href="http://majid.info/blog/the-temboz-rss-aggregator/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h2>Contents</h2>
<ul>
<li><a href="#intro">Introduction</a></li>
<li><a href="#features">Features</a></li>
<li><a href="#history">History</a></li>
<li><a href="#screenshots">Screen shots</a></li>
<li><a href="#bugs">Known bugs</a></li>
<li><a href="#credits">Credits</a></li>
<li><a href="#download">Download</a></li>
<li><a href="#updates">Updates</a></li>
<li><a href="#ps">Post scriptum</a></li>
</ul>
<h2><a id="intro" name="intro"></a>Introduction</h2>
<p>Temboz is a RSS aggregator. It is inspired by <a href="http://minutillo.com/steve/feedonfeeds/">FeedOnFeeds</a> (web-based personal aggregator), <a href="http://news.google.com/">Google News</a> (two column layout) and <a href="http://www.tivo.com/">TiVo</a> (thumbs up and down). I have been using FeedOnFeeds for some time now, but that software seems to have stopped evolving, and I had a number of optimizations to the user experience I wanted to make.</p>
<h2><a id="features" name="features"></a>Features</h2>
<p>Already implemented:</p>
<ul>
<li>Multithreaded, download feeds in parallel.</li>
<li>Built-in web server.</li>
<li>Two-column user interface for better readability and information density. Automatic reflow using CSS.</li>
<li>Ratings system for articles</li>
<li>Real-time hunter-gatherer user interface: items flagged with a &#8220;Thumbs down&#8221; disappear immediately off the screen (using Dynamic HTML), making room for new articles. No laborious flagging of items as in FeedOnFeeds.</li>
<li>Filtering entries (using Python syntax, e.g. <tt>'Salon' in feed_title and title == "King Kaufman's Sports Daily"</tt>, or simply by selecting keywords/phrases and hitting &#8220;Thumbs down&#8221;).</li>
<li>Ability to generate a RSS feeds from &#8220;Thumbs Up&#8221; articles, which is why Temboz would be a true aggregator, not just a reader.</li>
<li>Ad filtering</li>
<li>Automatic garbage collection: every day between 3AM and 4AM, uninteresting articles (by default those older than 7 days) are purged of their contents (but not metadata such as titles, permalinks or timestamps) to keep the database size manageable. After 6 months (by default), they are deleted altogether</li>
<li>Automatic database backups daily (immediately after garbage collection)</li>
</ul>
<p>On the to do list:</p>
<ul>
<li>Write better documentation</li>
<li>Handle permanent HTTP redirects for feed XML URLs</li>
<li>Automatic pacing of feed polling intervals using the average and standard deviation of observed feed item inter-arrival times, to reduce bandwidth usage and load for both client and server. Most feeds should be polled on a daily rather than hourly interval (e.g. my own, since I update once a week on average), but the mechanisms for a feed to indicate its polling rate preferences are quite inconsistent from one flavor of RSS/Atom to another.</li>
<li>&#8220;Survivor mode&#8221; &#8211; vote feeds that no longer perform off the aggregator based on relevance statistics.</li>
<li>Ability to cluster together articles (I tried a heuristic of looking for common URLs they are all pointing to, but this didn&#8217;t work well in practice).</li>
<li>Portability to Windows, distribution as a standalone package.</li>
</ul>
<h2><a id="history" name="history"></a>History</h2>
<p>I have been using it successfully for well over a year. It still has rough edges, with some administration functions only doable using the SQLite command-line utility. Here is a screen shot showing the reader user interface. The article highlighted in yellow was given a &#8220;Thumbs Up&#8221;. You can also see the user interface at work in a view of the <a href="http://www.majid.info/mylos/temboz.html">last 50 articles</a> I flagged as &#8220;thumbs up&#8221; among the feeds I read.</p>
<h2><a id="screenshots" name="screenshots"></a>Screen shots</h2>
<p><em>Click on a screen shot thumbnail for a full-sized version</em></p>
<p>The first screen shot shows the article reading interface, using a two-column layout. Clicking on the &#8220;Thumbs down&#8221; icon makes the article disappear, bringing a new one in its place (if available). Clicking on the &#8220;Thumbs up&#8221; icon highlights it in yello and flags it as interesting in the database.</p>
<p><a href="http://majid.info/blog/wp-content/uploads/2004/03/t1full.gif" target="_blank"><img src="http://majid.info/blog/wp-content/uploads/2004/03/t1.gif" border="0" alt="view items" width="450" height="432" /></a>The feed summary page shows statistics on feeds, starting with feeds with unread articles, then by alphabetical order. Feeds can be sorted based on other metrics. You have the option of &#8220;catching up&#8221; with a feed (marking all the articles as read). Feeds with errors are highlighted in red (not shown).</p>
<p><a href="http://majid.info/blog/wp-content/uploads/2004/03/t2full.gif" target="_blank"><img src="http://majid.info/blog/wp-content/uploads/2004/03/t2.gif" border="0" alt="view feeds" width="450" height="432" /></a>Clicking on the &#8220;details&#8221; link for a feed brings this page, which allows you to change title or feed URL, and shows the RSS or Atom fields accessible for filtering.</p>
<p><a href="http://majid.info/blog/wp-content/uploads/2004/03/t4full.gif" target="_blank"><img src="http://majid.info/blog/wp-content/uploads/2004/03/t4.gif" border="0" alt="feed details" width="450" height="432" /></a>Feeds can be filtered using Python expressions.</p>
<p><a href="http://majid.info/blog/wp-content/uploads/2004/03/t3full.gif" target="_blank"><img src="http://majid.info/blog/wp-content/uploads/2004/03/t3.gif" border="0" alt="filtering rules" width="450" height="432" /></a></p>
<h2><a id="bugs" name="bugs"></a>Known bugs</h2>
<p>You can check outstanding bug reports, change requests and more at the <a href="http://www.temboz.com/temboz/">public CVStrac</a> site.</p>
<h2><a id="credits" name="credits"></a>Credits</h2>
<p>Temboz is written in Python, and leverages Mark Pilgrim&#8217;s <a href="http://diveintomark.org/projects/feed_parser/">Ultra-liberal feed parser</a>, <a href="http://www.sqlite.org/">SQLite 2.x</a>, <a href="http://www.cheetahtemplate.org/">Cheetah</a>.</p>
<h2><a id="download" name="download"></a>Download</h2>
<p>You can download the current version: <a href="http://majid.info/temboz/temboz-0.8.tar.gz">temboz-0.8.tar.gz</a> I welcome any feedback you may have, specially as concerns improving installation.</p>
<p>The CVS version is far ahead of 0.8 in features. I have not yet had the time to test and document the migration procedure from 0.8 to 1.0, but if you are a new Temboz user I strongly advise you to get a nightly CVS snapshot instead (they are what I run on my own server): <a href="http://majid.info/temboz/temboz-CVS.tar.gz">temboz-CVS.tar.gz</a> or <a href="http://www.majid.info/temboz/temboz-CVS.zip">temboz-CVS.zip</a>.</p>
<h2><a id="updates" name="updates"></a>Updates</h2>
<p>For news on Temboz, please subscribe to the <a href="http://www.majid.info/mylos/weblog/categories/temboz/rss.xml">RSS feed</a>.</p>
<p>Temboz has a <a href="http://www.temboz.com/temboz/">CVStrac</a> where you can submit bug reports or change requests, and a Wiki, where all future documentation will ultimately reside.</p>
<h2><a id="ps" name="ps"></a>Post scriptum</h2>
<p>The name &#8220;Temboz&#8221; is a reference to Malima Temboz, &#8220;The mountain that walks&#8221;, an elephant whose tormented spirit is the object of Mike Resnick&#8217;s excellent SF novel, <a href="http://www.fictionwise.com/ebooks/eBook474.htm">Ivory</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/the-temboz-rss-aggregator/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Data mining Outlook for fun and profit</title>
		<link>http://majid.info/blog/data-mining-outlook-for-fun-and-profit/</link>
		<comments>http://majid.info/blog/data-mining-outlook-for-fun-and-profit/#comments</comments>
		<pubDate>Tue, 16 Mar 2004 00:18:57 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2004/03/15-1.html</guid>
		<description><![CDATA[For a few years now, I have owned the domain name majid.fm. Dot-fm stands for the Federated States of Micronesia, a micro-state in the Pacific Ocean, and they market their domain names to FM radio stations. Those are also my &#8230; <a href="http://majid.info/blog/data-mining-outlook-for-fun-and-profit/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>For a few years now, I have owned the domain name <tt>majid.fm</tt>. Dot-fm stands for the Federated States of Micronesia, a micro-state in the Pacific Ocean, and they market their domain names to FM radio stations. Those are also my initials. Unfortunately, the registration fees are quite expensive ($200 every two years), and the domain is redundant now that I have acquired <tt>majid.info</tt> and <tt>majid.org</tt> (<tt>majid.com</tt> is reserved by a Malaysian cybersquatter who is demanding a couple thousand dollars for it &#8211; I may be vain, but not that vain). I have decided to let the domain lapse when it expires on April 1st.</p>
<p>I used the majid-dot-FM domain for my emails, and set it up so emails sent to anything <tt>@majid.fm</tt> would be sent to my primary mailbox <tt>fazal@majid.fm</tt>. For instance, if I registered with Dell, I would give them the email address <tt>dell@majid.fm</tt>. This was helpful in tracing where I got my email from, and blacklisting companies that started spamming me (they shall remain nameless to protect the guilty yet litigious).</p>
<p>Unfortunately, spammers and some worms attempt dictionary attacks by trying all possible combinations like <tt>jim@majid.fm</tt>, <tt>smith@majid.fm</tt>, and so on. My spam filter would catch some, but not all of them, and it would be a terrible hassle. I do not want to have an auto-responder send emails back to people who email me at the old address, as this would at best flood innocent people whose addresses spammers are impersonating, and at worst actually give my new address to the spammers.</p>
<p>My solution to this dilemma is to produce a Python script that scans through all the emails in my Outlook personal folder (PST) files of archived emails, flag all those who sent me an email, and them manually send them a change of address notification (or in the case of websites and online stores, update my contact info online).</p>
<p>Simply using Outlook&#8217;s advanced search function will not work, as in many cases the <tt>To:</tt> header is set to something other than the address the email is delivered to, such as <tt>undisclosed-recipients</tt>, or the sender&#8217;s address when they send the email to multiple <tt>Bcc:</tt> recipients (the proper way to proceed when you want to send an email to multiple recipients without giving everyone in the list the email addresses of the other recipients). I actually have to sift through the raw message headers to see the envelope destination address.</p>
<p>Here is a simplified version of <a href="http://majid.info/blog/wp-content/uploads/2004/03/olmine.py">olmine.py</a>, the script I used. It requires Python 2.x with the win32all extensions, and Outlook 2000 with the Collaboration Data Objects (CDO) option installed (this is not the default). CDO is required to access the full headers. Of course, this script can be useful for all sorts of social network analysis fun on your own Outlook files, or more prosaically to generate a whitelist of email addresses for your spam filter.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">re</span>, win32com.<span style="color: black;">client</span>
&nbsp;
srcs = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
dsts = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
pairs = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># regular expression that scans for valid email addresses in the headers</span>
m_re = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'[-A-Za-z0-9.,_]*@majid<span style="color: #000099; font-weight: bold;">\.</span>fm'</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># regular expression that strips out headers that can cause false positives</span>
strip_re = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'(Message-Id:.*$|In-Reply-To:.*$|References:.*$)'</span>,
                      <span style="color: #dc143c;">re</span>.<span style="color: black;">IGNORECASE</span> | <span style="color: #dc143c;">re</span>.<span style="color: black;">MULTILINE</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> dump_folder<span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>:
  <span style="color: #483d8b;">&quot;&quot;&quot;Iterate recursively over the given folder and its subfolders&quot;&quot;&quot;</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'-'</span> <span style="color: #66cc66;">*</span> <span style="color: #ff4500;">72</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> folder.<span style="color: black;">Name</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'-'</span> <span style="color: #66cc66;">*</span> <span style="color: #ff4500;">72</span>
  <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, folder.<span style="color: black;">Messages</span>.<span style="color: black;">Count</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
      <span style="color: #808080; font-style: italic;"># PR_SENDER_EMAIL_ADDRESS</span>
      _from = folder.<span style="color: black;">Messages</span><span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.<span style="color: black;">Fields</span><span style="color: black;">&#91;</span>0x0C1F001F<span style="color: black;">&#93;</span>.<span style="color: black;">Value</span>
      <span style="color: #808080; font-style: italic;"># PR_TRANSPORT_MESSAGE_HEADERS</span>
      headers = folder.<span style="color: black;">Messages</span><span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>.<span style="color: black;">Fields</span><span style="color: black;">&#91;</span>0x7d001e<span style="color: black;">&#93;</span>.<span style="color: black;">Value</span>
    <span style="color: #ff7700;font-weight:bold;">except</span>:
      <span style="color: #808080; font-style: italic;"># ignore non-email objects like contacts or calendar entries</span>
      <span style="color: #ff7700;font-weight:bold;">continue</span>
    stripped_headers = strip_re.<span style="color: black;">sub</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>, headers<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> _to <span style="color: #ff7700;font-weight:bold;">in</span> m_re.<span style="color: black;">findall</span><span style="color: black;">&#40;</span>stripped_headers<span style="color: black;">&#41;</span>:
      srcs<span style="color: black;">&#91;</span>_from<span style="color: black;">&#93;</span> = srcs.<span style="color: black;">get</span><span style="color: black;">&#40;</span>_from, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span> + <span style="color: #ff4500;">1</span>
      dsts<span style="color: black;">&#91;</span>_to<span style="color: black;">&#93;</span> = dsts.<span style="color: black;">get</span><span style="color: black;">&#40;</span>_to, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span> + <span style="color: #ff4500;">1</span>
      <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span>_from, _to<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> pairs:
        <span style="color: #ff7700;font-weight:bold;">print</span> _from, <span style="color: #483d8b;">'-&gt;'</span>, _to
      pairs<span style="color: black;">&#91;</span>_from, _to<span style="color: black;">&#93;</span> = pairs.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>_from, _to<span style="color: black;">&#41;</span>, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span> + <span style="color: #ff4500;">1</span>
  <span style="color: #808080; font-style: italic;"># recurse</span>
  <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, folder.<span style="color: black;">Folders</span>.<span style="color: black;">Count</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
    dump_folder<span style="color: black;">&#40;</span>folder.<span style="color: black;">Folders</span><span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># connect to Outlook via CDO</span>
cdo = win32com.<span style="color: black;">client</span>.<span style="color: black;">Dispatch</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'MAPI.Session'</span><span style="color: black;">&#41;</span>
cdo.<span style="color: black;">Logon</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># iterate over all the open PST files</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, cdo.<span style="color: black;">InfoStores</span>.<span style="color: black;">Count</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
  store = cdo.<span style="color: black;">InfoStores</span><span style="color: black;">&#91;</span>i<span style="color: black;">&#93;</span>
  root = store.<span style="color: black;">RootFolder</span>
  m = root.<span style="color: black;">Messages</span>
  store.<span style="color: black;">ID</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'#'</span> <span style="color: #66cc66;">*</span> <span style="color: #ff4500;">72</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> store.<span style="color: black;">Name</span>
  <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'#'</span> <span style="color: #66cc66;">*</span> <span style="color: #ff4500;">72</span>
  dump_folder<span style="color: black;">&#40;</span>root<span style="color: black;">&#41;</span>
cdo.<span style="color: black;">Logoff</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/data-mining-outlook-for-fun-and-profit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Debugging DCOracle2 applications</title>
		<link>http://majid.info/blog/debugging-dcoracle2-applications/</link>
		<comments>http://majid.info/blog/debugging-dcoracle2-applications/#comments</comments>
		<pubDate>Sat, 04 Oct 2003 01:57:03 +0000</pubDate>
		<dc:creator>majid</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Mylos]]></category>

		<guid isPermaLink="false">http://www.majid.info/mylos/weblog/2003/10/03-1.html</guid>
		<description><![CDATA[DCOracle2 is the Oracle interface module for Python I use most often. It is advertised as &#8220;beta&#8221;, but quite suitable for production use, aside from a few minor rough edges. There are a few others, most notably cx_oracle, but I &#8230; <a href="http://majid.info/blog/debugging-dcoracle2-applications/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.zope.org/Members/matt/dco2">DCOracle2</a> is the Oracle interface module for Python I use most often. It is advertised as &#8220;beta&#8221;, but quite suitable for production use, aside from a few minor rough edges. There are a few others, most notably <a href="http://starship.python.net/crew/atuining/cx_Oracle/index.html">cx_oracle</a>, but I can&#8217;t vouch for them.</p>
<p>Debugging applications that make use of DCOracle2 can be challenging, as with any database environment, specially in a multi-threaded server context. I have developed a small utility module to aid in development. When it is imported, it will automatically trace all database calls made through DCOracle2, including arguments such as bind variables. More interestingly, it will also automatically run EXPLAIN PLAN on queries taking longer than 2 seconds (by default), to aid in tuning SQL statements. As a side bonus, if run by itself, it provides a (very basic) SQL shell that does offer command-line history and editing, something Oracle hasn&#8217;t managed to provide in SQL*Plus in almost 30 years <img src='http://majid.info/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>This code works with Python 2.2 and DCOracle2 1.1 and 1.3 beta. It will not work with 2.1 and earlier.</p>
<p>The latest version of the module file can be downloaded here: <a href="http://www.majid.info/python/debug_ora.py">debug_ora.py</a>, as well as the RCS repository <a href="http://www.majid.info/python/debug_ora.py">debug_ora.py,v</a>for those who care about this kind of stuff.</p>
<p>An example run of the module:</p>
<pre>
% python debug_ora.py scott/tiger@repos
SQL> select ename, job, dname from emp, dept where emp.deptno=dept.deptno;
SQL: Oct-03-2003 17:32:39:897
select ename, job, dname from emp, dept where emp.deptno=dept.deptno
ARG: () {}
SQL: !!!!!!!!!!!!!!!! slow query, time = 0.0 sec
SQL: !!!!!!!!!!!!!!!! execution plan follows
000      SELECT STATEMENT Optimizer=CHOOSE
001        NESTED LOOPS
002 001      TABLE ACCESS (FULL) ON EMP
003 001      TABLE ACCESS (BY INDEX ROWID) ON DEPT
004 003        INDEX (UNIQUE SCAN) ON PK_DEPT

ENAME  JOB       DNAME
------ --------- ----------
SMITH  CLERK     RESEARCH
ALLEN  SALESMAN  SALES
WARD   SALESMAN  SALES
JONES  MANAGER   RESEARCH
MARTIN SALESMAN  SALES
BLAKE  MANAGER   SALES
CLARK  MANAGER   ACCOUNTING
SCOTT  ANALYST   RESEARCH
KING   PRESIDENT ACCOUNTING
TURNER SALESMAN  SALES
ADAMS  CLERK     RESEARCH
JAMES  CLERK     SALES
FORD   ANALYST   RESEARCH
MILLER CLERK     ACCOUNTING
SQL>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://majid.info/blog/debugging-dcoracle2-applications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

