<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">g.raphaelli's weblog</title>
  <id>http://g.raphaelli.com/authors/g/feed.atom</id>
  <updated>2010-01-25T01:41:15Z</updated>
  <link href="http://g.raphaelli.com/" />
  <link href="http://g.raphaelli.com/authors/g/feed.atom" rel="self" />
  <generator uri="http://zine.pocoo.org/" version="0.1.2">Zine</generator>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Flickr.API 0.4.3</title>
    <id>tag:g.raphaelli.com,2010-01-25:/entry;2010/1/25/flickr-api-0-4-3</id>
    <updated>2010-01-25T01:41:15Z</updated>
    <published>2010-01-25T01:26:00Z</published>
    <link href="http://g.raphaelli.com/2010/01/25/flickr-api-0-4-3" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;a href="http://aaronland.info/"&gt;Aaron&lt;/a&gt; kindly informed me of a problem with &lt;a href="http://pypi.python.org/pypi/Flickr.API/"&gt;Flickr.API&lt;/a&gt; where calling &lt;a href="http://www.flickr.com/services/api/flickr.urls.lookupUser.htm"&gt;flickr.urls.lookupUser&lt;/a&gt; with the required 'url' parameter returns the the contents of the resource pointed to by that url, instead of the expected API response.  The problem is easily demonstrated using &lt;a href="http://static.g.raphaelli.com/contrib/code/flickrcall.py"&gt;flickrcall.py&lt;/a&gt;, introduced in &lt;a href="http://g.raphaelli.com/2009/08/06/flickr-api-helper-script"&gt;this post&lt;/a&gt;:

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;flickrcall.py flickr.urls.lookupUser &lt;span class="s2"&gt;&amp;quot;url=http://www.flickr.com/photos/graph&amp;quot;&lt;/span&gt;

&amp;lt;!DOCTYPE HTML PUBLIC &lt;span class="s2"&gt;&amp;quot;-//W3C//DTD HTML 4.01 Transitional//EN&amp;quot;&lt;/span&gt;&amp;gt;

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
...
&lt;/pre&gt;&lt;/div&gt;


The trouble lies in the Flickr.API.Request init:

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://api.flickr.com/services/rest/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


Clearly I didn't anticipate 'url' as a valid argument - if you pass it in through **args it overwrites the default url parameter.  Worse, if you wanted to use a different api endpoint, you couldn't even call this method with the required url parameter as python will complain about multiple values for the same 'url' argument.  Here's a simple demonstration of what's happening:

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;baz&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;bar&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


For 0.4.3, I've simply changed the url keyword parameter to apiurl to make things work as I haven't thought of any backwards compatible way to fix this.  Now we get the right response:

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;flickrcall.py flickr.urls.lookupUser &lt;span class="s2"&gt;&amp;quot;url=http://www.flickr.com/photos/graph&amp;quot;&lt;/span&gt;
&amp;lt;?xml &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span class="nv"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; ?&amp;gt;
&amp;lt;rsp &lt;span class="nv"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&amp;gt;
&amp;lt;user &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;83127823@N00&amp;quot;&lt;/span&gt;&amp;gt;
	&amp;lt;username&amp;gt;raphco&amp;lt;/username&amp;gt;
&amp;lt;/user&amp;gt;
&amp;lt;/rsp&amp;gt;
&lt;/pre&gt;&lt;/div&gt;


I'm hoping that unless you work at Flickr (&lt;a href="http://www.flickr.com/jobs"&gt;we're hiring!&lt;/a&gt;), you're not likely to change the endpoint url and this change should only fix this particular problem for you.</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Streaming Flickr Uploads in Python</title>
    <id>tag:g.raphaelli.com,2009-10-07:/entry;2009/10/7/streaming-flickr-uploads-in-python</id>
    <updated>2009-12-01T08:58:20Z</updated>
    <published>2009-10-07T07:29:00Z</published>
    <link href="http://g.raphaelli.com/2009/10/07/streaming-flickr-uploads-in-python" />
    <author>
      <name>g</name>
    </author>
    <content type="html">Flickr has supported up to 500 MB uploads since &lt;a href="http://blog.flickr.net/en/2009/04/27/hd-video-files-are-big/"&gt;April&lt;/a&gt;.  Unfortunately, uploading even a 100 MB file (easily less than 30 sec of high def video) with Flickr.API, possibly after reading &lt;a href="http://g.raphaelli.com/2009/08/06/flickr-api-helper-script"&gt;this&lt;/a&gt; or &lt;a href="http://g.raphaelli.com/2009/08/02/python-flickr-api-042"&gt;this&lt;/a&gt;, uses about about 100 MB of memory, because the entire file is first read() before being sent off to Flickr.  top reports this for a 94 MB video upload:

