<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://nedbatchelder.com/rssfull2html.xslt" media="screen" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/">
	<channel rdf:about="http://nedbatchelder.com//blog">
		<title>Ned Batchelder's blog</title>
		<link>http://nedbatchelder.com/blog</link>
		<description>Ned Batchelder's personal blog.</description>
		<dc:language>en-US</dc:language>
		<image rdf:resource="http://nedbatchelder.com/pix/rss-banner.gif"/>
		<items>
			<rdf:Seq>
				<rdf:li resource="http://nedbatchelder.com/blog/201201/goodbye_tabblo.html"/><rdf:li resource="http://nedbatchelder.com/blog/201201/slim_comparisons.html"/><rdf:li resource="http://nedbatchelder.com/blog/201201/decorated_fabric_over_the_edge.html"/><rdf:li resource="http://nedbatchelder.com/blog/201201/safari_buys_threepress.html"/><rdf:li resource="http://nedbatchelder.com/blog/201201/where_can_i_walk_to_in_30_minutes.html"/><rdf:li resource="http://nedbatchelder.com/blog/201201/happy_and_quiet.html"/><rdf:li resource="http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html"/><rdf:li resource="http://nedbatchelder.com/blog/201112/happy_hanukkah.html"/><rdf:li resource="http://nedbatchelder.com/blog/201112/stop_sopa.html"/><rdf:li resource="http://nedbatchelder.com/blog/201112/deleting_files_keeping_a_few.html"/>
			</rdf:Seq>
		</items>
	</channel>
	<image rdf:about="http://nedbatchelder.com/pix/rss-banner.gif">
		<title>Ned Batchelder's blog</title>
		<link>http://nedbatchelder.com/blog</link>
		<url>http://nedbatchelder.com/pix/rss-banner.gif</url>
	</image>
	
	<item rdf:about="http://nedbatchelder.com/blog/201201/goodbye_tabblo.html">
		<title>Goodbye Tabblo</title>
		<link>http://nedbatchelder.com/blog/201201/goodbye_tabblo.html</link>
		
		<dc:date>2012-01-31T20:35:36-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>A short recap of my history with <a class="offsite" href="http://tabblo.com">Tabblo</a>,
        a photo-sharing, storytelling site: I joined the startup in January
        2006, we were acquired by Hewlett-Packard in May 2007, and I left HP in
        December of 2010.</p><p>I mention this because another milestone in my relationship with Tabblo
        was reached this month: not only are all of the original startup
        employees gone from HP (I was the last), but now all of the employees I
        hired to work on the server code are also gone.  Now I literally don't
        know the people responsible for the site.  In this case, "responsible"
        doesn't mean, "updates the code for the site," because nothing has been
        changed on the site in years.  In this case, "responsible" means "will
        fix the servers if they fail."</p><p>As I figure it, there's only one milestone left to go: tabblo.com will eventually
        stop working, and no one will know how to fix it, and tabblo.com will
        be gone for good. Computers don't run indefinitely.  Left alone,
        servers will go for a long time, but eventually something will break.
        I don't think anyone at Hewlett-Packard will miss tabblo.com, and I
        don't think anyone there would know how to fix it if it broke.</p><p>I loved Tabblo, both as a job and as a product, and I have a message for 
        the current Tabblo users: leave Tabblo. I know there aren't similar
        alternatives, but Tabblo will not last forever. You should leave while
        it is still your choice.</p><p align="center"><a href="http://tabblo.com"><img src="http://nedbatchelder.com/pix/sad-tabblo.gif" alt="Sad Tabblo" width="104" height="76"></a></p><p>If the past is any guide, some Tabblo users will want to do something to
        make HP care, to make them pay attention and take care of Tabblo.  This
        is futile, HP won't care, not because HP is bad, but because Tabblo is
        not a viable business.  True, that's partly due to HP's neglect of it
        over the years, but even when we were acquired, the site was not an
        interesting business proposition for HP.</p><p>When HP bought us, they already had a photo site, <a class="offsite" href="http://snapfish.com">Snapfish</a>.  
        It isn't a community the way Tabblo is, and it doesn't allow for the
        same range of self-expression as Tabblo does.  But none of that
        mattered.  Whatever you think of Tabblo vs Snapfish, the fact remains:
        HP was never interested in Tabblo as a web site.  Snapfish already had
        millions of customers, and generated revenue for HP.  HP wasn't about 
        to confuse people by running two photo sites, and there weren't
        enough paying customers on Tabblo to make merging them a priority.
        Tabblo was never a money-maker at the scale a company like HP
        needs.</p><p>HP didn't acquire Tabblo to get tabblo.com. They acquired Tabblo so that
        we could build other web sites that used Tabblo-like technology to make
        web content printable. Tabblo.com was left running because it was
        easier to let it run than to shut it down. As time went on, other web
        sites were run with the same code on the same servers, so shutting down
        Tabblo was tricky logistically.  Now those other web sites are gone,
        and Tabblo just keeps on running.  With the latest employee departures,
        no one at HP even knows how to shut it down, other than to simply pull
        the plug.</p><p>The Tabblo site is still running, but it won't indefinitely.  When it
        fails, it will be gone.  I'm not putting any more pictures on it, and I
        don't think anyone else should either.</p><p>The only reason I don't feel bad about saying this is that Tabblo
        stopped being a viable site a long time ago, and we confronted the
        possibility of it disappearing a long time ago too.  There were various
        rumblings over the years of someone at HP finally deciding to shut down
        the site, but it never happened.  Ironically, the reason the site will
        be allowed to run until it simply dies is because HP knows they
        shouldn't just shut off the servers.  They know that the users deserve
        some advance notice, that a plan should be put in place for an orderly
        shut down.  But that takes time and attention and focus, and Tabblo
        isn't important enough to HP to get time or attention or focus.  So it
        will simply run until it dies.</p><p>Keep in mind: I don't work at HP, and I don't have any direct knowledge
        about anything happening there.  Maybe things are better than I imagine.
        But I saw these forces at work while I was there, and I've been in touch
        with the last ones out the door to know that nothing had gotten visibly better.</p><p>My last Tabblo work was writing <a href="http://nedbatchelder.com/code/save_tabblos.html">Tabblo Lifeboat</a>,
        a tool you can use to download all of your tabblos along with their photos.
        If you have stuff on Tabblo, give it a try.</p><p>To the current users of Tabblo: find something else.  Goodbye Tabblo, I
        love you in lots of ways, I wish it had turned out differently.  It was
        fun. I did the best I could.</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201201/slim_comparisons.html">
		<title>Slim comparisons</title>
		<link>http://nedbatchelder.com/blog/201201/slim_comparisons.html</link>
		
		<dc:date>2012-01-26T21:31:23-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>Hanging out in the <a href="irc://irc.freenode.net/#python">#python</a> IRC channel today, 
        I learned something new about Python comparisons.
        It isn't so much a new detail of the language, as a way to make use of a detail,
        a clever technique that I hadn't seen before.</p><p>When defining a class, it's often useful to define an equality comparison so that
        instances of your class can be considered equal.  For example, in an object with three
        attributes, the typical way to define __eq__ is like this:</p><blockquote class="code"><tt><span class="k">class</span>&#xA0;<span class="nc">Thing</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span>&#xA0;<span class="n">a</span><span class="p">,</span>&#xA0;<span class="n">b</span><span class="p">,</span>&#xA0;<span class="n">c</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">a</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">a</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">b</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">b</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">c</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">c</span>
