<?xml version="1.0" encoding="iso-8859-1"?>
<rdf:RDF 
  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/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:cc="http://web.resource.org/cc/"
  xmlns="http://purl.org/rss/1.0/"
>
<channel>
    <title>spaceblog</title>
    <link>http://spacepants.org/blog/</link>
    <description>spaceblog</description>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>All content Copyright &#169; 2004,2005 Jamie Wilkinson.

Permission to use, copy, modify and distribute the contents of this blog for any purpose and without fee is hereby granted in perpetuity, provided that the above copyright notice and this paragraph appear in all copies. The copyright holders make no representation about the suitability of the specification for any purpose. It is provided "as is" without expressed or implied warranty.
</dc:rights>
    <dc:date>2005-05-03T17:17+10:00</dc:date>
    <sy:updatePeriod>daily</sy:updatePeriod>
    <sy:updateFrequency>2</sy:updateFrequency>
    <sy:updateBase>2005-05-03T17:17+10:00</sy:updateBase>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
    <image rdf:resource="http://spacepants.org/src/glsnake.png" />
    <items>
        <rdf:Seq>
	    <rdf:li resource="http://spacepants.org/blog/welcome-deps" />
	    <rdf:li resource="http://spacepants.org/blog/movember-2007" />
	    <rdf:li resource="http://spacepants.org/blog/nsscache-launch" />
	    <rdf:li resource="http://spacepants.org/blog/blog-o-the-day-lca2007-2" />
	    <rdf:li resource="http://spacepants.org/blog/blog-o-the-day-lca2007-1" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-programme-choices" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-best-linux-conference" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-boned-rego-but-fixed-now" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-registrations-open" />
	    <rdf:li resource="http://spacepants.org/blog/movember-2006" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-programme-first-draft" />
	    <rdf:li resource="http://spacepants.org/blog/new-programming-music" />
	    <rdf:li resource="http://spacepants.org/blog/no-longer-fucking-amazing" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-cfp-updates-2" />
	    <rdf:li resource="http://spacepants.org/blog/woo-new-command" />
	    <rdf:li resource="http://spacepants.org/blog/wrapping-cgi-apps-in-wsgi" />
	    <rdf:li resource="http://spacepants.org/blog/more-than-a-feeling" />
	    <rdf:li resource="http://spacepants.org/blog/pylons-gotchas" />
	    <rdf:li resource="http://spacepants.org/blog/lca-proposals-so-far" />
	    <rdf:li resource="http://spacepants.org/blog/unittest-urllib2-mock-object" />
	    <rdf:li resource="http://spacepants.org/blog/lca2007-cfp-updates" />
	    <rdf:li resource="http://spacepants.org/blog/erlang-sudoku-solver" />
	    <rdf:li resource="http://spacepants.org/blog/sysadmin-day-2006" />
	    <rdf:li resource="http://spacepants.org/blog/pylons-0.9-out" />
	    <rdf:li resource="http://spacepants.org/blog/mock-ldap-server-object" />
	    <rdf:li resource="http://spacepants.org/blog/paste-fixture-dummy-smtplib" />
	    <rdf:li resource="http://spacepants.org/blog/seven-cfp-open" />
	    <rdf:li resource="http://spacepants.org/blog/pylons-paste-stack" />
	    <rdf:li resource="http://spacepants.org/blog/tdd-promotes-good-health" />
	    <rdf:li resource="http://spacepants.org/blog/wah-wah-spam-redux" />
        </rdf:Seq>
    </items>