&lt;div class="syntax"&gt;&lt;pre&gt;  PID COMMAND     #TH #PRTS #MREGS RPRVT  RSHRD  RSIZE  VSIZE
  627 python        1    20    133   98M   256K   101M   118M 
&lt;/pre&gt;&lt;/div&gt;


A quick search of pypi turned up &lt;a href="http://pypi.python.org/pypi/poster/"&gt;Poster&lt;/a&gt;, a library for &lt;q&gt;Streaming HTTP uploads and multipart/form-data encoding&lt;/q&gt;.  After hooking this up, the same 94 MB video upload is reported as:

&lt;div class="syntax"&gt;&lt;pre&gt;  PID COMMAND     #TH #PRTS #MREGS RPRVT  RSHRD  RSIZE  VSIZE
  631 python        1    20    132 4904K   256K  7524K    24M 
&lt;/pre&gt;&lt;/div&gt;


Notice the private memory usage drop from 98 MB to under 5 MB.  The code to get it done looks like this:

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;poster&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;poster.streaminghttp&lt;/span&gt;
&lt;span class="n"&gt;poster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;streaminghttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_openers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flopped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;flopped&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


flop swaps the (body generator, headers) tuple returned by Poster's multipart_encode into (headers, body) that Flickr.API's execute_request is expecting as seen in the next code block.  multipart_encode can receive a custom boundary but we're basically just going to ignore that here.

Then, when executing the upload request, this new form encoding can be used like:

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upload_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://api.flickr.com/services/upload&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upload_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;flop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;multipart_encode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


Now Poster takes care of transferring the file in 8KB chunks, instead of loading the whole file into memory.</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Flickr.API Helper Script</title>
    <id>tag:g.raphaelli.com,2009-08-06:/entry;2009/8/6/flickr-api-helper-script</id>
    <updated>2009-08-12T07:40:35Z</updated>
    <published>2009-08-07T01:23:00Z</published>
    <link href="http://g.raphaelli.com/2009/08/06/flickr-api-helper-script" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;Here is a [formerly] little script I use to test out Flickr API calls: &lt;a href="http://static.g.raphaelli.com/contrib/code/flickrcall.py"&gt;flickrcall.py&lt;/a&gt;.  It grew by about 200 lines getting it ready for posting here and it's still not pretty.  It does work though, so use it like this:&lt;/p&gt;

