<?xml version="1.0" encoding="ISO-8859-1"?>

<rdf:RDF
 xmlns="http://purl.org/rss/1.0/"
 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
>

<channel rdf:about="http://www.peterbe.com/rss.xml">
  <title>Peterbe.com</title>
  <link>http://www.peterbe.com</link>
  <description>Peter Bengtssons's personal homepage about little things that concern him.</description>
  <dc:language>en-uk</dc:language>
  <dc:publisher>mail@peterbe.com</dc:publisher>
<items>
  <rdf:Seq>
  <rdf:li rdf:resource="http://www.peterbe.com/plog/persistent-caching-async" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/cryptic-errors-when-using-django-nose" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/python-file-with-closing-automatically" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/integrate-browserid-in-a-tornado-web-app" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/trivial-but-powerful-tips-for-nosetests" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/going-real-simple-on-html5-audio" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/too-cool-for-me-everyone" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/cateechee-golf-pictures" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/too-cool-for-me" />
  <rdf:li rdf:resource="http://www.peterbe.com/plog/tornado-utils-send_mail" />
 </rdf:Seq>
</items>
<image>
  <title>Peterbe.com</title>
  <url>http://www.peterbe.com/misc_/Peterbecom/peterbe_logo_white_small.gif</url>
  <link>http://www.peterbe.com</link>
  <width>140</width>
  <height>37</height>
  <description>Peterbe.com (Peter Bengtsson on Python, Django, Zope, Kung Fu, London and photos)</description>
</image>
</channel>