<br>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span>&#xA0;<span class="n">other</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">print</span>&#xA0;<span class="s">&quot;Comparing&#xA0;</span><span class="si">%r</span><span class="s">&#xA0;and&#xA0;</span><span class="si">%r</span><span class="s">&quot;</span>&#xA0;<span class="o">%</span>&#xA0;<span class="p">(</span><span class="bp">self</span><span class="p">,</span>&#xA0;<span class="n">other</span><span class="p">)</span>&#xA0;
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="p">(</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">a</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">other</span><span class="o">.</span><span class="n">a</span>&#xA0;<span class="ow">and</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">b</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">other</span><span class="o">.</span><span class="n">b</span>&#xA0;<span class="ow">and</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">c</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">other</span><span class="o">.</span><span class="n">c</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="p">)</span>
<br></tt></blockquote><p>When run, it shows what happens:</p><blockquote class="code"><tt><span class="o">&gt;&gt;&gt;</span>&#xA0;<span class="n">x</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">Thing</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>
<br><span class="o">&gt;&gt;&gt;</span>&#xA0;<span class="n">y</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">Thing</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>
<br><span class="o">&gt;&gt;&gt;</span>&#xA0;<span class="k">print</span>&#xA0;<span class="n">x</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">y</span>
<br><span class="n">Comparing</span>&#xA0;<span class="o">&lt;</span><span class="n">Thing</span>&#xA0;<span class="mi">37088896</span><span class="o">&gt;</span>&#xA0;<span class="ow">and</span>&#xA0;<span class="o">&lt;</span><span class="n">Thing</span>&#xA0;<span class="mi">37088952</span><span class="o">&gt;</span>
<br><span class="bp">True</span>
<br></tt></blockquote><p>Here the __eq__ method compares the three attributes directly on the self and other objects,
        and returns a boolean, a simple direct comparison.</p><p>But on IRC, a different technique was proposed:</p><blockquote class="code"><tt><span class="k">class</span>&#xA0;<span class="nc">Thing</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span>&#xA0;<span class="n">a</span><span class="p">,</span>&#xA0;<span class="n">b</span><span class="p">,</span>&#xA0;<span class="n">c</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">a</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">a</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">b</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">b</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">c</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">c</span>
<br>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span>&#xA0;<span class="n">other</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">print</span>&#xA0;<span class="s">&quot;Comparing&#xA0;</span><span class="si">%r</span><span class="s">&#xA0;and&#xA0;</span><span class="si">%r</span><span class="s">&quot;</span>&#xA0;<span class="o">%</span>&#xA0;<span class="p">(</span><span class="bp">self</span><span class="p">,</span>&#xA0;<span class="n">other</span><span class="p">)</span>&#xA0;
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span>&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">,</span>&#xA0;<span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="p">)</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">other</span>
<br></tt></blockquote><p>Now when we run it, something unusual happens:</p><blockquote class="code"><tt><span class="o">&gt;&gt;&gt;</span>&#xA0;<span class="n">x</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">Thing</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>
<br><span class="o">&gt;&gt;&gt;</span>&#xA0;<span class="n">y</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">Thing</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>
<br><span class="o">&gt;&gt;&gt;</span>&#xA0;<span class="k">print</span>&#xA0;<span class="n">x</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">y</span>
<br><span class="n">Comparing</span>&#xA0;<span class="o">&lt;</span><span class="n">Thing</span>&#xA0;<span class="mi">37219968</span><span class="o">&gt;</span>&#xA0;<span class="ow">and</span>&#xA0;<span class="o">&lt;</span><span class="n">Thing</span>&#xA0;<span class="mi">37220024</span><span class="o">&gt;</span>
<br><span class="n">Comparing</span>&#xA0;<span class="o">&lt;</span><span class="n">Thing</span>&#xA0;<span class="mi">37220024</span><span class="o">&gt;</span>&#xA0;<span class="ow">and</span>&#xA0;<span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>
<br><span class="bp">True</span>
<br></tt></blockquote><p>Our __eq__ is being called twice!  The first time, it's called with two
    Thing objects, and it tries to compare a tuple of (1, 2, 3) to other, which
    is y, which is a Thing.  Tuples don't support comparison to Thing's, so it
    returns NotImplemented.  The == operator handles that case, and relying
    on the commutative nature of ==, tries swapping the two arguments.  That
    means comparing y to (1, 2, 3), which calls our __eq__ again.  Now it
    compares (1, 2, 3) to (1, 2, 3), which succeeds, producing the final True
    result.</p><p>This is an interesting technique, but I'm not sure I like it.  For one thing, the code 
    doesn't read clearly.  It's comparing a tuple to an object, which isn't supported.
    It only makes sense when you keep in mind the argument-swapping dance.</p><p>For another, it make operations work that maybe shouldn't:</p><blockquote class="code"><tt><span class="n">x</span>&#xA0;<span class="o">==</span>&#xA0;<span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>
<br><span class="p">(</span><span class="mi">1</span><span class="p">,</span>&#xA0;<span class="mi">2</span><span class="p">,</span>&#xA0;<span class="mi">3</span><span class="p">)</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">x</span>
<br></tt></blockquote><p>I don't know that I want these comparisons to succeed.  It exposes internals
    that should be hidden.  Of course, why would a caller who didn't know the
    internals try a comparison like this?  But things like this have a way of
    creeping out to bite you.</p><p>I'm glad to have a better understanding of the workings of comparisons,
    but I'm not sure I'll write them like this.</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201201/decorated_fabric_over_the_edge.html">
		<title>Decorated fabric over the edge</title>
		<link>http://nedbatchelder.com/blog/201201/decorated_fabric_over_the_edge.html</link>
		
		<dc:date>2012-01-10T10:21:16-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>Like many, I use <a class="offsite" href="http://fabfile.org">Fabric</a> to write deploy procedures, 
        but I feel like I'm doing it wrong. Fabric is fundamentally based on the ideas of
        "hosts" and "tasks".  You write a Python file whose functions are tasks, and 
        from the command line you can ask that a list of tasks be performed on a list of
        hosts.</p><p>Tasks can be decorated to affect their execution, for example, the @runs_once decorator
        will mean the function is only executed once, no matter how many hosts are specified.
        This can be useful for performing initial work, such as preparing a tarball to be copied
        to many hosts. So for example, I can write something like this:</p><blockquote class="code"><tt><span class="nd">@task</span>
<br><span class="k">def</span>&#xA0;<span class="nf">deploy</span><span class="p">():</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">make_tarball</span><span class="p">()</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">copy_tarball</span><span class="p">()</span>
<br>
<br><span class="nd">@runs_once</span>
<br><span class="k">def</span>&#xA0;<span class="nf">make_tarball</span><span class="p">()</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="c">#&#xA0;..&#xA0;create&#xA0;a&#xA0;.tgz&#xA0;..</span>
<br>
<br><span class="k">def</span>&#xA0;<span class="nf">copy_tarball</span><span class="p">()</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">put</span><span class="p">(</span><span class="s">&#39;the_tar_ball.tgz&#39;</span><span class="p">,</span>&#xA0;<span class="s">&#39;/tmp/&#39;</span><span class="p">)</span>
<br></tt></blockquote><p>Fabric will run this by running the deploy task for each host, which will call both make_tarball and copy_tarball,
        but the runs_once decorator on make_tarball means that it will only be executed for the first host, 
        while copy_tarball will be executed for all of them.</p><p>This is great, and building on it for a multi-server deploy, I wanted to have functions
        that would be run on a subset of the hosts.  I have servers divided into roles:
        app server vs. static content server, for example.  Fabric provides a role system, and
        includes a @roles decorator to control what gets run where:</p><blockquote class="code"><tt><span class="n">env</span><span class="o">.</span><span class="n">roledefs</span><span class="o">.</span><span class="n">update</span><span class="p">({</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="s">&#39;app&#39;</span><span class="p">:</span>&#xA0;<span class="p">[</span><span class="s">&#39;www1&#39;</span><span class="p">,</span>&#xA0;<span class="s">&#39;www2&#39;</span><span class="p">],</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="s">&#39;static&#39;</span><span class="p">:</span>&#xA0;<span class="p">[</span><span class="s">&#39;stat1&#39;</span><span class="p">]</span>
<br><span class="p">})</span>
<br>
<br><span class="nd">@roles</span><span class="p">(</span><span class="s">&#39;app&#39;</span><span class="p">)</span>
<br><span class="k">def</span>&#xA0;<span class="nf">my_func</span><span class="p">():</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">pass</span>
<br></tt></blockquote><p>But we run into a problem: @roles only works on top-level tasks invoked from the command line.
        If I decorate my copy_tarball function with it, it will be ignored.  This is because
        of how the decorator has been written: it annotates the function with role information,
        and the Fabric main loop knows how to read that annotation to decide what tasks to run
        on which hosts.</p><p>I wanted a deploy script that looked something like this:</p><blockquote class="code"><tt><span class="nd">@task</span>
<br><span class="k">def</span>&#xA0;<span class="nf">deploy</span><span class="p">():</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">copy_to_apps</span><span class="p">()</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">copy_to_static</span><span class="p">()</span>
<br>
<br><span class="nd">@only_roles</span><span class="p">(</span><span class="s">&#39;app&#39;</span><span class="p">)</span>
<br><span class="k">def</span>&#xA0;<span class="nf">copy_to_apps</span><span class="p">():</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="c">#..&#xA0;copy&#xA0;stuff&#xA0;..</span>
<br>
<br><span class="nd">@only_roles</span><span class="p">(</span><span class="s">&#39;static&#39;</span><span class="p">)</span>
<br><span class="k">def</span>&#xA0;<span class="nf">copy_to_static</span><span class="p">():</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="c">#..&#xA0;copy&#xA0;stuff&#xA0;..</span>
<br></tt></blockquote><p>So I wrote my own decorator to do roles the way I wanted:</p><blockquote class="code"><tt><span class="k">def</span>&#xA0;<span class="nf">only_roles</span><span class="p">(</span><span class="o">*</span><span class="n">roles</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="sd">&quot;&quot;&quot;Make&#xA0;a&#xA0;function&#xA0;run&#xA0;only&#xA0;on&#xA0;hosts&#xA0;that&#xA0;have&#xA0;certain&#xA0;roles.&quot;&quot;&quot;</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">_dec</span><span class="p">(</span><span class="n">fn</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="nd">@functools.wraps</span><span class="p">(</span><span class="n">fn</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">_wrapped</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span>&#xA0;<span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">for</span>&#xA0;<span class="n">role</span>&#xA0;<span class="ow">in</span>&#xA0;<span class="n">roles</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">if</span>&#xA0;<span class="n">env</span><span class="o">.</span><span class="n">host_string</span>&#xA0;<span class="ow">in</span>&#xA0;<span class="n">env</span><span class="o">.</span><span class="n">roledefs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">role</span><span class="p">,</span>&#xA0;<span class="p">()):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">fn</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span>&#xA0;<span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">_wrapped</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">_dec</span>
<br></tt></blockquote><p>But I felt funny about this:  I saw something in the Fabric docs that sounded like
        just what I wanted, but it didn't work as I thought it would, so I had to write
        my own.  This makes me think I'm using Fabric wrong.</p><p>The runs_once decorator is great for doing one-time initial work, and I found I 
        wanted a book-end for it: a way to do one-time cleanup work.  Fabric provided 
        nothing, and I could see why: there's no global knowledge about all the hosts
        and tasks, and no way to specify work to be done after they are through. For
        that matter, there's no way to specify work to be done before they start,
        but @runs_once provides that effect.</p><p>So I wrote another decorator, this one more devious and risky:</p><blockquote class="code"><tt><span class="k">def</span>&#xA0;<span class="nf">runs_last</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="sd">&quot;&quot;&quot;A&#xA0;decorator&#xA0;to&#xA0;run&#xA0;the&#xA0;function&#xA0;only&#xA0;on&#xA0;the&#xA0;*last*&#xA0;host.</span>
<br>
<br><span class="sd">&#xA0;&#xA0;&#xA0;&#xA0;This&#xA0;only&#xA0;works&#xA0;if&#xA0;you&#xA0;don&#39;t&#xA0;apply&#xA0;any&#xA0;other&#xA0;restrictions</span>
<br><span class="sd">&#xA0;&#xA0;&#xA0;&#xA0;on&#xA0;the&#xA0;function.</span>
<br>
<br><span class="sd">&#xA0;&#xA0;&#xA0;&#xA0;&quot;&quot;&quot;</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">times_invoked</span>&#xA0;<span class="o">=</span>&#xA0;<span class="mi">0</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="nd">@functools.wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span>&#xA0;<span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">times_invoked</span>&#xA0;<span class="o">+=</span>&#xA0;<span class="mi">1</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">all_hosts</span>&#xA0;<span class="o">=</span>&#xA0;<span class="nb">set</span><span class="p">()</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">for</span>&#xA0;<span class="n">hosts</span>&#xA0;<span class="ow">in</span>&#xA0;<span class="n">env</span><span class="o">.</span><span class="n">roledefs</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">all_hosts</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">hosts</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">if</span>&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">times_invoked</span>&#xA0;<span class="o">==</span>&#xA0;<span class="nb">len</span><span class="p">(</span><span class="n">all_hosts</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span>&#xA0;<span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">decorated</span>
<br></tt></blockquote><p>Here we count the number of invocations, and guess at the number of the last one
        based on the hosts we know about.  There are a variety of ways this could not
        work, but it was fine in my environment.</p><p>Since I'm sharing useful Fabric decorators, here's one that prevents repetitive
        work being done that won't have any extra effect:</p><blockquote class="code"><tt><span class="k">def</span>&#xA0;<span class="nf">idempotent</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="sd">&quot;&quot;&quot;Don&#39;t&#xA0;invoke&#xA0;`func`&#xA0;more&#xA0;than&#xA0;once&#xA0;for&#xA0;host&#xA0;and&#xA0;arguments.&quot;&quot;&quot;</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">past_results</span>&#xA0;<span class="o">=</span>&#xA0;<span class="p">{}</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="nd">@functools.wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">def</span>&#xA0;<span class="nf">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span>&#xA0;<span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">key</span>&#xA0;<span class="o">=</span>&#xA0;<span class="p">(</span><span class="n">env</span><span class="o">.</span><span class="n">host_string</span><span class="p">,</span>&#xA0;<span class="n">args</span><span class="p">,</span>&#xA0;<span class="nb">tuple</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">()))</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">if</span>&#xA0;<span class="n">key</span>&#xA0;<span class="ow">not</span>&#xA0;<span class="ow">in</span>&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">past_results</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">past_results</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span>&#xA0;<span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">func</span><span class="o">.</span><span class="n">past_results</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">decorated</span>
<br></tt></blockquote><p>Am I using Fabric wrong? It seems like maybe I'm expecting it to do too much,
        like the right way is to have my deploy() function in a larger script somehow.
        Or is Fabric fine like this, and I've just missed the right path?</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201201/safari_buys_threepress.html">
		<title>Safari buys Threepress</title>
		<link>http://nedbatchelder.com/blog/201201/safari_buys_threepress.html</link>
		
		<dc:date>2012-01-09T09:08:10-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>For the last year, I've been doing a lot of freelance work for <a class="offsite" href="http://threepress.org">Threepress</a>
        on Ibis Reader, their HTML5 e-book platform.  The good news out this morning is that they 
        have been <a class="offsite" href="http://techcrunch.com/2012/01/09/digital-publisher-safari-books-online-acquires-threepress-ibis-reader/">acquired by Safari Books Online</a>.</p><p>This is a great fit, because of their already-shared business, technology, and culture. Congratulations to
        Liza, Keith, Jonathan, and Chuck, who have all done great work, and will now be Safari employees.
        Xconomy has <a class="offsite" href="http://www.xconomy.com/boston/2012/01/09/safari-books-buys-threepress-forges-ahead-in-digital-publishing-jungle/?single_page=true">some sound bites</a>
        from Liza and her new boss about the acquisition.</p><p>The down-side for me is that I will not be working as much with them now, but there may be some smaller
        projects, and I'm really glad to have had the year with them that I did.</p><p>PS: it isn't mentioned in the new blurbs, but Ibis Reader is a Django project, so this is a big plus for Django and Python also.</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201201/where_can_i_walk_to_in_30_minutes.html">
		<title>Where can I walk to in 30 minutes?</title>
		<link>http://nedbatchelder.com/blog/201201/where_can_i_walk_to_in_30_minutes.html</link>
		
		<dc:date>2012-01-07T12:12:40-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>I like to walk to explore, even in my home neighborhood.  So I like to find new places to go.
        I wanted to print map and draw a 1½-mile radius circle on it to find a good range.
    </p><p>But the set of points you can reach with a mile and a half of walking is not a circle, it will
        depend on where the streets are.  Anyone know of a map app that can show me the actual
        range?</p><p><a class="offsite" href="http://mapnificent.net">Mapnificent</a> is a similar idea: it shows how far you can get in
        a given amount of time, using public transportation, walking, and maybe biking.  But it also
        just draws circles.</p><p>Is what I want out there?</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201201/happy_and_quiet.html">
		<title>Happy and quiet</title>
		<link>http://nedbatchelder.com/blog/201201/happy_and_quiet.html</link>
		
		<dc:date>2012-01-02T13:00:55-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>Happy New Year everyone, I hope your 2011 was good, and that 2012 will be
        even better.  This year I hear people talking about "intentions" rather
        than "resolutions," is that a reflection of reality, or an early
        cop-out so that failure doesn't feel like "failure"?</p><p>When I think about the year past and the year ahead, my "intention" is
        to spend my time more mindfully.  That doesn't dictate how I will spend
        it, or even that I should spend it purposefully, but that I should
        decide how to spend it, rather than falling into habits and ruts.</p><p>In Pico Iyer's relevant and recent widely-circulated piece, <a class="offsite" href="http://www.nytimes.com/2012/01/01/opinion/sunday/the-joy-of-quiet.html">The Joy of Quiet</a>,
        he notes our "progress:"</p><blockquote><div><p>In barely one generation we’ve moved from exulting in the
            time-saving devices that have so expanded our lives to trying to
            get away from them — often in order to make more time. The more
            ways we have to connect, the more many of us seem desperate to
            unplug. Like teenagers, we appear to have gone from knowing nothing
            about the world to knowing too much all but overnight.</p></div></blockquote><p>Luckily, the bulk of the essay is not on this same, "things are so
        different now" theme.  In fact, he quotes Blaise Pascal: </p><blockquote><div><p>Distraction is the only thing that consoles us for our miseries,
            and yet it is itself the greatest of our miseries.</p></div></blockquote><p>Later, he calls on Thoreau:</p><blockquote><div><p>We have more and more ways to communicate, as Thoreau noted,
                but less and less to say. Partly because we’re so busy
                communicating.</p></div></blockquote><p>Here's the <a class="offsite" href="http://thoreau.eserver.org/walden1d.html#5">full Thoreau paragraph</a>:</p><blockquote><div><p>As with our colleges, so with a hundred "modern improvements";
            there is an illusion about them; there is not always a positive
            advance. The devil goes on exacting compound interest to the last
            for his early share and numerous succeeding investments in them.
            Our inventions are wont to be pretty toys, which distract our
            attention from serious things. They are but improved means to an
            unimproved end, an end which it was already but too easy to arrive
            at; as railroads lead to Boston or New York. We are in great haste
            to construct a magnetic telegraph from Maine to Texas; but Maine
            and Texas, it may be, have nothing important to communicate. Either
            is in such a predicament as the man who was earnest to be
            introduced to a distinguished deaf woman, but when he was
            presented, and one end of her ear trumpet was put into his hand,
            had nothing to say. As if the main object were to talk fast and not
            to talk sensibly. We are eager to tunnel under the Atlantic and
            bring the Old World some weeks nearer to the New; but perchance the
            first news that will leak through into the broad, flapping American
            ear will be that the Princess Adelaide has the whooping cough.
            After all, the man whose horse trots a mile in a minute does not
            carry the most important messages; he is not an evangelist, nor
            does he come round eating locusts and wild honey. I doubt if Flying
            Childers [an English racehorse] ever carried a peck of corn to mill.</p></div></blockquote><p>So which is it? Are we in a bad state that has developed in the last
        generation, or are we merely in the same predicament as Pascal and
        Thoreau?  I think it's easy for people to lament their situation and
        think their condition is a recent malady.  Even Thoreau complains of
        "modern improvements," but if Thoreau complained in 1854, and Pascal in
        1658, then how can we honestly blame 21st-century developments for our
        situation?</p><p>People are omnivores, and along with meat and vegetables, we consume
        information, attention and distraction, craving as much as we can get.
        Too much food will make us fat, too much distraction will make us,
        well, distracted. It's true now, and it was true in Thoreau's time.</p><p>The new technologies certainly contribute to the problem, but they
        didn't create it.  Thoreau's quip about the "broad, flapping American
        ear" could have been written about tabloids, TV, and Twitter, but it
        wasn't.  Iyer's solution is quiet, a respite from connection, but I
        think we can also use the tools we have in better ways.</p><p>I liked Alex Soojung-Kim Pang's <a class="offsite" href="http://www.contemplativecomputing.org/2011/12/pico-iyer-on-the-joy-of-quiet.html">conclusion</a>:</p><blockquote><div><p>Connectivity and silence, collaboration and contemplation,
            sociability and solitude, are not opposites, and we shouldn't think
            that we should choose between one and the other. Rather, they're
            like food and water, or parents and children. Each is essential;
            they're different but not mutually exclusive. The great challenge
            is to find places for them all, and to know how to use
            them.</p></div></blockquote><p>So, along with the usual resolutions to eat more healthfully, here's to 
        spending time more mindfully.</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html">
		<title>Keep data out of your variable names</title>
		<link>http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html</link>
		
		<dc:date>2011-12-31T09:55:58-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>I saw this question this morning:</p><blockquote><div>
    <p>I'm adding words to lists depending on what character they begin with. This seems a silly way to do it, though it works:</p>
</div></blockquote><blockquote class="code"><tt><span class="n">nouns</span>&#xA0;<span class="o">=</span>&#xA0;<span class="nb">open</span><span class="p">(</span><span class="s">&#39;nouns.txt&#39;</span><span class="p">,</span>&#xA0;<span class="s">&#39;r&#39;</span><span class="p">)</span>
<br><span class="k">for</span>&#xA0;<span class="n">word</span>&#xA0;<span class="ow">in</span>&#xA0;<span class="n">nouns</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">word</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">word</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">if</span>&#xA0;<span class="n">word</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>&#xA0;<span class="o">==</span>&#xA0;<span class="s">&#39;a&#39;</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">a</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">elif</span>&#xA0;<span class="n">word</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>&#xA0;<span class="o">==</span>&#xA0;<span class="s">&#39;b&#39;</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">b</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">elif</span>&#xA0;<span class="n">word</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>&#xA0;<span class="o">==</span>&#xA0;<span class="s">&#39;c&#39;</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">c</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="c">#&#xA0;etc...</span>
<br></tt></blockquote><p>Naturally, the answer here is to make a dictionary keyed by first letter:</p><blockquote class="code"><tt><span class="n">words</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
<br><span class="k">for</span>&#xA0;<span class="n">word</span>&#xA0;<span class="ow">in</span>&#xA0;<span class="n">nouns</span><span class="p">:</span>
<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="n">words</span><span class="p">[</span><span class="n">word</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
<br></tt></blockquote><p>The question reminded me of others I've seen on Stack Overflow or in the #python IRC channel:</p><ul>
    <li>How do I see if a variable exists?</li>
    <li>How do I use a variable as the name of another variable?</li>
    <li>How do I use a variable as part of a SQL table name?</li>
</ul><p>The thing all these have in common is trying to bridge the gap between two domains:
    the data in your program, and the names of data in your program.  Any time this happens,
    it's a clear sign that you need to move up a level in your data modeling. Instead of
    26 lists, you need one dictionary.  Instead of N tables, you should have one table,
    with one more column in it.</p><p>These situations all seem really obvious, but there are more subtle situations where this
    dynamic appears. I just wish I could think of an example! :)</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201112/happy_hanukkah.html">
		<title>Happy Hanukkah</title>
		<link>http://nedbatchelder.com/blog/201112/happy_hanukkah.html</link>
		
		<dc:date>2011-12-20T13:55:15-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>Tonight is the first night of Hanukkah.  Of course, most calendars confusingly label
        tomorrow with "Hanukkah starts."  I wish instead they would label today with "Hanukkah starts tonight."
    </p><p>I've chatted with plenty of friends over the years about the different natures of
        Christmas and Hanukkah, and finally hit on an apt metaphor: Christmas is like the
        Super Bowl, Hanukkah is like the World Series.</p><p>Like the Super Bowl, Christmas is one concentrated day.  For celebrants, it is the only 
        thing happening that day, and it's preceded by weeks of anticipation and preparation.
        Another similarity is the zeal with which businesses try to piggy-back on the excitement.</p><p>Hanukkah, on the other hand, is spread out over about a week, like the World Series. 
        While the daylight passes as it normally would, the evenings are spent specially, and 
        the entire week is tinged with a special feeling because of it.  If you have to miss
        one night for another reason, that's OK, you've got others you can celebrate.</p><p>Each has its special feeling, either because of its intensity, or because of its calm.
        I enjoy them both.</p><p>If you celebrate Hanukkah, take pleasure in lighting your candles tonight!</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201112/stop_sopa.html">
		<title>Stop SOPA</title>
		<link>http://nedbatchelder.com/blog/201112/stop_sopa.html</link>
		
		<dc:date>2011-12-15T07:26:27-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>The <a href="https://en.wikipedia.org/wiki/Stop_Online_Piracy_Act">Stop Online Piracy Act</a> is the latest
        battle in the war between the movie studios and the rest of us.  It's a really bad bill that, depending
        on who you listen to, would either kill jobs in the US, or radically change the entire Internet, or
        both.</p><p>If you read this blog, you've probably already been pelted with calls to action. You may have figured
        you wouldn't do anything because it wouldn't have an effect.  <a class="offsite" href="http://americancensorship.org/modal/call-form.html">Do something anyway</a>.
        It's the right thing to do.</p><p>When I was working for Hewlett-Packard, one of the advantages over being at a startup was that we could sign
        licensing deals with other really big companies, like Disney.  I met once with a pair of HP guys
        who oversaw that sort of work. They used to work for the movie studios in a similar capacity.
        The day I met with them at our offices I happened to be wearing an <a href="https://www.eff.org/deeplinks/2011/10/sopa-hollywood-finally-gets-chance-break-internet">EFF</a> t-shirt, just because it
        was the top one on the pile that morning.  When they saw me in it, they looked at me like I was 
        carrying a sign that read, "I shoot babies."</p><p>They proceeded to tell me a story about a high-end TV console manufacturer that was building a
        $50k device that would let you feed in DVDs, and rip them to an internal hard-drive, then you'd
        have your entire collection of movies on a menu that you could access instantly.  The point of
        the story was that the movie studios forced them to stop making this product, because although
        it was a closed system that would appeal to their best and richest paying customers, 
        it involved ripping DVDs, and that was bad.  So they were closing off new avenues of business
        because of the internal technology involved. So dumb.</p><p>One of them then launched into a rant about what would the world be like if the movie industry
        was put out of business by piraters? How would people be entertained? "The movie industry performs
        a valuable service, finding and producing the best entertainment!"  Movies are great, it's true,
        but there's no constitutional right to the existence of a movie industry.  It's existed for
        only about 100 years.  Not only would people entertain themselves without it, just as they did before 
        the movie industry existed, they may find better ways to do it.</p><p>Technology changes, and businesses sometimes suffer.  The horse-buggy industry didn't try to write
        laws preventing automobiles.  The vaudevillians didn't lobby their congressmen to stop movies.
        Radio didn't restrict freedom of speech in an effort to block TV.</p><p>I don't want the movie industry to go out of business, and I don't think it will. Their desperate
        actions to stop their perceived enemies are wrong-headed and technologically a bad idea.</p><p>SOPA is like burning down a house to get rid of the mice. How far are we willing to
        let these industries go in their desperate effort to stop piracy?  Piracy's not a good thing,
        it's true, but the "solutions" in the SOPA are worse.</p><p><a class="offsite" href="http://americancensorship.org/modal/call-form.html">Call your congressman</a>.</p>
]]></description>
	</item>
	
	<item rdf:about="http://nedbatchelder.com/blog/201112/deleting_files_keeping_a_few.html">
		<title>Deleting files, keeping a few</title>
		<link>http://nedbatchelder.com/blog/201112/deleting_files_keeping_a_few.html</link>
		
		<dc:date>2011-12-12T09:47:11-05:00</dc:date>
		<dc:creator>Ned Batchelder</dc:creator>
		<description><![CDATA[<p>This is one of those conceptually easy tasks that seems frequently required, and yet needs a complex
        incantation to accomplish.  I have a series of files, and it will grow over time, and I want
        to clean them up, but keep the most recent N files.</p><p>After poking around the Google, I found this for deleting PATTERN, but keeping the five most recent:</p><blockquote class="code"><tt>ls -t1 PATTERN | tail -n +6 | xargs -r rm -r</tt></blockquote><p>That's dash-t-one on the ls command.  Or, in words:</p><ol>
        <li>List files matching PATTERN, in descending order of modification time, in one column,</li>
        <li>Pass through all the trailing lines, starting with the sixth from the beginning,</li>
        <li>Bundle all those filenames into an "rm -r" command, but not if there are none.</li>
    </ol><p>That wasn't so hard, was it??</p>
]]></description>
	</item>
	
</rdf:RDF>