&lt;p&gt;Create a config file (optional, you can specify the key and secret on the command line)&lt;/p&gt;&lt;p&gt;
&lt;/p&gt;&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cat &amp;gt; ~/.flickr.cfg
&lt;span class="o"&gt;[&lt;/span&gt;api&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; your key
&lt;span class="nv"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; your secret
^D
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Make the call&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;flickrcall.py flickr.test.echo &lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bar -j -b
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;that will return the familiar:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;api_key&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; your api key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;nojsoncallback&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flickr.test.echo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;stat&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Need to make an authenticated call but don't have a token yet?  Pass in -t:&lt;/p&gt;

&lt;div class="syntax"&gt;&lt;pre&gt;$ flickrcall.py -t -j -b -p write flickr.contacts.getList page=1 per_page=5
auth me:  http://flickr.com/services/auth/...
done [y]:
try auth_token= acquired_auth_token next time for write permissions
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Some desktop auth action later you have a token for use in subsequent calls.  Of course, you also get the response you were (hopefully) after:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;stat&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;contacts&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;perpage&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;contact&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I often just run ipython -i flickrcall.py ... to get an interactive session, the 'rsp' and 'r' variables contain the response object and the whole response as a string so you can mess with parsing or otherwise interrogate Flickr's response.&lt;/p&gt;

&lt;p&gt;Finally, flickrcall can be used as a library&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;flickrcall&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flickrcall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;flickr.test.echo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;-n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;foo=bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="s"&gt;&amp;#39;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;your_api_key&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here is the full usage:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;$ flickrcall.py -h
Usage: flickrcall.py [options] api.method [key=val ... ]

Options:
  -h, --help            show this help message and exit
  -c CONFIG, --config=CONFIG
                        flickr config file
  --no-sign             don&amp;#39;t sign request
  -k KEY, --key=KEY     Flickr API key
  -s SECRET, --secret=SECRET
                        Flickr API secret
  -n, --no-secret       don&amp;#39;t use the Flickr API secret
  -p PERMS, --permissions=PERMS
                        permissions
  -t, --get-auth-token  get an authentication token first
  -b, --nojsoncallback  nojsoncallback=1
  -j, --json            request response in json
  -x, --xml             request response in xml (rest)
  -D, --debug           print some details about what&amp;#39;s going on
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;A few things that could be added another day:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Optional checkToken&lt;/li&gt;
&lt;li&gt;Loading/Saving auth tokens in the config file&lt;/li&gt;
&lt;li&gt;Multiple API Key config file support&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Flickr.API 0.4.2</title>
    <id>tag:g.raphaelli.com,2009-08-02:/entry;2009/8/2/flickr-api-0-4-2</id>
    <updated>2009-08-02T04:03:56Z</updated>
    <published>2009-08-02T01:40:00Z</published>
    <link href="http://g.raphaelli.com/2009/08/02/python-flickr-api-042" />
    <author>
      <name>g</name>
    </author>
    <content type="html">I've uploaded a minor update of Flickr.API to &lt;a href="http://pypi.python.org/pypi/Flickr.API/"&gt;pypi&lt;/a&gt;.  This release addresses the deprecation warning on importing the md5 module under python2.6 by using the hashlib module where available.

This is a good opportunity to expand on the pydoc bundled in Flickr.API so here goes.

&lt;h3&gt;Making a simple call&lt;/h3&gt;

&lt;p&gt;flickr.test.echo is a simple place to start and a good way to make sure that everything is set up correctly.  It echoes back any parameters you provide.

&lt;/p&gt;&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Flickr.API&lt;/span&gt;
&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;test_rsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;flickr.test.echo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;that returns:&lt;/p&gt;

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;rsp&lt;/span&gt; &lt;span class="na"&gt;stat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;api_key&amp;gt;&lt;/span&gt; your api key &lt;span class="nt"&gt;&amp;lt;/api_key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;method&amp;gt;&lt;/span&gt;flickr.test.echo&lt;span class="nt"&gt;&amp;lt;/method&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/rsp&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


A few things to note here:
&lt;ul&gt;
&lt;li&gt;the default response format from flickr is xml (called 'rest' on &lt;a href="http://www.flickr.com/services/api/response.rest.html"&gt;http://www.flickr.com/services/api/response.rest.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;test_rsp is a file-like addinfourl instance, like that returned by urllib2.urlopen().  That means:
   &lt;/li&gt;&lt;li&gt;test_rsp has a read() method, great for passing into ElementTree.parse() or json.load()&lt;/li&gt;
   &lt;li&gt;test_rsp has an HTTP response code (test_rsp.code), which should be checked before you even bother to parse the response contents&lt;/li&gt;

&lt;li&gt;Every response from flickr is wrapped in rsp tags.  Be sure to check the value of stat after every call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adding some parameters to request a json response looks like:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;test_json_rsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;flickr.test.echo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;format&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;nojsoncallback&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That returns:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;api_key&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; your api key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;nojsoncallback&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flickr.test.echo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;stat&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There are numerous xml and json handling libraries in python so handling either response type is a matter of preference.&lt;/p&gt;

&lt;h3&gt;Putting it together&lt;/h3&gt;

&lt;p&gt;Putting these basic calls together to create a real application using the Flickr API should be pretty straightforward now.  Let's say you'd like to write a command-line uploading utility.  The &lt;a href="http://www.flickr.com/services/api/upload.api.html"&gt;Flickr API Upload Documentation&lt;/a&gt; specifies that uploading photos requires write permission on a user's account.  This means we need to authenticate the user and get our application approved with write access.  &lt;a href="http://www.flickr.com/services/api/auth.howto.desktop.html"&gt;Desktop Auth&lt;/a&gt; lays out the approach (starting with step #3):&lt;/p&gt;

&lt;h4&gt;Request a frob&lt;/h4&gt;

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;frob_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;flickr.auth.getFrob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;frob_rsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frob_request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;frob_rsp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;frob_rsp_et&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;etree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElementTree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frob_rsp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;frob_rsp_et&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getroot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;stat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;frob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frob_rsp_et&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findtext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;frob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This introduces the other way to make an api call with Flickr.API.  Here, we create a Flickr.API.Request object as frob_request and pass that into api.execute_request. Note that now we provide the secret instead of None - the secret is required for generating api call signatures.  api.execute_method is simply a convenience method that does exactly the same thing.  Flickr.API.Request provides more options for configuration as it is a urllib2.Request object.  This means you can configure a proxy server, add your own headers, etc.  Also, execute_method() takes arguments as a dict (args={}), originally intended to mirror the way Flickr::API works.  execute_request uses the more pythonic **kwargs for arguments.&lt;/p&gt;

&lt;p&gt;We also see here some of the application-level error handling that needs to be done.  Note that execute_request could throw any of the exceptions urllib2.urlopen() does so that should be wrapped in a try/except and handled appropriately.&lt;/p&gt;

&lt;h4&gt;Create a login link&lt;/h4&gt;

&lt;p&gt;The get_authurl method makes this step easy.  Continuing with the command line uploader, here we display the link and wait until the user confirms that it visited that url.  For web authentication, you don't get the frob until after authorization happens so you would just leave out the frob paramater.&lt;/p&gt;

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;auth_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_authurl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;write&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;frob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;auth me:  &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_url&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;raw_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;done [y]: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;Convert Frob to Token&lt;/h4&gt;

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;token_rsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;flickr.auth.getToken&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;frob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nojsoncallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token_rsp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;token_rsp_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_rsp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token_rsp_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;stat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_rsp_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;auth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here we see how to handle a json response.  Now that we have the token, we can make any authenticated calls by passing auth_token=token in the list of arguments.&lt;/p&gt;

&lt;h4&gt;Make an authenticated call&lt;/h4&gt;

&lt;p&gt;There are two methods for uploading photos provided in Flickr.API.  Like execute_method, execute_upload is a convenience method that calls execute_request with the appropriate Flickr.API.Request object. That looks like:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;photo.jpg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upload_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;photo.jpg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;auth_token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;test upload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;photo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you need to build your own request object, that could look like:&lt;/p&gt;
&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="n"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;photo.jpg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upload_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://api.flickr.com/services/upload&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;test upload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;upload_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode_multipart_formdata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Either way, the upload response contains the unique photo id assigned by Flickr to that photo.  This response is always XML, even if you try to specify the response format.  The url to that photo is: http://www.flickr.com/photos/nsid/photoid or just: http://www.flickr.com/photo.gne?id=photoid.&lt;/p&gt;

&lt;p&gt;That's it, go create.&lt;/p&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">libyahoo2 python bindings</title>
    <id>tag:g.raphaelli.com,2009-04-02:/entry;2009/4/2/liyahoo2-python-bindings</id>
    <updated>2009-04-08T03:37:34Z</updated>
    <published>2009-04-02T23:23:00Z</published>
    <link href="http://g.raphaelli.com/2009/4/2/libyahoo2-python-bindings" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;I have wanted Python bindings for libyahoo2 for a long time and finally sat down with Ned Batchedler's Pycon '09 slides, &lt;a href="http://nedbatchelder.com/text/whirlext.html"&gt;A Whirlwind Excursion through Python C Extensions&lt;/a&gt;, and wrote some basic ones available at:
&lt;/p&gt;&lt;div style="text-align: center;"&gt;
&lt;a href="http://bitbucket.org/graphaelli/ymsgrclient"&gt;http://bitbucket.org/graphaelli/ymsgrclient&lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;
Thanks Ned for the great presentation.  He's right that it's just an introduction as things like &lt;a href="http://docs.python.org/extending/extending.html#calling-python-functions-from-c"&gt;calling python from C&lt;/a&gt; couldn't even be covered.  I would recommend that aspiring module writers review Ned's presentation, move on to &lt;a href="http://docs.python.org/extending/index.html"&gt;Extending and Embedding the Python Interpreter&lt;/a&gt;, and then bookmark the crap out of the &lt;a href="http://docs.python.org/c-api/index.html"&gt;Python/C API Reference Manual&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Back to the bindings - a simple bot that will parrot your words back to you looks like this:

&lt;/p&gt;&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;YmsgrClient&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParrotBot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;YmsgrClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YmsgrClient&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_im&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;who&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_im&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;who&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ParrotBot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;YmsgrClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STATUS_AVAILABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are a number of rough edges and simply bad ideas in the code but it is sufficient for writing a single account client.  We've been using this for our bot at work for a few days without problems [or segfaults].  What we have had is an increase in bot functionality, as the barrier to writing few lines of Python and restarting the bot is much lower than writing a bunch of C and recompiling repeatedly until it works [or doesn't, giving up, and rolling it all back].&lt;/p&gt;

&lt;p&gt;Some things I'd like to do immediately:
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Convert the data structures and functions lifted directly from the &lt;a href="http://libyahoo2.svn.sourceforge.net/viewvc/libyahoo2/trunk/libyahoo2/src/sample_client.c?revision=308&amp;amp;view=markup"&gt;libyahoo2 sample client&lt;/a&gt; to Python objects and expose them&lt;/li&gt;
&lt;li&gt;Redo the login poll loop or at least allow callbacks to be scheduled somehow&lt;/li&gt;
&lt;li&gt;Add Documentation&lt;/li&gt;
&lt;li&gt;Throw better exceptions and use the logging module properly&lt;/li&gt;
&lt;/ul&gt;

Some things that someone should do eventually:
&lt;ul&gt;
&lt;li&gt;Add Webcam support and sample usage documentation&lt;/li&gt;
&lt;li&gt;Test functionality on Windows&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Contributions are welcome.  Constructive criticism is greatly appreciated.&lt;/p&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Circumventing security policy with gearman</title>
    <id>tag:g.raphaelli.com,2009-01-09/entry:2009/1/9/defeating-firewalls-with-gearmand</id>
    <updated>2009-01-12T10:45:44Z</updated>
    <published>2009-01-12T10:42:00Z</published>
    <link href="http://g.raphaelli.com/2009/1/9/circumventing-security-policy-with-gearman" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;Gearman can be handy for defeating security in situations like the one pictured:

&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;img src="http://static.g.raphaelli.com/images/2009/01/12/host-setup.png"&gt;&lt;/div&gt;

Here, Host A can initiate connections to Host B but Host B is blocked by a firewall/router ACL/etc from initiating communications with Host A.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;With gearman, you can call a service on Host A from Host B with a setup like this:

&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;img src="http://static.g.raphaelli.com/images/2009/01/12/gearman-rpc.png"&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Run a gearmand job server on host B (or any host that A and B can both reach).&lt;/li&gt;
&lt;li&gt;Register a gearman worker running on Host A with that job server.&lt;/li&gt;
&lt;li&gt;Submit work from a client running on Host B to that job server.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;This idea can be taken another step by chaining workers together such that calling abc() as depicted above creates a job that Host C, which can't reach or be reached by Host B at all, can ultimately execute.  That kind of setup makes it increasingly difficult to track an individual task's real status but it can be handy in a pinch.&lt;/p&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Gearmand Rewrite Released</title>
    <id>tag:g.raphaelli.com,2009-01-09/entry:2009/1/9/gearmand</id>
    <updated>2009-01-11T01:57:53Z</updated>
    <published>2009-01-09T07:43:00Z</published>
    <link href="http://g.raphaelli.com/2009/1/9/gearmand-rewrite-released" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;There is some big news for &lt;a href="http://www.gearmanproject.org/"&gt;Gearman&lt;/a&gt; fans out today - the first release of the C-rewrite of the gearmand job server has been &lt;a href="http://www.oddments.org/?p=30"&gt;announced&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Gearman is an excellent framework for farming out tasks to pools of machines, parallelizing tasks, and making calls between programming languages that speak gearman.  Like memcached, it's extremely easy to get started using it.  Once you do start using it, you'll never understand how you lived without it.&lt;/p&gt;

&lt;p&gt;The new release does not provide python bindings but the code in &lt;a href="http://code.sixapart.com/svn/gearman/trunk/api/python/"&gt;sixapart's svn&lt;/a&gt; should be compatible with the new server*.  The sample code provided with the both the perl and C versions is pretty trivial as they simply echo or reverse some strings.  To pass interesting data between client and worker you'll need to create your own convention.  YAML or simply JSON is very handy for this.&lt;/p&gt;

&lt;p&gt;Let's take a contrived example.  Of course, error handling will be omitted for clarity.&lt;/p&gt;

&lt;p&gt;This is a gearman worker that expects a list of urls and will do something to each of them.&lt;/p&gt;

&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; A Sample Gearman Worker &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wraps&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simplejson&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;urllib&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gearman&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GearmanWorker&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;job_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Decorates worker functions by calling them with a job&amp;#39;s arguments &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nd"&gt;@wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# do something with the job object&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;json_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Decorates a function that may be called with a&lt;/span&gt;
&lt;span class="sd"&gt;        JSON-formatted string but expects a python object &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nd"&gt;@wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# convert the args in JSON to a python object&lt;/span&gt;
        &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;

&lt;span class="nd"&gt;@job_in&lt;/span&gt;
&lt;span class="nd"&gt;@json_in&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fetching &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# do something with the url&lt;/span&gt;
        &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fetched&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GearmanWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobservers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fetch&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;
A client for this worker can look like:

&lt;/p&gt;&lt;div class="syntax"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simplejson&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gearman&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GearmanClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;

&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.flickr.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://www.yahoo.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GearmanClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobservers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;do_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fetch&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%i&lt;/span&gt;&lt;span class="s"&gt; urls fetched successfully&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fetched&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While this example is still quite simple, it does illustrate the idea of the convention necessary for passing real information between clients and workers.  The python libraries for gearman also support tasksets which are a set of tasks submitted at once to a job server for parallel execution.  This is a simple yet powerful way to speed up work and make the most of hardware investments.&lt;/p&gt;

&lt;p&gt;I hope that this rewrite of gearmand renews interest in the python community to enhance the current bindings.  I'd be very interested in a gearman protocol implementation for twisted (and might just start working on it soon).&lt;/p&gt;

&lt;p&gt;* on Mac OSX 10.4.11 I'm getting a bus error from gearmand after successfully running a job.  I haven't tracked it down or submitted a bug yet. &lt;b&gt;update:&lt;/b&gt; &lt;a href="https://bugs.launchpad.net/gearmand/+bug/315652"&gt;bug&lt;/a&gt; reported via IRC (yes, I'm gilad on freenode) and fixed with a two line patch.  A new release should be announced shortly.&lt;/p&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Dr. Dog, Gomez, The Black Keys at The Arena, Brisbane</title>
    <id>tag:g.raphaelli.com,2009-01-06/entry:2009/1/6/dr-dog-gomez-the-black-keys-at-the-arena-brisbane</id>
    <updated>2009-01-07T03:42:20Z</updated>
    <published>2009-01-06T10:17:00Z</published>
    <link href="http://g.raphaelli.com/2009/1/6/dr-dog-gomez-the-black-keys-at-the-arena-brisbane" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;Dr. Dog, Gomez, and The Black Keys played &lt;a href="http://www.arenaonline.com.au"&gt;The Arena&lt;/a&gt; last night.  It was the best rock show I've been to in a long time.&lt;/p&gt;

&lt;div style="text-align: center;"&gt;&lt;a href="http://flickr.com/photos/graph/3170085517/"&gt;&lt;img src="http://farm4.static.flickr.com/3087/3170085517_531bd4f186.jpg" alt="Dr. Dog Photo at The Arena, Brisbane" title="Dr. Dog on stage"&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="http://www.drdogmusic.com/"&gt;Dr. Dog&lt;/a&gt; opened and I'm pretty sure they played Worst Trip, The Ark, The Beach, The Old Days, and a few others.  They were easily my favorite new-to-me band of 2008.  I first heard them on &lt;a href="http://www.xpn.org/yrock-on-xpn/home"&gt;yrock on xpn&lt;/a&gt; and soon after bought their new album, &lt;a href="http://www.amazon.com/Fate-Dr-Dog/dp/B0018TAFW0/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=music&amp;amp;qid=1231207997&amp;amp;sr=8-1"&gt;Fate&lt;/a&gt;.  They rocked.&lt;/p&gt;

&lt;div style="text-align: center;"&gt;&lt;a href="http://flickr.com/photos/graph/3170090719/"&gt;&lt;img src="http://farm4.static.flickr.com/3091/3170090719_1ea4a4b5a2_m.jpg" alt="Gomez Photo at The Arena, Brisbane" title="Gomez"&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;While I went to the show to see the opening act, the second opener &lt;a href="http://www.gomeztheband.com"&gt;Gomez&lt;/a&gt; turned out to be awesome.  I'm definitely going to need to check out some more of their work.&lt;/p&gt;

&lt;div style="text-align: center;"&gt;&lt;a href="http://flickr.com/photos/graph/3170105327/"&gt;&lt;img src="http://farm2.static.flickr.com/1146/3170105327_1f46b54d12_m.jpg" alt="The Black Keys Photo at The Arena, Brisbane" title="The Black Keys"&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="http://www.theblackkeys.com/"&gt;The Black Keys&lt;/a&gt; headlined and were incredible - those two guys had so much energy.  They're touring a bit more in Australia and then heading back to my hometown area.  They'll be at the Electric Factory in Philly in Feb - word is they sold out the TLA so quickly the show got moved.&lt;/p&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Ganglia for MySQL Metrics</title>
    <id>tag:g.raphaelli.com,2009-01-05/entry:2009/1/5/mysql-metrics-in-ganglia</id>
    <updated>2009-08-07T23:46:46Z</updated>
    <published>2009-01-05T02:43:00Z</published>
    <link href="http://g.raphaelli.com/2009/1/5/ganglia-mysql-metrics" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;I just made some minor updates to the metric collection module I use for monitoring MySQL with Ganglia.  It collects over 100 metrics and includes a basic report like:

&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;img src="http://static.g.raphaelli.com/images/2009/01/05/ganglia_mysql_report_sample.png" alt="MySQL selects, inserts, updates, deletes on one graph" title="Sample Ganglia MySQL Report"&gt;&lt;/div&gt;

This blog is the only thing using this MySQL server and I don't generate much traffic (hi mom!)
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;It works either as a ganglia 3.1-style &lt;a href="http://ganglia.wiki.sourceforge.net/ganglia_gmond_python_modules"&gt;gmond python module&lt;/a&gt; or as a gmetric script, for use with any decent version of ganglia.  These are the relevant files:

&lt;/p&gt;&lt;ul&gt;
        &lt;li&gt;the module: &lt;a href="http://static.g.raphaelli.com/contrib/code/ganglia/mysql.py"&gt;mysql.py&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;sample module configuration file: &lt;a href="http://static.g.raphaelli.com/contrib/code/ganglia/mysql.pyconf"&gt;mysql.pyconf&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;ganglia frontend report: &lt;a href="http://static.g.raphaelli.com/contrib/code/ganglia/mysql_query_report.php"&gt;mysql_query_report.php&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;dependency module, for parsing InnoDB Status: &lt;a href="http://static.g.raphaelli.com/contrib/code/ganglia/DBUtil.py"&gt;DBUtil.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;

Comments and improvements are welcome.</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Packaging Python Modules</title>
    <id>tag:g.raphaelli.com,2009-01-01/entry:2009/1/1/packaging-python-modules</id>
    <updated>2009-08-07T23:48:13Z</updated>
    <published>2009-01-01T06:05:00Z</published>
    <link href="http://g.raphaelli.com/2009/1/1/packaging-python-modules" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;p&gt;Tools like &lt;a href="http://peak.telecommunity.com/DevCenter/EasyInstall"&gt;EasyInstall&lt;/a&gt; and &lt;a href="http://pypi.python.org/pypi/pip/"&gt;PIP&lt;/a&gt; are excellent for frequently changing environments.  I use virtualenv extensively in my development environments where I can easy_install or pip -E install away.  These tools, however, are not appropriate for production environments.  In production, repeatability and minimal dependency on development tools are essential.  When rolling out hundreds of servers, installing MySQL-python bindings with easy_install would require setting up a build environment on each host.  The operating system's package manager is sufficient for managing the installation of these modules.&lt;/p&gt;

&lt;p&gt;What if you need different versions of the same package on a particular host?  Assuming there are two applications running on a host that require different versions of the same package: move one of the applications to a separate host or VM.  If one application needs two versions of a particular package: fix the application.  In an emergency, running an application out of a temporary virtualenv can do the job at the expense of operational headaches.&lt;/p&gt;

&lt;p&gt;I run Redhat based systems so RPMs are the way to go for me.  python setup.py bdist_rpm is handy but there are a number of things I don't like about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packages are not python-versioned.  pyOpenSSL-0.8-1.i386.rpm does not indicate which python it is built against.  Also, what if you want that package for both python2.4 and python2.5?&lt;/li&gt;
&lt;li&gt;Similarly, I prefer that python library packages be clearly identified in the list of installed packages. A 'python-' prefix is effective.  rrdtool-1.2.27-5.i386.rpm is the application package, python25-rrdtool.i386.rpm are the python2.5 bindings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since those things are not easy to override in the stock bdist_rpm, I use a lightly modified distutils/command/bdist_rpm.py arbitrarily named bdist_rpm_ver.py.  Building production packages is usually as easy as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;easy_install --build-directory ~/rpm/BUILD/ --editable &amp;lt;pkg&amp;gt;&lt;/li&gt;
&lt;li&gt;cd ~/rpm/BUILD/&amp;lt;pkg&amp;gt;&lt;/li&gt;
&lt;li&gt;pythonXX setup.py bdist_rpm_ver --fix-python --binary-only&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This certainly is not perfect yet - sometimes I have to break down and do a --spec-only and tweak things by hand.  Fortunately, this happens once and only takes a few minutes to fix and then I have a stable package ready for deployment.  Also, I handle dependencies separately from the rpms currently but they can be added in step 3 with --requires, --conflicts, etc.&lt;/p&gt;

&lt;p&gt;I'm interested in hearing more about:
&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I’ve been meaning to write a post on why I think using system packaging for libraries is counter-productive, but that’ll wait for another time.
&lt;br&gt;&lt;br&gt;Ian Bicking - &lt;a href="http://blog.ianbicking.org/2008/12/14/a-few-corrections-to-on-packaging/"&gt;A Few Corrections To “On Packaging”&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;In my experience, system packaging is the way to go for simple, repeatable installations to a single host or 1000s of hosts.&lt;/p&gt;</content>
  </entry>
  <entry xml:base="http://g.raphaelli.com/authors/g/feed.atom">
    <title type="text">Prelude</title>
    <id>tag:g.raphaelli.com,2008-12-30/entry:intro</id>
    <updated>2009-08-07T23:47:49Z</updated>
    <published>2008-12-30T21:45:00Z</published>
    <link href="http://g.raphaelli.com/2008/12/31/prelude" />
    <author>
      <name>g</name>
    </author>
    <content type="html">&lt;a href="http://zine.pocoo.org/"&gt;Zine 0.1.1&lt;/a&gt; is installed and I'm really going to do this thing [ with the words and the writing ].</content>
  </entry>
</feed>