<item rdf:about="http://www.peterbe.com/plog/persistent-caching-async">
  <title><![CDATA[Persistent caching with fire-and-forget updates]]></title>
  <description><![CDATA[<p>I just recently landed some <a href="https://github.com/peterbe/toocool/commit/1d872f05e1961c5d2cd9ee93836a056d436780fe">patches on toocool</a> that implements and interesting pattern that is seen more and more these days. I call it: <strong>Persistent caching with fire-and-forget updates</strong></p>
<p>Basically, the implementation is this: You issue a request that requires information about a Twitter user: E.g. <a href="http://toocoolfor.me/following/chucknorris/vs/peterbe">http://toocoolfor.me/following/chucknorris/vs/peterbe</a>
The app looks into its MongoDB for information about the <a href="https://github.com/peterbe/toocool/blob/master/models.py#L38">tweeter</a> and if it can't find this user it goes onto the Twitter REST API and looks it up and saves the result in MongoDB. 
The next time the same information is requested, and the data is available in the MongoDB it instead checks if the <code>modify_date</code> or more than an hour and if so, it sends a job to the message queue (Celery with Redis in my case) to perform an update on this tweeter. </p>
<p><a href="https://github.com/peterbe/toocool/blob/master/handlers.py#L479">You can basically see the code here</a> but just to reiterate and abbreviate, it looks like this:
<br /><div class="my_code_default">&nbsp;<span class="p_36">tweeter</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">db</span><span class="p_38">.</span><span class="p_36">Tweeter</span><span class="p_38">.</span><span class="p_36">find_one</span><span class="p_38">({</span><span class="p_30">'username'</span><span class="p_38">:</span>&nbsp;<span class="p_36">username</span><span class="p_38">})</span><br />&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_43">not</span>&nbsp;<span class="p_36">tweeter</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">result</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_43">yield</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">gen</span><span class="p_38">.</span><span class="p_36">Task</span><span class="p_38">(...)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_36">result</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">tweeter</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">save_tweeter_user</span><span class="p_38">(</span><span class="p_36">result</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">else</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_33">#&nbsp;deal&nbsp;with&nbsp;the&nbsp;error!</span><br />&nbsp;<span class="p_43">elif</span>&nbsp;<span class="p_36">age</span><span class="p_38">(</span><span class="p_36">tweeter</span><span class="p_38">[</span><span class="p_30">'modify_date'</span><span class="p_38">])</span>&nbsp;<span class="p_38">&gt;</span>&nbsp;<span class="p_37">3600</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">tasks</span><span class="p_38">.</span><span class="p_36">refresh_user_info</span><span class="p_38">.</span><span class="p_36">delay</span><span class="p_38">(</span><span class="p_36">username</span><span class="p_38">,</span>&nbsp;<span class="p_38">...)</span><br />&nbsp;<span class="p_33">#&nbsp;render&nbsp;the&nbsp;template!</span><br /></div>
</p>
<p>What the client gets, i.e. the user using the site, is it that apart from the very first time that URL is request is instant results but data is being maintained and refreshed.</p>
<p>This pattern works great for data that doesn't have to be up-to-date to the second but that still needs a way to cache invalidate and re-fetch. This works because my limit of 1 hour is quite arbitrary. An alternative implementation would be something like this:
<br /><div class="my_code_default">&nbsp;<span class="p_36">tweeter</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">db</span><span class="p_38">.</span><span class="p_36">Tweeter</span><span class="p_38">.</span><span class="p_36">find_one</span><span class="p_38">({</span><span class="p_30">'username'</span><span class="p_38">:</span>&nbsp;<span class="p_36">username</span><span class="p_38">})</span><br />&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_43">not</span>&nbsp;<span class="p_36">tweeter</span>&nbsp;<span class="p_43">or</span>&nbsp;<span class="p_38">(</span><span class="p_36">tweeter</span>&nbsp;<span class="p_43">and</span>&nbsp;<span class="p_36">age</span><span class="p_38">(</span><span class="p_36">tweeter</span><span class="p_38">)</span>&nbsp;<span class="p_38">&gt;</span>&nbsp;<span class="p_37">3600</span>&nbsp;<span class="p_38">*</span>&nbsp;<span class="p_37">24</span>&nbsp;<span class="p_38">*</span>&nbsp;<span class="p_37">7</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_33">#&nbsp;re-fetch&nbsp;from&nbsp;Twitter&nbsp;REST&nbsp;API</span><br />&nbsp;<span class="p_43">elif</span>&nbsp;<span class="p_36">age</span><span class="p_38">(</span><span class="p_36">tweeter</span><span class="p_38">)</span>&nbsp;<span class="p_38">&gt;</span>&nbsp;<span class="p_37">3600</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_33">#&nbsp;fire-and-forget&nbsp;update</span><br /></div>
</p>
<p>That way you don't suffer from persistently cached data that is too old. </p>]]></description>
  <link>http://www.peterbe.com/plog/persistent-caching-async</link>
  <dc:subject>Python</dc:subject>
  <dc:date>2011-12-13T23:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/cryptic-errors-when-using-django-nose">
  <title><![CDATA[Cryptic errors when using django-nose]]></title>
  <description><![CDATA[<p>After about 3 days of debugging using <code>pdb</code>, <code>print</code> and writing to a log file I've almost finally solve my bizarre errors I was getting when running a whole test suite. The error that it lead to was that Django refused to re-register models to the admin and the errors looked something like this:
<pre>
  ...
  File "/Users/peterbe/dev/MOZILLA/PTO/pto/urls.py", line 6, in &lt;module&gt;
    admin.autodiscover()
  File "/Users/peterbe/dev/MOZILLA/PTO/pto/vendor/src/django/django/contrib/admin/__init__.py", line 26, in autodiscover
    import_module('%s.admin' % app)
  File "/Users/peterbe/dev/MOZILLA/PTO/pto/vendor/src/django/django/utils/importlib.py", line 35, in import_module
    __import__(name)
  File "/Users/peterbe/dev/MOZILLA/PTO/pto/apps/users/admin.py", line 30, in &lt;module&gt;
    admin.site.register(UserProfile, UserProfileAdmin)
  File "/Users/peterbe/dev/MOZILLA/PTO/pto/vendor/src/django/django/contrib/admin/sites.py", line 85, in register
    raise AlreadyRegistered('The model %s is already registered' % model.__name__)
 AlreadyRegistered: The model UserProfile is already registered
</pre>
</p>
<p>Turns out to be independent of which Django project I ran and it was something no one else was able to reproduce on any machine with the exact same code. </p>
<p>After 2 days I found that there's a difference between a successful run and a failing run was how I specified (to <code>nose</code>) which module to load:
<pre>
 ./manage.py test users  # fails!
 ./manage.py test users.test  # works!
</pre>
</p>
<p>In both cases it finds the same tests. So it would either fail 10 times or work 10 times. Hmmm...</p>
<p>The bridging between <code>nose</code> and Django is done by awesome <a href="https://github.com/jbalogh/django-nose">django-nose</a> developed here at Mozilla by  Django extraordinaire <a href="https://github.com/jbalogh">Jeff Balogh</a> and it's a non-trivial piece of code as it depends on some really smart importing tricks and stuff which I haven't even begun to understand. </p>
<p>However, after so many trial and errors I finally discovered that the solution (for me) was to delete the <code>~/.noserc</code> file. What's strange is that all it contained was:
<pre>
 [nosetests]
 with-doctest=1
</pre>
</p>
<p>I might never actually find out what went wrong. Ultimately I think a reason things went wrong was because it incorrectly populated <code>sys.modules</code> with excessive keys that would cause double imports of <code>urls.py</code> which in turn runs <code>admin.autodiscover()</code> but incorrectly does so twice. </p>
<p>Sorry for the rambling. And sorry for not actually finding the real bug. I did spent 2-3 days debugging this non-stop and hopefully some other poor frustrated person is going to see this and also look into the <code>~/.noserc</code> for ways to fix it maybe. </p>]]></description>
  <link>http://www.peterbe.com/plog/cryptic-errors-when-using-django-nose</link>
  <dc:subject>Django</dc:subject>
  <dc:date>2011-12-06T19:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/python-file-with-closing-automatically">
  <title><![CDATA[Python file with closing automatically]]></title>
  <description><![CDATA[<p>Perhaps someone who knows more about the internals of python and the recent changes in 2.6 and 2.7 can explain this question that came up today in a <a href="https://github.com/mozilla/bedrock/commit/4c6c41a107a29835d7e3c289af96ae0a1b649934#commitcomment-762684">code review</a>. </p>
<p>I suggest using <code>with</code> instead of <code>try: ... finally:</code> to close a file that was written to. Instead of this:
<br /><div class="my_code_default">&nbsp;<span class="p_36">dest</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">file</span><span class="p_38">(</span><span class="p_30">'foo'</span><span class="p_38">,</span>&nbsp;<span class="p_30">'w'</span><span class="p_38">)</span><br />&nbsp;<span class="p_43">try</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">dest</span><span class="p_38">.</span><span class="p_36">write</span><span class="p_38">(</span><span class="p_30">'stuff'</span><span class="p_38">)</span><br />&nbsp;<span class="p_43">finally</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">dest</span><span class="p_38">.</span><span class="p_36">close</span><span class="p_38">()</span><br />&nbsp;<span class="p_43">print</span>&nbsp;<span class="p_36">open</span><span class="p_38">(</span><span class="p_30">'foo'</span><span class="p_38">).</span><span class="p_36">read</span><span class="p_38">()</span>&nbsp;&nbsp;<span class="p_33">#&nbsp;will&nbsp;print&nbsp;'stuff'</span><br /></div>
</p>
<p>We can use this:
<br /><div class="my_code_default">&nbsp;<span class="p_36">with</span>&nbsp;<span class="p_36">file</span><span class="p_38">(</span><span class="p_30">'foo'</span><span class="p_38">,</span>&nbsp;<span class="p_30">'w'</span><span class="p_38">)</span>&nbsp;<span class="p_36">as</span>&nbsp;<span class="p_36">dest</span><span class="p_38">:</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">dest</span><span class="p_38">.</span><span class="p_36">write</span><span class="p_38">(</span><span class="p_30">'stuff'</span><span class="p_38">)</span><br />&nbsp;<span class="p_43">print</span>&nbsp;<span class="p_36">open</span><span class="p_38">(</span><span class="p_30">'foo'</span><span class="p_38">).</span><span class="p_36">read</span><span class="p_38">()</span>&nbsp;&nbsp;<span class="p_33">#&nbsp;will&nbsp;print&nbsp;'stuff'</span><br /></div>
</p>
<p>Why does that work? I'm guessing it's because the <code>file()</code> instance object has a built in <code>__exit__</code> method. Is that right?</p>
<p>That means I don't need to use <a href="http://docs.python.org/library/contextlib.html#contextlib.closing">contextlib.closing(thing)</a> right?</p>
<p>For example, suppose you have this class:
<br /><div class="my_code_default">&nbsp;<span class="p_43">class</span>&nbsp;<span class="p_31">Farm</span><span class="p_38">(</span><span class="p_36">object</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">__enter__</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">print</span>&nbsp;<span class="p_39">"Entering"</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">return</span>&nbsp;<span class="p_36">self</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">__exit__</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">,</span>&nbsp;<span class="p_36">err_type</span><span class="p_38">,</span>&nbsp;<span class="p_36">err_val</span><span class="p_38">,</span>&nbsp;<span class="p_36">err_tb</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">print</span>&nbsp;<span class="p_39">"Exiting"</span><span class="p_38">,</span>&nbsp;<span class="p_36">err_type</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">close</span><span class="p_38">()</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">close</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">print</span>&nbsp;<span class="p_39">"Closing"</span><br /><br />&nbsp;<span class="p_36">with</span>&nbsp;<span class="p_36">Farm</span><span class="p_38">()</span>&nbsp;<span class="p_36">as</span>&nbsp;<span class="p_36">farm</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">pass</span><br />&nbsp;<span class="p_33">#&nbsp;this&nbsp;will&nbsp;print:</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;&nbsp;Entering</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;&nbsp;Exiting&nbsp;None</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;&nbsp;Closing</span><br /></div>
</p>
<p>Another way to achieve the same specific result would be to use the <code>closing()</code> decrorator:
<br /><div class="my_code_default">&nbsp;<span class="p_43">class</span>&nbsp;<span class="p_31">Farm</span><span class="p_38">(</span><span class="p_36">object</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">close</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">print</span>&nbsp;<span class="p_39">"Closing"</span><br /><br />&nbsp;<span class="p_43">from</span>&nbsp;<span class="p_36">contextlib</span>&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">closing</span><br />&nbsp;<span class="p_36">with</span>&nbsp;<span class="p_36">closing</span><span class="p_38">(</span><span class="p_36">Farm</span><span class="p_38">())</span>&nbsp;<span class="p_36">as</span>&nbsp;<span class="p_36">farm</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">pass</span><br />&nbsp;<span class="p_33">#&nbsp;this&nbsp;will&nbsp;print:</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;&nbsp;Closing</span><br /></div>
</p>
<p>So the <code>closing()</code> decorator "steals" the <code>__enter__</code> and <code>__exit__</code>. This last one can be handy if you do this:
<br /><div class="my_code_default">&nbsp;<span class="p_43">from</span>&nbsp;<span class="p_36">contextlib</span>&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">closing</span><br />&nbsp;<span class="p_36">with</span>&nbsp;<span class="p_36">closing</span><span class="p_38">(</span><span class="p_36">Farm</span><span class="p_38">())</span>&nbsp;<span class="p_36">as</span>&nbsp;<span class="p_36">farm</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">raise</span>&nbsp;<span class="p_36">ValueError</span><br /><br />&nbsp;<span class="p_33">#&nbsp;this&nbsp;will&nbsp;print</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;Closing</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;Traceback&nbsp;(most&nbsp;recent&nbsp;call&nbsp;last):</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;&nbsp;File&nbsp;"dummy.py",&nbsp;line&nbsp;16,&nbsp;in&nbsp;&lt;module&gt;</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;ValueError</span><br />&nbsp;<span class="p_33">#&nbsp;&nbsp;ValueError</span><br /></div>
</p>
<p>This is turning into my own loud thinking and I think I get it now. <code>contextlib.closing()</code> basically makes it possible to do what I did there with the <code>__enter__</code> and <code>__exit__</code> and it seems the <code>file()</code> built-in has a exit handler that takes care of the closing already so you don't have to do it with any extra decorators. </p>]]></description>
  <link>http://www.peterbe.com/plog/python-file-with-closing-automatically</link>
  <dc:subject>Python</dc:subject>
  <dc:date>2011-12-02T23:30:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/integrate-browserid-in-a-tornado-web-app">
  <title><![CDATA[Integrate BrowserID in a Tornado web app]]></title>
  <description><![CDATA[<p><a href="kwissle-login.png"><img src="http://www.peterbe.com/plog/integrate-browserid-in-a-tornado-web-app/display-thumbnail/kwissle-login.png" alt="Integrate BrowserID in a Tornado web app" class="floatright" border="1" /></a>
<a href="https://browserid.org/">BrowserID</a> is a new single sign-on initiative lead by <a href="http://www.mozilla.org/">Mozilla</a> that takes a very refreshing approach to single sign-on. It's basically like OpenID except better and similar to the OAuth solutions from Google, Twitter, Facebook, etc but without being tied to those closed third-parties. </p>
<p>At the moment, BrowserID is ready for production (I have it on <a href="http://kwissle.com/login/">Kwissle</a>) but the <a href="https://github.com/mozilla/browserid/wiki/How-to-Use-BrowserID-on-Your-Site">getting started docs</a> is still something that is under active development (I'm actually contributing to this). </p>
<p>Anyway, I thought I'd share how to integrate it with <a href="http://www.tornadoweb.org/">Tornado</a></p>
<p>First, you need to do the client-side of things. I use jQuery but that's not a requirement to be able to use BrowserID. Also, there are different "patterns" to do login. Either you have a header that either says "Sign in"/"Hi Your Username". Or you can have a dedicated page (e.g. <code>mysite.com/login/</code>). Let's, for simplicity sake, pretend we build a dedicated page to log in. First, add the necessary HTML:
<br /><div class="my_code_default">&nbsp;<span class="p_38">&lt;</span><span class="p_36">a</span>&nbsp;<span class="p_36">href</span><span class="p_38">=</span><span class="p_39">"#"</span>&nbsp;<span class="p_36">id</span><span class="p_38">=</span><span class="p_39">"browserid"</span>&nbsp;<span class="p_36">title</span><span class="p_38">=</span><span class="p_39">"Sign-in&nbsp;with&nbsp;BrowserID"</span><span class="p_38">&gt;</span><br />&nbsp;&nbsp;&nbsp;<span class="p_38">&lt;</span><span class="p_36">img</span>&nbsp;<span class="p_36">src</span><span class="p_38">=</span><span class="p_39">"/images/sign_in_blue.png"</span>&nbsp;<span class="p_36">alt</span><span class="p_38">=</span><span class="p_39">"Sign&nbsp;in"</span><span class="p_38">&gt;</span><br />&nbsp;<span class="p_38">&lt;/</span><span class="p_36">a</span><span class="p_38">&gt;</span><br />&nbsp;<span class="p_38">&lt;</span><span class="p_36">script</span>&nbsp;<span class="p_36">src</span><span class="p_38">=</span><span class="p_39">"https://browserid.org/include.js"</span>&nbsp;<span class="p_36">async</span><span class="p_38">&gt;&lt;/</span><span class="p_36">script</span><span class="p_38">&gt;</span><br /></div>
</p>
<p>Next you need the Javascript in place so that clicking on the link above will open the BrowserID pop-up:
<br /><div class="my_code_default">&nbsp;<span class="p_36">function</span>&nbsp;<span class="p_36">loggedIn</span><span class="p_38">(</span><span class="p_36">response</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;<span class="p_36">location</span><span class="p_38">.</span><span class="p_36">href</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">response</span><span class="p_38">.</span><span class="p_36">next_url</span><span class="p_38">;</span><br />&nbsp;&nbsp;&nbsp;<span class="p_38">/*</span>&nbsp;<span class="p_36">alternatively</span>&nbsp;<span class="p_36">you</span>&nbsp;<span class="p_36">could</span>&nbsp;<span class="p_36">do</span>&nbsp;<span class="p_36">something</span>&nbsp;<span class="p_36">like</span>&nbsp;<span class="p_36">this</span>&nbsp;<span class="p_36">instead</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$<span class="p_38">(</span><span class="p_30">'#header&nbsp;.loggedin'</span><span class="p_38">).</span><span class="p_36">show</span><span class="p_38">().</span><span class="p_36">text</span><span class="p_38">(</span><span class="p_30">'Hi&nbsp;'</span>&nbsp;<span class="p_38">+</span>&nbsp;<span class="p_36">response</span><span class="p_38">.</span><span class="p_36">first_name</span><span class="p_38">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">...</span><span class="p_43">or</span>&nbsp;<span class="p_36">something</span>&nbsp;<span class="p_36">like</span>&nbsp;<span class="p_36">that</span>&nbsp;<span class="p_38">*/</span><br />&nbsp;<span class="p_38">}</span><br /><br />&nbsp;<span class="p_36">function</span>&nbsp;<span class="p_36">gotVerifiedEmail</span><span class="p_38">(</span><span class="p_36">assertion</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;<span class="p_38">//</span>&nbsp;<span class="p_36">got</span>&nbsp;<span class="p_36">an</span>&nbsp;<span class="p_36">assertion</span><span class="p_38">,</span>&nbsp;<span class="p_36">now</span>&nbsp;<span class="p_36">send</span>&nbsp;<span class="p_36">it</span>&nbsp;<span class="p_36">up</span>&nbsp;<span class="p_36">to</span>&nbsp;<span class="p_36">the</span>&nbsp;<span class="p_36">server</span>&nbsp;<span class="p_43">for</span>&nbsp;<span class="p_36">verification</span><br />&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_38">(</span><span class="p_36">assertion</span>&nbsp;<span class="p_38">!==</span>&nbsp;<span class="p_36">null</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;$<span class="p_38">.</span><span class="p_36">ajax</span><span class="p_38">({</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">type</span><span class="p_38">:</span>&nbsp;<span class="p_30">'POST'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">url</span><span class="p_38">:</span>&nbsp;<span class="p_30">'/auth/login/browserid/'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">data</span><span class="p_38">:</span>&nbsp;<span class="p_38">{</span>&nbsp;<span class="p_36">assertion</span><span class="p_38">:</span>&nbsp;<span class="p_36">assertion</span>&nbsp;<span class="p_38">},</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">success</span><span class="p_38">:</span>&nbsp;<span class="p_36">function</span><span class="p_38">(</span><span class="p_36">res</span><span class="p_38">,</span>&nbsp;<span class="p_36">status</span><span class="p_38">,</span>&nbsp;<span class="p_36">xhr</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_38">(</span><span class="p_36">res</span>&nbsp;<span class="p_38">===</span>&nbsp;<span class="p_36">null</span><span class="p_38">)</span>&nbsp;<span class="p_38">{}//</span><span class="p_36">loggedOut</span><span class="p_38">();</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">else</span>&nbsp;<span class="p_36">loggedIn</span><span class="p_38">(</span><span class="p_36">res</span><span class="p_38">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">},</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">error</span><span class="p_38">:</span>&nbsp;<span class="p_36">function</span><span class="p_38">(</span><span class="p_36">res</span><span class="p_38">,</span>&nbsp;<span class="p_36">status</span><span class="p_38">,</span>&nbsp;<span class="p_36">xhr</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">alert</span><span class="p_38">(</span><span class="p_39">"login&nbsp;failure"</span>&nbsp;<span class="p_38">+</span>&nbsp;<span class="p_36">res</span><span class="p_38">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">}</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">});</span><br />&nbsp;&nbsp;<span class="p_38">}</span><br />&nbsp;&nbsp;<span class="p_43">else</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">//</span><span class="p_36">loggedOut</span><span class="p_38">();</span><br />&nbsp;&nbsp;<span class="p_38">}</span><br />&nbsp;<span class="p_38">}</span><br /><br />&nbsp;$<span class="p_38">(</span><span class="p_36">function</span><span class="p_38">()</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;$<span class="p_38">(</span><span class="p_30">'#browserid'</span><span class="p_38">).</span><span class="p_36">click</span><span class="p_38">(</span><span class="p_36">function</span><span class="p_38">()</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">navigator</span><span class="p_38">.</span><span class="p_36">id</span><span class="p_38">.</span><span class="p_36">getVerifiedEmail</span><span class="p_38">(</span><span class="p_36">gotVerifiedEmail</span><span class="p_38">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">return</span>&nbsp;<span class="p_36">false</span><span class="p_38">;</span><br />&nbsp;&nbsp;&nbsp;<span class="p_38">});</span><br />&nbsp;<span class="p_38">});</span><br /></div>
</p>
<p>Next up is the server-side part of BrowserID. Your job is to take the assertion that is given to you by the AJAX POST and trade that with https://browserid.org for an email address:
<br /><div class="my_code_default">&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">urllib</span><br />&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">web</span><br />&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">escape</span>&nbsp;<br />&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">httpclient</span><br />&nbsp;<span class="p_38">...</span><br /><br />&nbsp;<span class="p_decorator">@route('/auth/login/browserid/')&nbsp;&nbsp;</span><span class="p_33">#&nbsp;...or&nbsp;whatever&nbsp;you&nbsp;use</span><br />&nbsp;<span class="p_43">class</span>&nbsp;<span class="p_31">BrowserIDAuthLoginHandler</span><span class="p_38">(</span><span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">web</span><span class="p_38">.</span><span class="p_36">RequestHandler</span><span class="p_38">):</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">check_xsrf_cookie</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span>&nbsp;&nbsp;<span class="p_33">#&nbsp;more&nbsp;about&nbsp;this&nbsp;later</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">pass</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_decorator">@tornado.web.asynchronous</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">post</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">assertion</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">get_argument</span><span class="p_38">(</span><span class="p_30">'assertion'</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">http_client</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">httpclient</span><span class="p_38">.</span><span class="p_36">AsyncHTTPClient</span><span class="p_38">()</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">domain</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">request</span><span class="p_38">.</span><span class="p_36">host</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">url</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_30">'https://browserid.org/verify'</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">data</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_30">'assertion'</span><span class="p_38">:</span>&nbsp;<span class="p_36">assertion</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_30">'audience'</span><span class="p_38">:</span>&nbsp;<span class="p_36">domain</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">}</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">response</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">http_client</span><span class="p_38">.</span><span class="p_36">fetch</span><span class="p_38">(</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">url</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">method</span><span class="p_38">=</span><span class="p_30">'POST'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">body</span><span class="p_38">=</span><span class="p_36">urllib</span><span class="p_38">.</span><span class="p_36">urlencode</span><span class="p_38">(</span><span class="p_36">data</span><span class="p_38">),</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">callback</span><span class="p_38">=</span><span class="p_36">self</span><span class="p_38">.</span><span class="p_36">async_callback</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">.</span><span class="p_36">_on_response</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">)</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">_on_response</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">,</span>&nbsp;<span class="p_36">response</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">struct</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">escape</span><span class="p_38">.</span><span class="p_36">json_decode</span><span class="p_38">(</span><span class="p_36">response</span><span class="p_38">.</span><span class="p_36">body</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_36">struct</span><span class="p_38">[</span><span class="p_30">'status'</span><span class="p_38">]</span>&nbsp;<span class="p_38">!=</span>&nbsp;<span class="p_30">'okay'</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">raise</span>&nbsp;<span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">web</span><span class="p_38">.</span><span class="p_36">HTTPError</span><span class="p_38">(</span><span class="p_37">400</span><span class="p_38">,</span>&nbsp;<span class="p_39">"Failed&nbsp;assertion&nbsp;test"</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">email</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">struct</span><span class="p_38">[</span><span class="p_30">'email'</span><span class="p_38">]</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">set_secure_cookie</span><span class="p_38">(</span><span class="p_30">'user'</span><span class="p_38">,</span>&nbsp;<span class="p_36">email</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">expires_days</span><span class="p_38">=</span><span class="p_37">1</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">set_header</span><span class="p_38">(</span><span class="p_39">"Content-Type"</span><span class="p_38">,</span>&nbsp;<span class="p_39">"application/json;&nbsp;charset=UTF-8"</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">response</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_38">{</span><span class="p_30">'next_url'</span><span class="p_38">:</span>&nbsp;<span class="p_30">'/'</span><span class="p_38">}</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">write</span><span class="p_38">(</span><span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">escape</span><span class="p_38">.</span><span class="p_36">json_encode</span><span class="p_38">(</span><span class="p_36">response</span><span class="p_38">))</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">finish</span><span class="p_38">()</span><br /></div>
</p>
<p>Now that should get you up and running. There's of couse a tonne of things that can be improved. Number one thing to improve is to use XSRF on the AJAX POST. The simplest way to do that would be to somehow dump the XSRF token generated into your page and include it in the AJAX POST. Perhaps something like this:
<br /><div class="my_code_default">&nbsp;<span class="p_38">&lt;</span><span class="p_36">script</span><span class="p_38">&gt;</span><br />&nbsp;<span class="p_36">var</span>&nbsp;<span class="p_36">_xsrf</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_30">'{{&nbsp;xsrf_token&nbsp;}}'</span><span class="p_38">;</span><br />&nbsp;<span class="p_38">...</span><br />&nbsp;<span class="p_36">function</span>&nbsp;<span class="p_36">gotVerifiedEmail</span><span class="p_38">(</span><span class="p_36">assertion</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span><br />&nbsp;&nbsp;<span class="p_38">//</span>&nbsp;<span class="p_36">got</span>&nbsp;<span class="p_36">an</span>&nbsp;<span class="p_36">assertion</span><span class="p_38">,</span>&nbsp;<span class="p_36">now</span>&nbsp;<span class="p_36">send</span>&nbsp;<span class="p_36">it</span>&nbsp;<span class="p_36">up</span>&nbsp;<span class="p_36">to</span>&nbsp;<span class="p_36">the</span>&nbsp;<span class="p_36">server</span>&nbsp;<span class="p_43">for</span>&nbsp;<span class="p_36">verification</span><br />&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_38">(</span><span class="p_36">assertion</span>&nbsp;<span class="p_38">!==</span>&nbsp;<span class="p_36">null</span><span class="p_38">)</span>&nbsp;<span class="p_38">{</span>&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$<span class="p_38">.</span><span class="p_36">ajax</span><span class="p_38">({</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">type</span><span class="p_38">:</span>&nbsp;<span class="p_30">'POST'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">url</span><span class="p_38">:</span>&nbsp;<span class="p_30">'/auth/login/browserid/'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">data</span><span class="p_38">:</span>&nbsp;<span class="p_38">{</span>&nbsp;<span class="p_36">assertion</span><span class="p_38">:</span>&nbsp;<span class="p_36">assertion</span><span class="p_38">,</span>&nbsp;<span class="p_36">_xsrf</span><span class="p_38">:</span>&nbsp;<span class="p_36">_xsrf</span>&nbsp;<span class="p_38">},</span><br />&nbsp;<span class="p_38">...</span>&nbsp;<br />&nbsp;<span class="p_38">&lt;/</span><span class="p_36">script</span><span class="p_38">&gt;</span><br /></div>
</p>
<p>Another thing that could obviously do with a re-write is the way users are handled server-side. In the example above I just set the asserted user's email address in a secure cookie. More realistically, you'll have a database of users who you match by email address but instead store their database ID in a cookie or something like that. </p>
<p>What's so neat about solutions such as OpenID, BrowserID, etc. is that you can combine two things in one process: Sign-in and Registration. In your app, all you need to do is a simple if statement in the code like this:
<br /><div class="my_code_default">&nbsp;<span class="p_36">user</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">db</span><span class="p_38">.</span><span class="p_36">User</span><span class="p_38">.</span><span class="p_36">find_by_email</span><span class="p_38">(</span><span class="p_36">email</span><span class="p_38">)</span>&nbsp;<br />&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_43">not</span>&nbsp;<span class="p_36">user</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">user</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">db</span><span class="p_38">.</span><span class="p_36">User</span><span class="p_38">()</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">user</span><span class="p_38">.</span><span class="p_36">email</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">email</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">user</span><span class="p_38">.</span><span class="p_36">save</span><span class="p_38">()</span><br />&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">set_secure_cookie</span><span class="p_38">(</span><span class="p_30">'user'</span><span class="p_38">,</span>&nbsp;<span class="p_36">str</span><span class="p_38">(</span><span class="p_36">user</span><span class="p_38">.</span><span class="p_36">id</span><span class="p_38">))</span><br /></div>
</p>
<p>Hopefully that'll encourage a couple of more Tornadonauts to give BrowserID a try. </p>]]></description>
  <link>http://www.peterbe.com/plog/integrate-browserid-in-a-tornado-web-app</link>
  <dc:subject>Tornado</dc:subject>
  <dc:date>2011-11-21T21:20:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/trivial-but-powerful-tips-for-nosetests">
  <title><![CDATA[Trivial but powerful tips for nosetests]]></title>
  <description><![CDATA[<p>I'm clearly still a <a href="http://readthedocs.org/docs/nose/en/latest/">nosetests</a> beginner because it was only today that I figured out how to set certain plugins to always be on.</p>
<p>First of all you might like these plugins too:
<pre>
 $ pip install rudolf
 $ pip install disabledoc
</pre>
</p>
<p>Docs: <a href="http://pypi.python.org/pypi/rudolf/">rudolf</a> and <a href="https://github.com/kumar303/disable-docstring">disabledoc</a></p>
<p>To get these gorgeous little tricks into every run of <code>nosetests</code> edit the file <code>~/.noserc</code> and add the following:
<pre>
 [nosetests]
 with-disable-docstring=1
 with-color=1
</pre>
</p>
<p>That should make your life a little easier. </p>
<p><strong>UPDATE:</strong></p>
<p>I've since managed to shoot myself in both legs with messing around with nosetests plugins because I heavily rely on <a href="https://github.com/jbalogh/django-nose">django-nose</a> in Django. Long story short: be careful if you get strange import related errors!</p>]]></description>
  <link>http://www.peterbe.com/plog/trivial-but-powerful-tips-for-nosetests</link>
  <dc:subject>Python</dc:subject>
  <dc:date>2011-11-18T21:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/going-real-simple-on-html5-audio">
  <title><![CDATA[Going real simple on HTML5 audio]]></title>
  <description><![CDATA[<p><a href="http://donecal.com">DoneCal</a> users are to 80+% Chrome and Firefox users. Both Firefox and Chrome support the HTML <code>&lt;audio&gt;</code> element without any weird plugins and they both support the Ogg Vorbis (<code>.ogg</code>) file format. <a href="https://github.com/peterbe/worklog/commit/98e65c873ff00c88bfa362758c7ce16c332217e7">change log here</a></p>
<p>So, I used use the rather enterprisey plugin called <a href="http://www.schillmania.com/projects/soundmanager2/">SoundManager2</a> which attempts to abstract away all hacks into one single API. It uses a mix of browser sniffing, HTML5 and Flash. Although very promising, it is quite cumbersome. It doesn't work flawlessly despite their hard efforts. Unfortunately, using it also means a 30kb (optimized) Javascript file and a 3kb <code>.swf</code> file (if needed). So, instead of worrying about my very few Internet Explorer users I decided to go really dumb and simple on this.</p>
<p>The solution basically looks like this:
<br /><div class="my_code_default">&nbsp;<span class="c_21">//&nbsp;somewhere.js<br /></span>&nbsp;<span class="c_24">var</span>&nbsp;<span class="c_24">SOUND_URLS</span>&nbsp;<span class="c_26">=</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;<span class="c_24">foo</span><span class="c_26">:</span>&nbsp;<span class="c_16">'path/to/foo.ogg'</span><span class="c_26">,</span><br />&nbsp;&nbsp;&nbsp;<span class="c_24">egg</span><span class="c_26">:</span>&nbsp;<span class="c_16">'path/to/egg.ogg'</span><br />&nbsp;<span class="c_26">};</span><br /><br />&nbsp;<span class="c_21">//&nbsp;play-sounds.js<br /></span><br />&nbsp;<span class="c_17">/*&nbsp;Call&nbsp;to&nbsp;create&nbsp;and&nbsp;partially&nbsp;download&nbsp;the&nbsp;audo&nbsp;element.<br />&nbsp;&nbsp;*&nbsp;You&nbsp;can&nbsp;all&nbsp;this&nbsp;as&nbsp;much&nbsp;as&nbsp;you&nbsp;like.&nbsp;*/</span><br />&nbsp;<span class="c_24">function</span>&nbsp;<span class="c_24">preload_sound</span><span class="c_26">(</span><span class="c_24">key</span><span class="c_26">)</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;<span class="c_24">var</span>&nbsp;<span class="c_24">id</span>&nbsp;<span class="c_26">=</span>&nbsp;<span class="c_16">'sound-'</span>&nbsp;<span class="c_26">+</span>&nbsp;<span class="c_24">key</span><span class="c_26">;</span><br />&nbsp;&nbsp;<span class="c_33">if</span>&nbsp;<span class="c_26">(!</span><span class="c_24">document</span><span class="c_26">.</span><span class="c_24">getElementById</span><span class="c_26">(</span><span class="c_24">id</span><span class="c_26">))</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_33">if</span>&nbsp;<span class="c_26">(!</span><span class="c_24">SOUND_URLS</span><span class="c_26">[</span><span class="c_24">key</span><span class="c_26">])</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_33">throw</span>&nbsp;<span class="c_29">"Sound&nbsp;for&nbsp;'"</span>&nbsp;<span class="c_26">+</span>&nbsp;<span class="c_24">key</span>&nbsp;<span class="c_26">+</span>&nbsp;<span class="c_29">"'&nbsp;not&nbsp;defined"</span><span class="c_26">;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_26">}</span>&nbsp;<span class="c_33">else</span>&nbsp;<span class="c_33">if</span>&nbsp;<span class="c_26">(</span><span class="c_24">SOUND_URLS</span><span class="c_26">[</span><span class="c_24">key</span><span class="c_26">].</span><span class="c_24">search</span><span class="c_26">(</span><span class="c_28">/\.ogg/i</span><span class="c_26">)</span>&nbsp;<span class="c_26">==</span>&nbsp;<span class="c_26">-</span><span class="c_25">1</span><span class="c_26">)</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_33">throw</span>&nbsp;<span class="c_29">"Sound&nbsp;for&nbsp;'"</span>&nbsp;<span class="c_26">+</span>&nbsp;<span class="c_24">key</span>&nbsp;<span class="c_26">+</span>&nbsp;<span class="c_29">"'&nbsp;must&nbsp;be&nbsp;.ogg&nbsp;URL"</span><span class="c_26">;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_26">}</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">var</span>&nbsp;<span class="c_24">a</span>&nbsp;<span class="c_26">=</span>&nbsp;<span class="c_24">document</span><span class="c_26">.</span><span class="c_24">createElement</span><span class="c_26">(</span><span class="c_16">'audio'</span><span class="c_26">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">a</span><span class="c_26">.</span><span class="c_24">setAttribute</span><span class="c_26">(</span><span class="c_16">'id'</span><span class="c_26">,</span>&nbsp;<span class="c_24">id</span><span class="c_26">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">a</span><span class="c_26">.</span><span class="c_24">setAttribute</span><span class="c_26">(</span><span class="c_16">'src'</span><span class="c_26">,</span>&nbsp;<span class="c_24">SOUND_URLS</span><span class="c_26">[</span><span class="c_24">key</span><span class="c_26">]);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">document</span><span class="c_26">.</span><span class="c_24">body</span><span class="c_26">.</span><span class="c_24">appendChild</span><span class="c_26">(</span><span class="c_24">a</span><span class="c_26">);</span><br />&nbsp;&nbsp;<span class="c_26">}</span><br />&nbsp;&nbsp;<span class="c_33">return</span>&nbsp;<span class="c_24">id</span><span class="c_26">;</span><br />&nbsp;<span class="c_26">}</span><br /><br />&nbsp;<span class="c_24">function</span>&nbsp;<span class="c_24">play_sound</span><span class="c_26">(</span><span class="c_24">key</span><span class="c_26">)</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;<span class="c_24">document</span><span class="c_26">.</span><span class="c_24">getElementById</span><span class="c_26">(</span><span class="c_24">preload_sound</span><span class="c_26">(</span><span class="c_24">key</span><span class="c_26">)).</span><span class="c_24">play</span><span class="c_26">();</span><br />&nbsp;<span class="c_26">}</span><br /><br />&nbsp;<span class="c_21">//&nbsp;elsewhere.js<br /></span>&nbsp;<span class="c_24">$</span><span class="c_26">.</span><span class="c_24">lightbox</span><span class="c_26">.</span><span class="c_24">open</span><span class="c_26">({</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">onComplete</span><span class="c_26">:</span>&nbsp;<span class="c_24">function</span><span class="c_26">()</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">preload_sound</span><span class="c_26">(</span><span class="c_16">'foo'</span><span class="c_26">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_26">}</span><br />&nbsp;<span class="c_26">});</span><br />&nbsp;<span class="c_24">$</span><span class="c_26">(</span><span class="c_16">'#lightbox&nbsp;button'</span><span class="c_26">).</span><span class="c_24">click</span><span class="c_26">(</span><span class="c_24">function</span><span class="c_26">()</span>&nbsp;<span class="c_26">{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="c_24">play_sound</span><span class="c_26">(</span><span class="c_16">'foo'</span><span class="c_26">);</span><br />&nbsp;<span class="c_26">});</span><br /></div>
</p>
<p>Basically, only Firefox, Chrome and Opera support <code>.ogg</code> but it's a good and open source encoding so I don't mind being a bit of an asshole about it. This little script could be slightly extended with some browser sniffing to work with Safari people but right now it doesn't feel like it's worth the effort. </p>
<p>This make me happy and I feel lean and light. A good feeling!</p>]]></description>
  <link>http://www.peterbe.com/plog/going-real-simple-on-html5-audio</link>
  <dc:subject>Web development</dc:subject>
  <dc:date>2011-10-13T22:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/too-cool-for-me-everyone">
  <title><![CDATA[New feature on Too Cool For Me: Everyone I follow]]></title>
  <description><![CDATA[<p><a href="http://toocoolfor.me/screenshots#5"><img src="http://www.peterbe.com/plog/too-cool-for-me-everyone/display-thumbnail/everyone.png" alt="New feature on Too Cool For Me: Everyone I follow" class="floatright" border="1" /></a>
I've added a new feature to <a href="http://toocoolfor.me/">Too Cool For Me</a> that lists all the users that you follow and splits them up into "Follows me" and "Too cool for me".</p>
<p>To try it you have to authenticate with Twitter (READ ONLY mode) then go to <a href="http://toocoolfor.me/everyone">toocoolfor.me/everyone</a></p>
<p>This means you can use Too Cool For Me without having to use the Bookmarklet.</p>]]></description>
  <link>http://www.peterbe.com/plog/too-cool-for-me-everyone</link>
  <dc:subject>Web development</dc:subject>
  <dc:date>2011-10-08T17:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/cateechee-golf-pictures">
  <title><![CDATA[Cateechee golf pictures]]></title>
  <description><![CDATA[<p><a href="http://joshziff.smugmug.com/Friends/Cateechee-Golf-2011/"><img src="http://www.peterbe.com/plog/cateechee-golf-pictures/display-thumbnail/Cateechee-Golf-2011-003-L.jpg" alt="Cateechee golf pictures" class="floatright" border="1" /></a>
My friend Josh Ziff has uploaded pictures from the little golf tournament (we played scrambles) at <a href="http://www.cateechee.com/">Cateechee</a> in <a href="http://maps.google.com/maps?q=Hartwell,+GA,+USA&hl=en&sll=51.482207,-0.073696&sspn=0.008632,0.017359&vpsrc=0&t=m&z=14">Hartwell, Georgia</a> a couple of weeks ago. </p>
<p><a href="http://joshziff.smugmug.com/Friends/Cateechee-Golf-2011/">Check out the pictures here</a></p>]]></description>
  <link>http://www.peterbe.com/plog/cateechee-golf-pictures</link>
  <dc:subject>Misc. links</dc:subject>
  <dc:date>2011-09-27T09:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/too-cool-for-me">
  <title><![CDATA[Too Cool For Me?]]></title>
  <description><![CDATA[<p><a href="http://www.peterbe.com/plog/too-cool-for-me/screenshot.png"><img src="http://www.peterbe.com/plog/too-cool-for-me/display-thumbnail/screenshot.png" alt="Too Cool For Me?" class="floatright" border="1" /></a>
<a href="http://toocoolfor.me">Too Cool For Me?</a> is a fun little side-project I've been working on. It's all about and only for <a href="http://twitter.com/">Twitter</a>. You login, then install a <a href="http://en.wikipedia.org/wiki/Bookmarklet">bookmarklet</a> then when browsing twitter you can see who follows you and who is too cool for you. </p>
<p>For me it's a chance to try some new tech and at the same time scratch an itch I had. The results can be quite funny but also sad too when you realise that someone uncool isn't following you even though you follow him/her. </p>
<p>The code is open source and available on <a href="https://github.com/peterbe/toocool">Github</a> and at least it might help people see how to do a web app in <a href="http://www.tornadoweb.org">Tornado</a> using <a href="http://www.mongodb.com">MongoDB</a> and asynchronous requests to the <a href="https://dev.twitter.com/">Twitter API</a></p>]]></description>
  <link>http://www.peterbe.com/plog/too-cool-for-me</link>
  <dc:subject>Tornado</dc:subject>
  <dc:date>2011-09-25T14:00:00+00:00</dc:date>
</item>

<item rdf:about="http://www.peterbe.com/plog/tornado-utils-send_mail">
  <title><![CDATA[Goodies from tornado-utils - part 3: send_mail]]></title>
  <description><![CDATA[<p>This is Part 3 in a series of blogs about various bits and pieces in the <a href="https://github.com/peterbe/tornado-utils">tornado-utils</a> package. <a href="http://www.peterbe.com/plog/goodies-from-tornado-utils-testclient">Part 1 is here</a> and <a href="http://www.peterbe.com/plog/tornado-utils-tornado_static">part 2 is here</a></p>
<p><strong>send_mail</strong></p>
<p><a href="https://github.com/peterbe/tornado-utils/tree/master/tornado_utils/send_mail">Code is here</a></p>
<p>First of all, I should say: I didn't write much of this code. It's copied from Django and modified to work in any Tornado app. It hinges on the same idea as Django that you have to specify what backend you want to use. A backend is an importable string pointing to a class that has the <code>send_messages</code> method. </p>
<p>To begin, here's a sample use case inside a request handler:
<br /><div class="my_code_default">&nbsp;<span class="p_43">class</span>&nbsp;<span class="p_31">ContactUs</span><span class="p_38">(</span><span class="p_36">tornado</span><span class="p_38">.</span><span class="p_36">web</span><span class="p_38">.</span><span class="p_36">RequestHandler</span><span class="p_38">):</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">post</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">msg</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">get_argument</span><span class="p_38">(</span><span class="p_30">'msg'</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_33">#&nbsp;NB:&nbsp;you&nbsp;might&nbsp;want&nbsp;to&nbsp;set&nbsp;this&nbsp;once&nbsp;and&nbsp;for&nbsp;all&nbsp;in&nbsp;something&nbsp;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_33">#&nbsp;like&nbsp;self.application.settings['email_backend']</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">backend</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_30">'tornado_utils.send_mail.backends.smtp.EmailBackend'</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">send_email</span><span class="p_38">(</span><span class="p_36">backend</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_39">"New&nbsp;contact&nbsp;form&nbsp;entry"</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">msg</span>&nbsp;<span class="p_38">+</span>&nbsp;<span class="p_30">'\n\n--\nFrom&nbsp;our&nbsp;contact&nbsp;form\n'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_30">'noreply@example.com'</span><span class="p_38">,</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">[</span><span class="p_30">'webmaster@example.com'</span><span class="p_38">],</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">write</span><span class="p_38">(</span><span class="p_39">"Thanks!"</span><span class="p_38">)</span><br /></div>
</p>
<p>The problem is that SMTP is slow. Even though, in human terms, it's fast, it's still too slow for a non-blocking server that Tornado is. Taking 1-2 seconds to send a message over SMTP means it's blocking every other request to Tornado for 1-2 seconds. The solution is instead save the message on disk in pickled form and use a cron job to pick up the messages and send them by SMTP instead, outside the Tornado process. First do this re-write:
<br /><div class="my_code_default">&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">...</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">post</span><span class="p_38">(</span><span class="p_36">self</span><span class="p_38">):</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">msg</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">self</span><span class="p_38">.</span><span class="p_36">get_argument</span><span class="p_38">(</span><span class="p_30">'msg'</span><span class="p_38">)</span><br /><span class="p_38">-</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">backend</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_30">'tornado_utils.send_mail.backends.smtp.EmailBackend'</span><br /><span class="p_38">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">backend</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_30">'tornado_utils.send_mail.backends.pickle.EmailBackend'</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_38">...</span><br /></div>
</p>
<p>Now, write a cron job script that looks something like this:
<br /><div class="my_code_default">&nbsp;<span class="p_33">#&nbsp;send_pickled_messages.py</span><br />&nbsp;<span class="p_36">DRY_RUN</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">False</span><br /><br />&nbsp;<span class="p_43">def</span>&nbsp;<span class="p_35">main</span><span class="p_38">():</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">from</span>&nbsp;<span class="p_36">tornado_utils</span><span class="p_38">.</span><span class="p_36">send_mail</span>&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">config</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">filenames</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">glob</span><span class="p_38">(</span><span class="p_36">os</span><span class="p_38">.</span><span class="p_36">path</span><span class="p_38">.</span><span class="p_36">join</span><span class="p_38">(</span><span class="p_36">config</span><span class="p_38">.</span><span class="p_36">PICKLE_LOCATION</span><span class="p_38">,</span>&nbsp;<span class="p_30">'*.pickle'</span><span class="p_38">))</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">filenames</span><span class="p_38">.</span><span class="p_36">sort</span><span class="p_38">()</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_43">not</span>&nbsp;<span class="p_36">filenames</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">return</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">from</span>&nbsp;<span class="p_36">tornado_utils</span><span class="p_38">.</span><span class="p_36">send_mail</span>&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">backends</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">import</span>&nbsp;<span class="p_36">cPickle</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_36">DRY_RUN</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">EmailBackend</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">backends</span><span class="p_38">.</span><span class="p_36">console</span><span class="p_38">.</span><span class="p_36">EmailBackend</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">else</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">EmailBackend</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">backends</span><span class="p_38">.</span><span class="p_36">smtp</span><span class="p_38">.</span><span class="p_36">EmailBackend</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">max_count</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_37">10</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">filenames</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">filenames</span><span class="p_38">[:</span><span class="p_36">max_count</span><span class="p_38">]</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">messages</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_38">[</span><span class="p_36">cPickle</span><span class="p_38">.</span><span class="p_36">load</span><span class="p_38">(</span><span class="p_36">open</span><span class="p_38">(</span><span class="p_36">x</span><span class="p_38">,</span>&nbsp;<span class="p_30">'rb'</span><span class="p_38">))</span>&nbsp;<span class="p_43">for</span>&nbsp;<span class="p_36">x</span>&nbsp;<span class="p_43">in</span>&nbsp;<span class="p_36">filenames</span><span class="p_38">]</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">backend</span>&nbsp;<span class="p_38">=</span>&nbsp;<span class="p_36">EmailBackend</span><span class="p_38">()</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">backend</span><span class="p_38">.</span><span class="p_36">send_messages</span><span class="p_38">(</span><span class="p_36">messages</span><span class="p_38">)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">if</span>&nbsp;<span class="p_43">not</span>&nbsp;<span class="p_36">DRY_RUN</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_43">for</span>&nbsp;<span class="p_36">filename</span>&nbsp;<span class="p_43">in</span>&nbsp;<span class="p_36">filenames</span><span class="p_38">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p_36">os</span><span class="p_38">.</span><span class="p_36">remove</span><span class="p_38">(</span><span class="p_36">filename</span><span class="p_38">)</span><br /></div>
</p>
<p>That code just above is butchered from a more comprehensive script I have but you get the idea. Writing to a pickle file is so fast it's in the lower milliseconds region. However, it depends on disk IO so if you need more speed, write a simple backend that writes instead of saving pickles on disk, make it write to a fast in-memory database like Redis or Memcache.  </p>
<p>The code isn't new and it's been battle tested but it's only really been battle tested in the way that my apps use it. So you might stumble across bugs if you use it in a way I haven't tested. However, the code is Open Source and happily available for you to help out and improve. </p>]]></description>
  <link>http://www.peterbe.com/plog/tornado-utils-send_mail</link>
  <dc:subject>Tornado</dc:subject>
  <dc:date>2011-09-23T20:00:00+00:00</dc:date>
</item>

</rdf:RDF>