</channel>
<image rdf:about="http://spacepants.org/src/glsnake.png">
    <title>spacepants.org</title>
    <link>http://spacepants.org/blog/</link>
    <url>http://spacepants.org/src/glsnake.png</url>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</image>
	<item rdf:about="http://spacepants.org/blog/welcome-deps">
    <title>I, for one, welcome our new enhancement proposals</title>
    <link>http://spacepants.org/blog/welcome-deps</link>
    <dc:date>2008-02-04T13:08+10:00</dc:date>
    <content:encoded><![CDATA[

<p>I just read with excitement <a href="http://lists.debian.org/debian-project/2008/01/msg00045.html">the announcement of Debian Enhancement Proposals</a>, something that I too have been contemplating in recent months (but due to my ghastly lack of commitment to the Debian community doubted my ability to drive it).
</p>
<p>I work in company driven by engineering documents and designs, I like RFCs, and I like what Python has done with its PEPs.  Debian's adoption of this useful tool can only improve the community and the distribution.
</p>
<p> <a href="http://dep.debian.net/">Yay!</a> 
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/movember-2007">
    <title>Mo rides, 20c</title>
    <link>http://spacepants.org/blog/movember-2007</link>
    <dc:date>2007-11-19T23:47+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Hello!
</p>
<p>For the last 19 days I have been growing a filthy mo, again. Why?  Because it was so much fun last time, raising money and awareness for the fight against male depression and prostate cancer.
</p>
<ul>
 <li>
     Depression affects 1 in 6 men...Most don't seek help. Untreated depression is a leading risk factor for suicide.
 </li>

 <li>
     Last year in Australia 18,700 men were diagnosed with prostate cancer and more than 2,900 died of prostate cancer - equivalent to the number of women who die from breast cancer annually.
 </li>

 <li>
     Men are far less healthy than women. The average life expectancy of males is 5 years less than females.
 </li>
</ul>
<p>To sponsor my Mo please go to <a href="http://www.movember.com/au/donate/?action=sponsorlink&rego=79829">http://www.movember.com/au/donate</a>, enter my registration number which is 79829 and your credit card details. Or you can sponsor me by cheque made payable to the "Movember Foundation" clearly marking the donation as being for my Registration Number: 79829. Please mail cheques to: PO Box 292, Prahran VIC 3181. All donations over $2 are tax deductible.
</p>
<p>The money raised by Movember is donated to the Prostate Cancer Foundation of Australia and beyondblue - the national depression initiative, which will use the funds to create awareness, fund research and increase support networks for those men who suffer from prostate cancer and male depression.
</p>
<p>For those that have supported Movember in previous years you can be very
   proud of the impact it has had and can check out the detail at: <a href="http://www.movember.com/au/outcomes/07/Fundraising-Outcomes">Fundraising Outcomes</a>.
</p>
<p>Movember culminates at the end of the month at the Gala Parties. These glamorous and groomed events will see Tom Selleck and Borat look-a-likes battle it out for their chance to take home the prestigious Man of Movember title. If you would like to be part of this great night you'll need to purchase a <a href="http://www.movember.com/au/galatickets/">Gala Party</a> ticket.
</p>
<p>Guys, invest in your future!  Girls, invest in your future!
</p>
<p>Thanks for your support!
</p>
<p>Jamie
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/nsscache-launch">
    <title>nsscache open source launch</title>
    <link>http://spacepants.org/blog/nsscache-launch</link>
    <dc:date>2007-11-05T21:54+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Today we open sourced the project I've been working on for the last 9
   months, <a href="http://code.google.com/p/nsscache">nsscache</a>.
</p>
<p>It's a glorified version of:
</p>
<pre><code>ldapsearch | awk &gt; /etc/passwd
</code></pre><p>in that we in theory support more than just LDAP as a data source, and
   offer two types of database storage (nss_db using Berkeley DB, and
   plain text files).
</p>
<p>If you're having issues with your nss_ldap setup, then try it out :)
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/blog-o-the-day-lca2007-2">
    <title>blog o' the day, linux.conf.au 2007 day 2</title>
    <link>http://spacepants.org/blog/blog-o-the-day-lca2007-2</link>
    <dc:date>2007-01-16T17:12+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Today's blog of the day comes from <a href="http://etbe.blogspot.com/2007/01/some-ideas-for-running-conference.html">this
post</a>:
</p>
<blockquote><p>The easy solution to this is for the conference organizers to provide
   laptops that have multiple boot options for different distributions.
</p>
</blockquote><p>I think this is certainly interesting, and
   <a href="http://mel8ourne.org">Mel8</a>  might want to experiment with this, but
   I find the use of the word "easy" quite amusing here :)
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/blog-o-the-day-lca2007-1">
    <title>blog o' the day, linux.conf.au 2007 day 1</title>
    <link>http://spacepants.org/blog/blog-o-the-day-lca2007-1</link>
    <dc:date>2007-01-16T06:51+10:00</dc:date>
    <content:encoded><![CDATA[

<p> <a href="http://coffee.geek.nz/2007/01/14/software_even_your_mother_can_use.html">This
post</a>
   caught my eye on <a href="http://planet.lca2007.linux.org.au">the feed</a> this morning:
</p>
<blockquote><p>your maternal ancestors might be complete morons, but you insult your
   audience by impying everyone else's is too!
</p>
</blockquote>
]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-programme-choices">
    <title>linux.conf.au 2007 programme choices</title>
    <link>http://spacepants.org/blog/lca2007-programme-choices</link>
    <dc:date>2007-01-12T01:39+10:00</dc:date>
    <content:encoded><![CDATA[

<p>As the clock ticks approach single digit figures, the organising team
   are ramping up.  Everything's coming along smoothly in time for a
   kick-arse start on Monday.
</p>
<p>One thing that makes me sad is that I'll likely not be able to watch a
   lot of the talks -- but if I do get a chance, I'd see these:
</p>
<ul>
 <li><p> <a href="http://lca2007.linux.org.au/talk/109">clustering tdb</a> by Andrew
   Tridgell.  When I first saw this proposal came in, I knew it was a
   good one.  Someone should make an LDAP server that doesn't suck,
   and use tdb to solve the multimaster replication problem in the
   storage layer.  Oh wait -- that's what he's doing already.
</p>

 </li>

 <li><p> <a href="http://lca2007.linux.org.au/talk/84">Puppet</a> by Luke Kanies.
   Luke's been working on this awesome next-generation systems
   configuration management tool for a few years now.  He approached
   me, back in the day, to be a beta-tester -- he and I were both
   hitting scaling problems with <code>cfengine</code>.  I hope every sysadmin
   makes it to this talk!
</p>

 </li>
</ul>
<p>There's a few others to note: Theodore T'so always has an interesting
   talk; this year he's giving two cool subjects a run.  The tutorial on
   heartbeat 2 by Alan Robertson is sure to be full of good loadbalancing
   fu.
</p>
<p>There's lots of exciting things going on in the programme, so I hope
   to see you all next week!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-best-linux-conference">
    <title>we're #1!</title>
    <link>http://spacepants.org/blog/lca2007-best-linux-conference</link>
    <dc:date>2006-11-14T19:31+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Who's the <a href="http://www.google.com/search?q=best+linux+conference">best linux
conference</a> in
   the world?<br/><a href="http://lca2007.linux.org.au">WE ARE, BABY</a>!
</p>
<p>Turns out we're only <a href="http://www.google.com/search?q=linux+conference">second
hit</a> for "linux
   conference", but we're still
   <a href="http://pleasesendustolinuxconfau.info/">best</a>!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-boned-rego-but-fixed-now">
    <title>linux.conf.au boned, fixed again</title>
    <link>http://spacepants.org/blog/lca2007-boned-rego-but-fixed-now</link>
    <dc:date>2006-11-14T18:54+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Yeah, I <a href="http://justblamepia.com">broke the registration process</a> when I rolled out some bugfixes
   last night, but I've fixed them now!  Continue in your merry scramble
   for tickets to the ROCKIN'EST FREE AND OPEN SORES GIG IN THE SOUTH.
</p>
<p>(In unrelated news, can you think of a good reason why <a href="http://pleasedontsendthemtolinuxconfau.info/">they shouldn't
go to lca2007</a>?
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-registrations-open">
    <title>lca2007 registrations now open!</title>
    <link>http://spacepants.org/blog/lca2007-registrations-open</link>
    <dc:date>2006-11-01T20:34+10:00</dc:date>
    <content:encoded><![CDATA[

<p> <a href="http://lca2007.linux.org.au/Registration">Registrations</a> to LCA 2007
   are now open!  Get in quick!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/movember-2006">
    <title>mo</title>
    <link>http://spacepants.org/blog/movember-2006</link>
    <dc:date>2006-11-01T07:37+10:00</dc:date>
    <content:encoded><![CDATA[

<img src='http://www.orange-county-chopper.com/images/PaulSr.jpg' />

<p> <a href="http://www.movember.com/au/sponsor/">Sponsor me</a> for
   movember!  My rego number is 4098!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-programme-first-draft">
    <title>linux.conf.au 2007 programme first draft</title>
    <link>http://spacepants.org/blog/lca2007-programme-first-draft</link>
    <dc:date>2006-10-18T07:41+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Last night the Seven met, as they do, around a dark table, deep below
   the city in a room built by the Templar Knights; surrounded, as
   normal, by ancient iconography of power and knowledge.  Their goal: a
   draft programme for <a href="http://lca2007.linux.org.au">LCA 2007</a>.
</p>
<p>We ranked the streams in order of popularity; nothing too scientific,
   based purely on presenter name and subject matter, what would likely
   draw the biggest crowds?
</p>
<p>Then with that ordering, we'd go through the streams, picking off the
   top of all lists, and putting them in the biggest room.  Repeat for
   the next one in the next talk slot, offsetting them so that no two
   venues would carry the same stream at the same time.
</p>
<p> <img src="http://static.flickr.com/103/272182553_2338c6c2f5_o.jpg" alt="halfway through the first programme draft"/> 
</p>
<p>By the end of it, we had a nice patchwork quilt.
</p>
<p> <img src="http://static.flickr.com/86/272182569_5fef4b1833_o.jpg" alt="the finished draft"/> 
</p>
<p>I don't think we're done with it, <a href="http://justblamepia.com">yet</a>, but it's a good start!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/new-programming-music">
    <title>new programming music</title>
    <link>http://spacepants.org/blog/new-programming-music</link>
    <dc:date>2006-09-20T15:47+10:00</dc:date>
    <content:encoded><![CDATA[

<p>On <a href="http://www.mega-nerd.com/erikd/Blog/Music/ministry-greatest_fits.html">Erik's recommendation</a> I ordered Ministry of Sound.. no, just kidding.  I got Greatest Fits ordered from <a href="http://www.redeye.com.au/">Red Eye</a>, my favouritest record store ever, and it arrived yesterday.
</p>
<p>Rockin!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/no-longer-fucking-amazing">
    <title>LCA 2007: no longer "fucking amazing"</title>
    <link>http://spacepants.org/blog/no-longer-fucking-amazing</link>
    <dc:date>2006-09-19T21:41+10:00</dc:date>
    <content:encoded><![CDATA[

<p>To combat the risk of driving people away from conferences, <a href="http://lca2007.linux.org.au">linux.conf.au 2007</a> wil no longer be <a href="http://lists.linux.org.au/archives/linux-aus/2006-September/msg00215.html">fucking amazing</a>.
</p>
<p>Instead, it will merely OPEN YOUR SORES, FREE YOUR SOFT WEAR, and ROCK.
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-cfp-updates-2">
    <title>linux.conf.au 2007 proposal count through the roof!</title>
    <link>http://spacepants.org/blog/lca2007-cfp-updates-2</link>
    <dc:date>2006-09-17T09:59+10:00</dc:date>
    <content:encoded><![CDATA[

<p> <img src="http://spacepants.org/images/lca2007.png" alt="lca 2007 logo"/> 
</p>
<p>The response to the <a href="http://lca2007.linux.org.au/cfp">CFP</a> has been
   massive!  Right now our reviewers are starting to read the torrent of
   submissions...
</p>
<ul>
 <li>
     212 proposals for presentations/seminars
 </li>

 <li>
     31 proposals for tutorials
 </li>

 <li>
     17 proposals for miniconfs
 </li>
</ul>
<p>Holy shit!
</p>
<p>Reading through some of the titles and abstracts, theres a <em>lot</em> of
   really awesome stuff that people have been working on, I'm really
   excited about what the programme is going to look like!
</p>
<p>Unfortunately, we're going to have to reject a lot of these :(  On the
   upside, it does mean that the stuff that does make it through is going
   to be so mindblowingly awesome that you can't afford not to be at
   linux.conf.au 2007!
</p>
<p>Rock!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/woo-new-command">
    <title>woo, new command</title>
    <link>http://spacepants.org/blog/woo-new-command</link>
    <dc:date>2006-09-14T10:51+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Shoulder-surfing <a href="http://inodes.org/blog">johnf</a> the other night at the <a href="http://lca2007.linux.org.au">seven</a> meeting, I saw him use
</p>
<pre><code>cd -
</code></pre><p>to return to a previous directory...
</p>
<p>I'd been using <code>pushd</code> and <code>popd</code> when I remembered; tried out <code>cd -</code> just now and bam!  productivity increased 300%!  Now I won't be opening new terminals just to keep a shell in past directories...
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/wrapping-cgi-apps-in-wsgi">
    <title>wrapping CGI applications in WSGI</title>
    <link>http://spacepants.org/blog/wrapping-cgi-apps-in-wsgi</link>
    <dc:date>2006-09-12T12:30+10:00</dc:date>
    <content:encoded><![CDATA[

<p>We've got a large "legacy" body of code that is used by our staff to
   track most of our business, it's a whole lot of Python CGI that uses
   some custom HTML and DB frameworky code; it's pretty ugly and having
   become a convert to the cult of <a href="http://pylonshq.com">Pylons</a>,
   <a href="http://wsgi.org">WSGI</a>, and <a href="http://sqlalchemy.org">SQLAlchemy</a>, I
   really want to replace it.
</p>
<p>Of course, anyone knows that one of the <a href="http://joelonsoftware.com/articles/fog0000000069.html">Things You Should Never
Do</a> is rewrite
   from scratch.  Even in the same language.
</p>
<p>It would be much easier to integrate the old app into a new Pylons
   app, have them running side-by-side, and slowly deprecate the old one
   as new interfaces are written.  (This is still not a perfect idea, as
   demonstrated by the 4 year old TCL code that the current app was meant to replace still
   running in production ;-)  As bugs in the old code are found, we can
   either <a href="http://angryflower.com/pathof.gif">beat our heads against brick
walls</a> or replace just that
   functionality with a sane data model, similar looking templates, and
   shiny new controller smarts, and no-one would be the wiser, except of
   course that for some reason the developers are no longer constantly
   grumpy and the webapp is running smoother and faster than before, and
   crashing less often...
</p>
<p>It occurred to me yesterday the best way to get a legacy CGI app to run
   along with Pylons is to convert it to a WSGI application, and just
   mash it in at the bottom of the application stack, where Pylons would
   normally go when it 404s.
</p>
<p>Here's the result of some free time and caffeinated excitement this
   morning:
</p>
<pre><code>import imp
import sys
import StringIO

def application(environ, start_response):
    # trap the exit handler, we don't want scripts exiting randomly
    # we might want to do something with the return code later
    retcode = None
    def exit_handler(rc):
        retcode = rc

    sys.exit = exit_handler

    # trap the output buffer
    outbuf = StringIO.StringIO()
    sys.stdout = outbuf

    # catch stderr output in the parent's error stream
    sys.stderr = environ['wsgi.errors']

    # import the script
    script = environ['PATH_TRANSLATED']
    f = open(script, "rb")
    imp.load_module('__main__', f, script, ("py", "rb", imp.PY_SOURCE))
    f.close()

    # outbuf has a typical CGI response, headers separated by a double
    # newline, then content
    (header, content) = outbuf.getvalue().split('\n\n', 1)
    headers = [tuple(x.split(': ', 1)) for x in header.split('\n')]

    # return it wsgi style
    start_response('200 OK', headers)
    return [content]
</code></pre><p>Our CGI apps print out on <code>stdout</code>, as you'd expect, so we need to
   trap that, here done with a <code>StringIO</code> monkeypatched on the top of
   <code>sys.stdout</code>.  We also need to hack <code>sys.exit</code> out of the way, so that
   the CGIs don't quit before we've completed the WSGI protocol.  (I
   think this might cause some bugs in the execution though, because now
   it's not terminating execution of the module, but I haven't found an
   example yet to bother worrying about it.)
</p>
<p>I import the script, rather than using <code>os.system</code>, because it
   feels right.  I use <code>imp.load_module</code> rather than <code>import</code> because we
   don't know what the script is until runtime :)
</p>
<p>The real trick comes from a tip I found
   <a href="http://www.python.net/crew/davem/cgifaq/faqw.cgi?req=show&file=faq01.008.htp">here</a>
   , whilst looking for how to run the imported module as <code>__main__</code>.
   Just <code>imp.load_module</code> and tell it that it's <code>__main__</code>!  Simple!
</p>
<p>(The hardest part about this whole excercise was now fiddling with
   <code>sys.path</code> and the CWD to make sure the imported script was running
   with the right environment that the CGIs used to expect, this is all
   done in the CGI runner <code>dispatch.cgi</code> which I won't copy here because
   it's pretty trivial and well documented in the <a href="http://www.python.org/dev/peps/pep-0333/">WSGI spec</a>.)
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/more-than-a-feeling">
    <title>more than a feeling</title>
    <link>http://spacepants.org/blog/more-than-a-feeling</link>
    <dc:date>2006-09-10T14:43+10:00</dc:date>
    <content:encoded><![CDATA[

<blockquote><p>I woke up this morning, and the sun was gone
   Turned on some music to start my day..
</p>
</blockquote><p>For a while, I've wanted to be woken up by anything other than my
   clock radio, so last night I peeked at
   <a href="http://banshee-project.org/">banshee</a> to see if it had a remote
   control... turns out it does!
</p>
<p>Hacked up this script, <code>shins</code>:
</p>
<pre><code>#!/bin/sh

DISPLAY=:0
XAUTHORITY=/home/jaq/.Xauthority
export DISPLAY XAUTHORITY
banshee --enqueue /media/usbdisk/music0/Albums/The\ Shins/Chutes\ Too\ Narrow/01\ -\ Kissing\ the\ Lipless.ogg
banshee --play
</code></pre><p>and set it to run at 9am:
</p>
<pre><code>dawn% at 9am
warning: commands will be executed using /bin/sh
at&gt; sh shins
at&gt; &lt;EOT&gt;
job 6 at Sun Sep 10 09:00:00 2006
</code></pre><p>and this morning I was woken to the soft sounds of <a href="http://www.last.fm/music/The+Shins">The
Shins</a>, just as planned.  Great
   start to the day!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/pylons-gotchas">
    <title>pylons gotchas</title>
    <link>http://spacepants.org/blog/pylons-gotchas</link>
    <dc:date>2006-09-03T12:42+10:00</dc:date>
    <content:encoded><![CDATA[

<p> <a href="http://benno.id.au">Benno</a> was over, hacking on the <a href="http://lca2007.linux.org.au">LCA 2007 website</a> with me yesterday, and
   we hit two gotchas,  both I knew about but when I explained them to
   him they sounded silly.
</p>

<h3> <code>c</code> considered harmful. </h3>
<p> <code>c</code> is a request-local global object that you can attach objects to,
   which is useful as a way of passing data from the controller to the
   template code -- when you're calling a parameterised template you
   might not know at call time what the args the template wants are, but
   you can pass them all in on <code>c</code>.  If you're using some pattern like a
   mixin CRUD class for generalising common data operations, then the
   code that actually calls the template doesn't know what the object is,
   but the template it's calling does.
</p>
<p> <code>c</code> has the magical property that it has overloaded <code>__getattr__</code> to
   return an empty string if the attribute is not found.  This is a mixed
   blessing; your templates can access an attribute that hasn't been
   attached and it'll mostly cope with it.  (Problems happen when you try
   to access attributes of nonexistent attributes, and you get the
   confusing message 'str has no attribute X'.)
</p>
<p>However, this means you hide bugs; you've forgotten to attach the
   object you want to <code>c</code> and then your code runs fine; it's the users
   who find the problem after deployment, not during development.  Having
   a <code>__getattr__</code> that throws exceptions means you find out about these
   problems a lot sooner.
</p>
<p>I think both of these points show that <code>c</code> in general is a bad idea;
   you should make use of explicit args so that your template interface
   is clearly defined -- I haven't yet found a nice way of doing it that
   is as easy as or better than using <code>c</code> though.
</p>

<h3> Myghty expressions that evaluate to False return empty strings. </h3>
<p>We had a simple construct like so:
</p>
<pre><code>Count: &lt;% len(c.review_collection) %&gt;
</code></pre><p>which has the interesting property of evaluating to '' when
   <code>c.review_collection</code> is empty; <code>len()</code> returns 0 which is False.
</p>
<p>This is pretty retarded; I suspect there's a shortcut along the lines
   of:
</p>
<pre><code>content = evaluate_fragment("len(c.review_collection)")
if content:
    write(content)
</code></pre><p>when these inline blocks are rendered; the <code>if</code> block clearly will
   fail to trigger when the inline block evaluates to <code>0</code>, <code>False</code>, <code>[]</code>, or
   <code>{}</code>.  I can't think of a case where this is a good thing.
</p>
<p>The workaround is to wrap the <code>len()</code> call in <code>str()</code>, so that the
   fragment doesn't evaluate to false.
</p>
<pre><code>&lt;% str(len(c.review_collection)) %&gt;
</code></pre><p>More gotchas as they come to hand.
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca-proposals-so-far">
    <title>LCA 2007 proposals so far</title>
    <link>http://spacepants.org/blog/lca-proposals-so-far</link>
    <dc:date>2006-08-31T13:40+10:00</dc:date>
    <content:encoded><![CDATA[

<p>I'm having a browse of the submissions to LCA that we've got so far, and there's some cool stuff in there!
</p>
<p>There's still a little over 2 weeks for you to get your proposals in, so don't hold back!  I'm sure you <em>all</em> have something very exciting you want to talk about at the conference!
</p>
<p>The more submissions we get, the rockin'er the conference will be!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/unittest-urllib2-mock-object">
    <title>Dad, I dug another hole...</title>
    <link>http://spacepants.org/blog/unittest-urllib2-mock-object</link>
    <dc:date>2006-08-15T13:53+10:00</dc:date>
    <content:encoded><![CDATA[

<p>I wrote another mock object, this time replacing <code>urlopen</code> from urllib2.
</p>
<pre><code>import urllib2
import StringIO
import unittest

class Dummy_urllib2(object):

    def install(cls):
        urllib2.urlopen = Dummy_urllib2.urlopen

    install = classmethod(install)

    def urlopen(self, url, data=None):
        self.url = url
        self.data = data

        response = StringIO.StringIO("foo")

        def geturl():
            return url

        response.geturl = geturl

        def info():
            return {}

        response.info = info

        return response

    urlopen = classmethod(urlopen)


class TestDummy_urllib2(unittest.TestCase):
    def test_install(self):
        Dummy_urllib2.install()

        url = 'http://notfound.example.org'

        try:
            r = urllib2.urlopen(url)
        except urllib2.URLError, e:
            self.fail("URLError raised, Dummy_urllib2 not installed or failed: %s" % e)

        self.assertEqual(url, Dummy_urllib2.url)
        self.assertEqual(url, r.geturl())
        self.assertEqual(None, Dummy_urllib2.data)
        self.assertEqual("foo", r.read())

if __name__ == '__main__':
    unittest.main()
</code></pre><p>This time it comes with <em>it's own</em> test suite.  How meta!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/lca2007-cfp-updates">
    <title>linux.conf.au 2007 CFP updates</title>
    <link>http://spacepants.org/blog/lca2007-cfp-updates</link>
    <dc:date>2006-08-14T17:22+10:00</dc:date>
    <content:encoded><![CDATA[

<p>I'm exhausted; a weekend of hacking and deployment has left me a bit frazzled.
</p>
<p>But it's OK! <a href="http://lca2007.linux.org.au">linux.conf.au 2007</a>'s website has had a facelift, thanks to Andy Fitzsimmons for the CSS tweaks, and our CFP submission process is a lot better.
</p>
<p>If you haven't yet put in a proposal for a talk, a miniconf, a tutorial, then now's the time!<br/><a href="http://lca2007.linux.org.au/cfp">The CFP is still open</a>!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/erlang-sudoku-solver">
    <title>a sudoku solver in Erlang</title>
    <link>http://spacepants.org/blog/erlang-sudoku-solver</link>
    <dc:date>2006-07-31T12:36+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Matt will be pleased to hear that despite my claims that I'd have a
   productive weekend doing important things for <a href="http://lca2007.linux.org.au">LCA
2007</a> organisation, I spent the weekend
   programming.
</p>
<p> <a href="http://algorithm.com.au">Some bastard</a> gave a great talk on Erlang at
   <a href="http://slug.org.au">SLUG</a> last Friday, and I really wanted to cut my
   teeth on it.  So, hungover, I spent most of Saturday and a little bit
   of Sunday morning playing with the <a href="http://erlang.se/doc/doc-5.4/doc/getting_started/part_frame.html">getting started
guide</a>
   and writing a Sudoku solver.
</p>
<p>If you're curious, you can pull it down from my
   <a href="http://bazaar-vcs.org">bzr</a> repository:
</p>
<p> <a href="http://repo.spacepants.org/sudoku/sudoku.dev">http://repo.spacepants.org/sudoku/sudoku.dev</a> 
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/sysadmin-day-2006">
    <title>boned :(</title>
    <link>http://spacepants.org/blog/sysadmin-day-2006</link>
    <dc:date>2006-07-29T11:01+10:00</dc:date>
    <content:encoded><![CDATA[

<p>In an office full of sysadmins, you'd think one of them would have
   known what day it was yesterday :(
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/pylons-0.9-out">
    <title>Pylons 0.9 is out!</title>
    <link>http://spacepants.org/blog/pylons-0.9-out</link>
    <dc:date>2006-07-29T10:52+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Awesome!<br/><a href="http://pylonshq.com">Pylons 0.9</a> is out, with more rockin'
   features than ever :-)
</p>
<p>Also, the new website is a big improvement over the last.
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/mock-ldap-server-object">
    <title>mock LDAP server object</title>
    <link>http://spacepants.org/blog/mock-ldap-server-object</link>
    <dc:date>2006-07-26T19:04+10:00</dc:date>
    <content:encoded><![CDATA[

<p>Today's big achievement was an LDAP mock object, similar to the SMTP mock object found in <code>paste.fixture</code>.  I was refactoring the sign in and sign out code of an in-house application that uses LDAP as an authentication store, and I needed to test that the logic of the controllers was correct.  So, referring back to <a href="http://pythonpaste.org">Paste</a>'s lovely <code>fixture</code> module, I came up with the following:
</p>
<pre><code>import ldap

class Dummy_ldap(object):

    def __init__(self, server):
        print "dummy ldap init"
        self.server = server

    def install(cls):
        ldap.initialize = cls

    install = classmethod(install)

    def simple_bind_s(self, dn, passwd):
        return True

    def search(self, base, scope, search_filter):
        self.base = base
        self.filter = search_filter
        self.results = [(ldap.RES_SEARCH_ENTRY, [('dn', "cn=test,%s" % self.base),
                                                 ('mail', ['test'])]),
                        (ldap.RES_SEARCH_RESULT, []),
                        ]
        self.counter = 0
        return 1

    def result(self, rid, number):
        r = self.results[self.counter]
        self.counter += 1
        return r
</code></pre><p>A bit hackish, yes, but I'm not trying to reimplement LDAP here, I just want to trap the calls I use.  Obviously a little bit of work is needed to, say, disallow the bind, or throw an exception, but these are trivial extensions.
</p>
<p>Just as in <code>Dummy_smtplib</code>, you call the classmethod <code>install</code> to set it up (i.e. monkeypatch <code>ldap.initialize</code>) and you get to trap how it behaves.
</p>
<pre><code>class TestAccountController(ControllerTest):

    def test_signin_signout(self):
        Dummy_ldap.install()

        # ... do stuff
</code></pre><p>Simple!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/paste-fixture-dummy-smtplib">
    <title>paste.fixture's Dummy_smtplib</title>
    <link>http://spacepants.org/blog/paste-fixture-dummy-smtplib</link>
    <dc:date>2006-07-20T21:21+10:00</dc:date>
    <content:encoded><![CDATA[

<p>I'm working on two webapps that need to send email to users, and in
   both, the user is expected to click on a URL to confirm their
   registration.  A common enough idiom for website accounts.
</p>
<p>As a disciple of the cult of test-driven development, I want to be
   able to make a test that generates that email, inspects the contents
   for the URL, visits that generated URL, and then checks that the
   registration is completed.
</p>
<p>Michael K, who <a href="http://spacepants.org/blog/semiautomatic-test-generation">previously
suggested</a>
   other abuses of Python's dynamic nature, reminded me a few months ago
   that you could monkeypatch an imported library with your own, and it'd
   be preserved throughout the "address space" of the running Python
   program.
</p>
<p>I'd not really played with it, and had been putting off writing a test
   for email because I didn't really understand what I wanted.  The other
   night at DebSIG, I asked (complained?) again about it, and he said
   "Hey, <code>paste.fixture</code> already does it!"  I'd said I knew, but it had
   no documentation, and no-one on the paste users list had responded to
   my request for examples.  He gave me a curt "Read the fucking source"
   response (in a much nicer way of course) and I thought, he's right!
   Back in the day I used to read library source code in order to work
   out poorly documented APIs, why now do I rely on clear documentation
   so much?  I should just dig in and write some example code to test it
   out, and JFDI.
</p>
<p>Enough of the backstory.
</p>
<p>Here's a quick guide to setting up an email sender test, using
   <code>paste.fixture</code> around your <a href="http://pylonshq.com">Pylons</a> application.
</p>
<p>Firstly, in your controller, you have something that sends email, like so:
</p>
<pre><code>import smtplib

from app.lib.base import BaseController, m

class FooController(BaseController):
    def index(self):
        """Do something and send email while you're at it"""
        s = smtplib.SMTP("localhost")

        s.sendmail("from@example.org",
                   "to@example.com",
                   """
Hey, this is a message

http://localhost/foo/activate/37
"""
        s.quit()

        m.subexec("foo/index.myt")

    def activate(self, id):
        m.write("awesome, activating %s" % id)
</code></pre><p>Pretty basic, if we visit <code>/foo/</code> then we send an email, and then if we visit <code>/foo/activate/N</code> we inform the visitor of the activation.
</p>
<p>The test is pretty simple too:
</p>
<pre><code>import re
import unittest

from paste.fixture import Dummy_smtplib

def TestFooController(unittest.TestCase):
    def test_foo_activation(self):
</code></pre><p>You just call the classmethod <code>install</code> on <code>Dummy_smtplib</code> to set it up, which does some magic behind the scenes (really it just replaces <code>smtplib.SMTP</code> with itself)
</p>
<pre><code>        Dummy_smtplib.install()
</code></pre><p>then run through the process you want to test
</p>
<pre><code>        # get the start page
        res = self.app.get('/foo')
</code></pre><p>and now we check that the message was sent, and its contents
</p>
<pre><code>        self.failIfEqual(None, Dummy_smtplib.existing, "no message sent")

        match = re.match(r'^.*/foo/activate/([^ ]+)', Dummy_smtplib.existing.message)
        self.failIfEqual(None, match)

        # visit the URL
        res = self.app.get('/foo/activate/%s' % match.group(1))
</code></pre><p>and finally, test the result of the activation.
</p>
<pre><code>        res.mustcontain('awesome')
        res.mustcontain(match.group(1))
</code></pre><p>You've also got to clean up, reset the dummy SMTP library for next time (you'll get an exception thrown if you don't, to remind you).
</p>
<pre><code>        Dummy_smtplib.existing.reset()
</code></pre><p>If you do any database stuff, then the times to check the status of the data model are just before the actuvation URL is visited, and again afterwards.  I keep the model empty for each test, so I can pull out all the records and make sure that there's only one afterwards, and that it has the right attributes before and after.
</p>
<p>Pretty easy stuff.
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/seven-cfp-open">
    <title>lca2007 CFP open!</title>
    <link>http://spacepants.org/blog/seven-cfp-open</link>
    <dc:date>2006-07-18T19:49+10:00</dc:date>
    <content:encoded><![CDATA[
	<img src="http://lca2007.linux.org.au/cfp.png" class="icon"/>

<p>I was on a jet from Austin to London when it happened, but busy the
   hours beforehand in a flat in Austin nursing a hangover putting the
   final touches on <a href="http://lca2007.linux.org.au">the website</a>.  So,
   though I missed the chance to announce at the time, I'll take this
   opportunity to blog about it now!
</p>
<p>The <a href="http://lca2007.linux.org.au/cfp">LCA 2007 CFP</a> is open, so <a href="http://lca2007.linux.org.au/cfp/submit">submit your
proposal</a> for a talk,
   miniconf, etc now!  (Or in reality, start writing your proposal now,
   so that you can submit it before the CFP closes ;-)
</p>
<p>We're looking forward to your submissions!
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/pylons-paste-stack">
    <title>pylons, paste, and wsgi</title>
    <link>http://spacepants.org/blog/pylons-paste-stack</link>
    <dc:date>2006-07-17T14:20+10:00</dc:date>
    <content:encoded><![CDATA[

<p>I scribbled this down on our whiteboard last Friday, trying to explain
   how <a href="http://pylonshq.com">Pylons</a> and <a href="http://pythonpaste.org">Paste</a>
   fit together.  Prevously <a href="http://perkypants.org">jdub</a> and Lindsay had
   asked me similar questions.  Until Friday, I wasn't even sure myself.
</p>
<p> <img src="http://spacepants.org/images/pylons-paste-stack.png" alt="pylons and paste stack diagram"/> 
</p>
<p>The first thing to note is that Paste is not a framework or single
   library, it's a collection of components that by themselves don't do a
   lot, but with their powers combined form a set of useful and sometimes
   essential tools for building a web application in Python.
</p>
<p>Paste implements an interface known as <a href="http://wsgi.org">WSGI</a>, aka
   the Web Server Gateway Interface.  It's defined in <a href="http://www.python.org/dev/peps/pep-0333/">PEP
333</a>.  Basically WSGI
   describes a Chain of Command design pattern; each piece of a WSGI
   application takes a request, and either acts on that request or passes
   it along the chain.  The interface described by WSGI means you can
   plug WSGI apps (or as Pylons calls them, /middleware/) together in any
   order as you like.
</p>
<p>Why is this useful?  Well, it means you can take an off-the-shelf
   authentication handler to cope with 403 and 401 responses and take
   care of logins.  One would only need to say "this is how you
   authenticate someone" and "this is how you ask the user for their
   password."  Other things are possible; Pylons ships with an ultra-sexy
   500 handler that puts you in a DHTML debugger, complete with traceback
   and Python interpreter.  (Of course such a tool is a giant security
   hole so it is easily turned off in production environments.)
</p>
<p>So, that's Paste.  There's a few special cases in there, though:
   PasteScript and PasteDeploy.  They're special in that they tend to be
   at the bottom of the stack -- they're specifically for launching WSGI
   applications, configuration of the application (e.g. authenticatoin
   details alluded to above) and connecting to the application
   (e.g. direct HTTP, FastCGI, and other connectors).  I suspect that my
   diagram above doesn't lend itself well to describing how PasteScript
   and PasteDeploy really work; it's still a bit of dark magic to me.  I
   hope someone else would be able to build on this article with their
   own that rebuts the errors and clears the grey areas.
</p>
<p>In a Pylons app, you tend not to notice Paste, except when deploying
   (because you tend to run the command <code>paster serve</code> to
   launch a development environment).  Pylons itself is mostly just
   glue.  It's a thin veil of a framework over the top of some very
   powerful supporting libraries but presents them in a convenient and
   well defined way.
</p>
<p>When you create a Pylons app, you get your paste middleware built for
   you, and then the entry point for your app is created as a WSGI
   application too.  So it sits on top of the stack, taking in requests,
   and sending out responses.  Your app can define its own middleware,
   too, so you have a lot of control over what happens between your app
   and the browser.
</p>
<p>The main components of a Pylons app are:
</p>
<ul>
 <li><p>A route mapper, by default <a href="http://routes.groovie.org">Routes</a>.    The route mapper takes in URLs from the request passed into the app, and maps that URL to a controller object and method call.  (If you've used RoR then you probably are familiar with this already.)
</p>

 </li>

 <li><p>A templating engine, by default <a href="http://myghty.org">Myghty</a>.  The templating engine generates the view presented to the browser.
</p>

 </li>

 <li><p>A data model.  Pylons doesn't prefer any method of data model, it just makes available a <code>model</code> module within which you can define your own data model.  I use <a href="http://sqlalchemy.org">SQLAlchemy</a> as an ORM because it is very powerful and is nicely suited to working with existing schemas.  It works as an MVC between the data model presented to the application and the database schema itself.
</p>

 </li>
</ul>
<p>Pylons lets you swap out any of these components with your own, if you
   desire.  I find Routes and Myghty to be powerful and flexible and
   friendly enough that there's no reason to want anything else.
</p>
<p>Your controller objects, like any MVC pattern, coordinate between the
   model and the view.  An action performed on a controller retrieves
   some data from the model, possibly altering it, and renders that data
   using the template engine.
</p>
<p>There are other parts, other libraries that you'll see in a Pylons
   app, that aren't represented here.  WebHelpers is a library of
   convenience functions used in the template engine, for generating
   common HTML and JavaScript.<br/><code>paste.fixture</code> is a web app test
   framework that takes advantage of the common interface of WSGI to
   allow one to test their application without requiring a full web
   server and socket handling.<br/><a href="http://formencode.org">FormEncode</a>
   handles form validation, useful from within a controller object.
   These are but to name a few.
</p>
<p>Unfortunately there is a sore need for overviews like this one in the
   Paste and Pylons community; as stated earlier I didn't fully
   understand the relationships myself until I came up with this diagram.
   Hopefully then, dear reader, you have a better insight into how this
   collection of names fit together, and can avoid the steep learning
   curve :-)
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/tdd-promotes-good-health">
    <title>TDD promotes good health</title>
    <link>http://spacepants.org/blog/tdd-promotes-good-health</link>
    <dc:date>2006-06-01T16:01+10:00</dc:date>
    <content:encoded><![CDATA[

<p>There's an important advantage to Test Driven Development that I don't think was covered on list or by Rob at his talk.
</p>
<p>By having a test suite, you can code after a heavy liquid lunch and be sure that you're not decreasing the quality of existing code.  It makes it easier to focus on a specific task and write code to solve that problem.  Having something do all the work for you when testing is a massive bonus, because obviulsy the side-effects of a pub lunch are that you are easily distracted and lack the willingness to focus on the task at hand.  Test suites lower the barrier of entry to getting work done.
</p>
<p>Who'da thought that best practices would also be best for drinking practice?
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

	<item rdf:about="http://spacepants.org/blog/wah-wah-spam-redux">
    <title>poignant thoughts on threads about spam</title>
    <link>http://spacepants.org/blog/wah-wah-spam-redux</link>
    <dc:date>2006-05-31T16:08+10:00</dc:date>
    <content:encoded><![CDATA[

<blockquote><p>Threads about spam on slug always waste far more of my time than the
   spam itself ever does.
</p>
</blockquote><p>-- <a href="http://lists.slug.org.au/archives/slug/2006/05/msg00909.html">Andrew Bennetts</a>, a beacon of reason cutting through the foggy quagmire of FUD.
</p>
<blockquote><p>Never argue with stupid people. They'll just drag you down to their
   level and beat you with experience.
</p>
</blockquote><p>-- sent to me in private email
</p>

]]></content:encoded>
    <dc:creator>Jamie Wilkinson (mailto:jaq@spacepants.org)</dc:creator>
    <dc:rights>Copyright &#169; 2004,2005 Jamie Wilkinson.</dc:rights>
    <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</item>

<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
  <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction" />
  <cc:permits rdf:resource="http://web.resource.org/cc/Distribution" />
  <cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
  <cc:requires rdf:resource="http://web.resource.org/cc/Notice" />
  <cc:requires rdf:resource="http://web.resource.org/cc/Attribution" />
  <cc:requires rdf:resource="http://web.resource.org/cc/Copyleft" />
</cc:License>
</rdf:RDF>
