<?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/200807/walle.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/spore_creature_creator_and_steganography.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/subversions_biggest_hole.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/reporting_server_reliability.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/python_import_helper.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/iron_man_videos.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/biology_factoid_of_the_day.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/bad_web_typography_full_justify.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/pylint.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200806/one_of_the_worst_decisions_in_history.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/200807/walle.html">
        <title>WALL-E</title>
        <link>http://nedbatchelder.com/blog/200807/walle.html</link>
		
        <dc:date>2008-07-01T07:12:45-04:00</dc:date>
        <description><![CDATA[<p>I saw Pixar's latest, <a class="offsite" href="http://www.pixar.com/theater/trailers/walle/index.html">WALL-E</a> yesterday, and am of two minds about it.
I really enjoyed it, it's a great movie.  But it's not as great as all the gushing
reviews are making it out.
</p><p>The first half-hour of the movie is outstanding: it's a moody evocative story
drawn with an apocalyptic palette, with a cute protagonist in the middle of it all.
Without words, WALL-E draws us in and makes us feel for him.  This is the part
of the movie people are talking about when they say it is a great sci-fi film.
</p><p>The second half of the movie takes place in space, but ironically is where
the movie falls under the cartoon gravity of a kid's movie again.  The plot takes
over, and simplistic characters and turns are the norm.  Don't get me wrong, it's
a great kid's movie, one of Pixar's best.  WALL-E himself is a great character,
a believable robot with big expressive eyes that perfectly convey his emotion to us.</p><p>But let's get something straight: <a class="offsite" href="http://nymag.com/daily/entertainment/2008/06/start_the_campaign_walle_for_b.html">the people</a>
who are
<a class="offsite" href="http://claytonboen.com/should-wall-e-be-nominated-for-best-picture/">talking about</a>
a <a class="offsite" href="http://www.justpressplay.net/movies/movie-news/41/3605.html">Best Picture Oscar</a>
are crazy.  Forget the <a class="offsite" href="http://goldderby.latimes.com/awards_goldderby/2008/06/news-wall-e-821.html">insider movie calculus</a>
that says it won't happen: it's just not that good a movie.  It isn't that it's
animated: WALL-E's earth-bound segment proves that CGI animation can carry a rich
story just fine.  It's that it's a cartoon: by the time the movie is over, the
plot has been neatly wrapped up, a few strange holes in logic have been glossed
over, our heartstrings have been expertly tugged, and we can go home happy.</p><p>A Best Picture can end happily of course, but stepping back to take in WALL-E's
full structure, you can see that it's a kid's movie. That means trading subtlety
for some physical humor, all of which is fine, but it means you have to give up
your seat at the grown-up's table.  WALL-E is rated G, a sign that the material
is mild enough for small children, without the depth of story that a Best Picture needs to have.
<a class="offsite" href="http://en.wikipedia.org/wiki/Academy_Award_for_Best_Picture">Oliver!</a>
was the only G-rated Best Picture, and that was in the early days of the rating system.
It's hard to imagine it would be rated G today.
</p><p>I'm not even sure that WALL-E is my favorite Pixar movie.  Finding Nemo and
The Incredibles I thought did a better job consistently hitting their mark, and
finding richness in the stories they told.  The ultimate mark of a great story
is the development of characters over the course of the movie, and for that,
it's hard to beat Pixar's first, Toy Story.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/spore_creature_creator_and_steganography.html">
        <title>Spore creature creator and steganography</title>
        <link>http://nedbatchelder.com/blog/200806/spore_creature_creator_and_steganography.html</link>
		
        <dc:date>2008-06-25T06:43:51-04:00</dc:date>
        <description><![CDATA[<p><a class="offsite" href="http://www.spore.com/">Spore</a> is the wildly anticipated new game
from Will Wright, and <a class="offsite" href="http://www.spore.com/what/screensmovies#creator">Creature Creator</a>
is the first part of it to be released for us to try. It allows you to build
creatures, Mr-Potato-Head-style, which will eventually be usable in the full
game:</p><p align="center"><img src="http://nedbatchelder.com/pix/500001332762.png" alt="A Spore creature" width="128" height="128"><img src="http://nedbatchelder.com/pix/500002605560.png" alt="A Spore creature" width="128" height="128"><img src="http://nedbatchelder.com/pix/500002620239.png" alt="A Spore creature" width="128" height="128"></p><p>It's fun to put arms and legs and body parts together to make creatures,
but the more impressive part of the technology is that once you make your creature,
it's fully animated already, with a repertoire of moves like walking, sitting,
dancing and greeting.  This is no small feat considering you aren't constrained
to building a humanoid creature.  For example, <a class="offsite" href="http://www.spore.com/sporepedia#qry=usr-nedbatchelder">my tricyclotops</a>
has three legs, and that front centered leg participates in the animations in a
way that seems very natural, considering I've never seen a creature with legs
in that formation. 
</p><p>The developers behind this animation have written up the technology:
<a class="offsite" href="http://chrishecker.com/Real-time_Motion_Retargeting_to_Highly_Varied_User-Created_Morphologies">Real-time
Motion Retargeting to Highly Varied User-Created Morphologies</a>. One of the
authors, John DeWeese, has <a class="offsite" href="http://www.spore.com/view/profile/MaxisJD">a handful of riotously varied creatures</a>
on his Spore page.</p><p>If you look at the sidebar on the Spore creature pages, you'll see instructions
that you can save those PNG files, and drag them into Creator, and you'll have
the creature.  That interested me: one of the things I did with <a href="http://nedbatchelder.com/code/aptus">Aptus</a>
was to save the coordinate info for a picture as a
<a class="offsite" href="http://www.libpng.org/pub/png/spec/1.0/PNG-Chunks.html#C.tEXt">tEXt record</a>
in the PNG file. Aptus can open a PNG file it saved, and instead of dealing with
pixel data, can read the coordinates and recreate the Mandelbrot view directly,
allowing you to continue exploring from there.</p><p>Looking at the PNGs on the Spore page though, they have not done this. There
is no data other than the image.  But dragging the PNG into Creator does indeed
give you the creature as structured data.  Renaming the PNG doesn't affect the
data transfer, but any sort of editing of the image does.
They're using <a class="offsite" href="http://en.wikipedia.org/wiki/Steganography">steganography</a>,
hiding one message inside another.  In this case, they seem to be using the least
significant bits in all the pixels.</p><p>Some quick Python shows details. Using <a class="offsite" href="http://www.pythonware.com/library/pil/handbook/index.htm">PIL</a>, we can examine
the numeric values of the pixels:</p><blockquote class="code"><tt><span class="p_commentline">#&#160;Open&#160;an&#160;image,&#160;and&#160;show&#160;the&#160;RGBA&#160;data&#160;for&#160;the&#160;first&#160;ten&#160;pixels.</span><br/>
<span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">Image</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">sys</span><br/>
<br/>
<span class="p_identifier">im</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">Image</span><span class="p_operator">.</span><span class="p_identifier">open</span><span class="p_operator">(</span><span class="p_identifier">sys</span><span class="p_operator">.</span><span class="p_identifier">argv</span><span class="p_operator">[</span><span class="p_number">1</span><span class="p_operator">])</span><br/>
<span class="p_word">for</span><span class="p_default">&#160;</span><span class="p_identifier">pix</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">list</span><span class="p_operator">(</span><span class="p_identifier">im</span><span class="p_operator">.</span><span class="p_identifier">getdata</span><span class="p_operator">())[:</span><span class="p_number">10</span><span class="p_operator">]:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">print</span><span class="p_default">&#160;</span><span class="p_identifier">pix</span><br/>
</tt></blockquote><p>produces:</p><blockquote class="code"><tt>(0, 1, 0, 1)<br>(0, 1, 1, 1)<br>(0, 0, 1, 0)<br>(1, 1, 0, 0)<br>(1, 0, 0, 0)<br>(0, 0, 1, 0)<br>(1, 1, 0, 1)<br>(0, 0, 1, 0)<br>(1, 1, 1, 0)<br>(0, 1, 1, 0)<br>(1, 0, 1, 1)<br>(1, 0, 1, 0)<br>(0, 0, 0, 1)<br>(0, 1, 1, 1)<br>(0, 1, 0, 1)<br>(1, 0, 1, 1)<br>(0, 1, 1, 1)<br>(1, 0, 1, 1)<br>(0, 1, 0, 0)<br>(1, 1, 0, 1)<br></tt></blockquote><p>These pixels are part of the black transparent edge of the image, except
it isn't truly black and it isn't truly transparent.  There's one
bit of information being encoded in each channel, or four bits per pixel.</p><p>We can go further and yank out the full 8-bit data:</p><blockquote class="code"><tt><span class="p_commentline">#&#160;Open&#160;an&#160;image&#160;file,&#160;read&#160;the&#160;low&#160;bits&#160;as&#160;8-bit&#160;data,</span><br/>
<span class="p_commentline">#&#160;and&#160;write&#160;it&#160;out&#160;to&#160;a&#160;.out&#160;file&#160;next&#160;to&#160;the&#160;image.</span><br/>
<span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">Image</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">sys</span><br/>
<br/>
<span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">stegdata</span><span class="p_operator">(</span><span class="p_identifier">imfile</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Read&#160;the&#160;low&#160;bits&#160;of&#160;pixels&#160;as&#160;8-bit&#160;data.&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">im</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">Image</span><span class="p_operator">.</span><span class="p_identifier">open</span><span class="p_operator">(</span><span class="p_identifier">imfile</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">bytes</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_string">""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">hi</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_number">0</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">for</span><span class="p_default">&#160;</span><span class="p_identifier">ipix</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_operator">(</span><span class="p_identifier">r</span><span class="p_operator">,</span><span class="p_identifier">g</span><span class="p_operator">,</span><span class="p_identifier">b</span><span class="p_operator">,</span><span class="p_identifier">a</span><span class="p_operator">)</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">enumerate</span><span class="p_operator">(</span><span class="p_identifier">im</span><span class="p_operator">.</span><span class="p_identifier">getdata</span><span class="p_operator">()):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">nyb</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_operator">(</span><span class="p_identifier">r</span><span class="p_operator">%</span><span class="p_number">2</span><span class="p_operator">)*</span><span class="p_number">8</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_operator">(</span><span class="p_identifier">g</span><span class="p_operator">%</span><span class="p_number">2</span><span class="p_operator">)*</span><span class="p_number">4</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_operator">(</span><span class="p_identifier">b</span><span class="p_operator">%</span><span class="p_number">2</span><span class="p_operator">)*</span><span class="p_number">2</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_operator">(</span><span class="p_identifier">a</span><span class="p_operator">%</span><span class="p_number">2</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">if</span><span class="p_default">&#160;</span><span class="p_identifier">ipix</span><span class="p_default">&#160;</span><span class="p_operator">%</span><span class="p_default">&#160;</span><span class="p_number">2</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">bytes</span><span class="p_default">&#160;</span><span class="p_operator">+=</span><span class="p_default">&#160;</span><span class="p_identifier">chr</span><span class="p_operator">(</span><span class="p_identifier">hi</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">nyb</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">hi</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_number">0</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">else</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">hi</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">nyb</span><span class="p_operator">*</span><span class="p_number">16</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">return</span><span class="p_default">&#160;</span><span class="p_identifier">bytes</span><br/>
<br/>
<span class="p_identifier">fname</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">sys</span><span class="p_operator">.</span><span class="p_identifier">argv</span><span class="p_operator">[</span><span class="p_number">1</span><span class="p_operator">]</span><br/>
<span class="p_identifier">data</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">stegdata</span><span class="p_operator">(</span><span class="p_identifier">fname</span><span class="p_operator">)</span><br/>
<span class="p_identifier">open</span><span class="p_operator">(</span><span class="p_identifier">fname</span><span class="p_operator">+</span><span class="p_character">'.out'</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_character">'w'</span><span class="p_operator">).</span><span class="p_identifier">write</span><span class="p_operator">(</span><span class="p_identifier">data</span><span class="p_operator">)</span><br/>
</tt></blockquote><p>I had to guess here how to put the bits back together into a byte. 
The results are full-spectrum 8-bit data, but I don't know how to interpret it:</p><blockquote class="code"><tt>000000: 57 2c 82 d2 e6 ba 17 5b  7b 4d 20 32 76 eb f4 8b  W,.....[{M 2v...<br>000010: 4a b7 54 8b b6 9c a7 ba  d5 6a 5f a4 54 15 f1 1f  J.T......j_.T...<br>000020: c6 90 df 98 54 72 6d 62  58 71 69 f6 63 fe 7e 23  ....TrmbXqi.c.~#<br>000030: 49 97 de 81 d7 08 ec 5a  1a 63 57 e6 8e 27 16 03  I......Z.cW..'..<br>000040: 80 5c 56 a1 34 6b d8 fb  49 46 f9 d6 7b 32 ce 6b  .\V.4k..IF..{2.k<br>000050: a3 2c 35 4e f7 e7 52 1a  62 1f ce 8e 47 5f e7 ba  .,5N..R.b...G_..<br>000060: 14 ea 74 58 39 ac eb 53  ee c1 c8 3b cc 38 11 d6  ..tX9..S...;.8..<br>000070: fd 3f dd 41 ff 35 03 a3  67 c4 a6 43 1c 82 24 41  .?.A.5..g..C..$A<br>000080: b7 1d ce 66 5a 32 b3 f0  34 6b f3 0f 73 f9 ee f6  ...fZ2..4k..s...<br>000090: 05 41 56 7b 27 19 40 25  bc e7 b1 02 c9 43 e7 7d  .AV{'.@%.....C.}<br>0000a0: fd b4 11 82 52 1f c8 d0  3c ad 92 ee 1e 57 6d e7  ....R...&lt;....Wm.<br>0000b0: ad fd 72 53 b3 fd 1a 9b  10 52 57 01 86 11 42 7c  ..rS.....RW...B|<br>0000c0: a2 74 ed f6 1b 28 33 cf  7a 19 79 fa b0 6c 04 a7  .t...(3.z.y..l..<br>0000d0: 89 36 c5 08 d8 ee e8 de  5a a3 b8 48 3d 94 62 0d  .6......Z..H=.b.<br>0000e0: 0a 38 4c 21 5d 15 b8 54  e1 ea d7 0b 12 bf 8a a0  .8L!]..T........<br>0000f0: a3 e0 96 1a a8 79 c3 44  62 9e de 02 ea a0 31 8d  .....y.Db.....1.<br>000100: 96 12 e0 7c ad e0 a5 9f  fe 89 54 a6 54 f2 9d 6c  ...|......T.T..l<br>000110: 42 c1 f0 14 8d 15 49 a5  d3 80 2c b1 26 ca af 80  B.....I...,.&amp;...<br>000120: a8 cf a8 a4 77 02 60 ea  c0 d8 4d 2c d9 18 1e 67  ....w.`...M,...g<br>000130: 8f 9a 29 67 30 92 b5 62  da 1d c1 30 21 f8 eb 21  ..)g0..b...0!..!<br>000140: fe d8 c2 a6 64 cf 52 dc  58 d1 0c ef d0 60 fb 9b  ....d.R.X....`..<br>000150: 02 7a e9 d1 d6 a7 3c 01  79 7b da a7 9b 0b ef 3f  .z....&lt;.y{.....?<br>000160: 80 a3 d1 87 d2 81 50 d1  a2 59 c0 65 c3 8b c5 7b  ......P..Y.e...{<br>000170: 8c e5 56 50 bf c2 6e 50  82 26 23 9a 76 2b e7 3b  ..VP..nP.&amp;#.v+.;<br>000180: 4f 5a ec f3 87 aa 27 fe  33 74 40 48 ba db 4f 25  OZ....'.3t@H..O%<br>... ...<br></tt></blockquote><p>Nothing jumps out at me here.  As an exercise in code-breaking, this one is
probably possible, since we have a way to generate as many cases as we need.
I looked at other images, and there was no clear pattern.</p><p>It's interesting that Spore chose steganography here, since it's
usually described as a way to hide a message so that its very existence is a
secret.  But there's no sensitive data here, and they tipped us off to its
presence with their instructions.  Perhaps they wanted to save space by using
those unneeded low bits?  Perhaps they didn't have the tools for manipulating
tEXt records?</p><p>In any case, Spore is already a fertile breeding ground, both for wild new
life forms, and geek interest in its technology.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/subversions_biggest_hole.html">
        <title>Subversion&#39;s biggest hole</title>
        <link>http://nedbatchelder.com/blog/200806/subversions_biggest_hole.html</link>
		
        <dc:date>2008-06-21T09:12:54-04:00</dc:date>
        <description><![CDATA[<p><a class="offsite" href="http://subversion.tigris.org/svn_1.5_releasenotes.html">Subversion 1.5
has just been released</a>, but it does nothing to fix what I consider to be
Subversion's biggest hole.  I know people deride svn for not being
distributed, or for doing a bad job of merging, and both of those will be
solved when everyone finally switches over to <a class="offsite" href="http://git.or.cz/">git</a>,
as I'm sure we all will eventually.
</p><p>But I don't miss those things.  The thing I miss that Subversion is lacking
is a repository setting for globally ignored files.  You can set a property
on a directory to ignore (for example) *.pyc files.  And you can set your client
to always ignore *.pyc files in any working tree on your machine.  But there is
no way to set a repository to ignore *.pyc files anywhere they appear in the tree.
</p><p>This is such a common need in software development, and is truly a property of
the repository, not the client.  Why are we still either touching every directory
in the tree, or touching every client on the team?</p><p>And probably this will also be solved when we all switch over to git...</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/reporting_server_reliability.html">
        <title>Reporting server reliability</title>
        <link>http://nedbatchelder.com/blog/200806/reporting_server_reliability.html</link>
		
        <dc:date>2008-06-19T06:41:07-04:00</dc:date>
        <description><![CDATA[<p>The next time someone inquires about how reliable your system is, say this:
</p><blockquote><div><p>We're almost at five 9's: we're at five 8's!</p></div></blockquote><p>If you don't know what I'm talking about, the Wikipedia article on
<a class="offsite" href="http://en.wikipedia.org/wiki/Uptime">uptime</a> explains it pretty well:
"Five nines" refers to a system being available 99.999% of the time, and is considered
really good. "Five eights" would be 88.888% of the time, which would be horrible,
and is in no way considered "almost" five nines.
</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/python_import_helper.html">
        <title>Python import helper</title>
        <link>http://nedbatchelder.com/blog/200806/python_import_helper.html</link>
		
        <dc:date>2008-06-18T07:11:23-04:00</dc:date>
        <description><![CDATA[<p><a href="http://nedbatchelder.com/code/aptus">Aptus</a> has dependencies on three large packages,
<a class="offsite" href="http://wxpython.org">wxPython</a>,
<a class="offsite" href="http://numpy.scipy.org">Numpy</a>, and
<a class="offsite" href="http://pythonware.com/products/pil">PIL</a>.
The simple thing to do would be to import the modules and use the methods I need.
But if the module is missing, an unhelpful ImportError message is all you get.
And if the module is present, but isn't recent enough, then the method call may
fail with a missing name.
</p><p>To solve these problems, I use this helper function instead:
</p><blockquote class="code"><tt><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">importer</span><span class="p_operator">(</span><span class="p_identifier">name</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Import&#160;modules&#160;in&#160;a&#160;helpful&#160;way,&#160;raising&#160;detailed&#160;exceptions</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if&#160;the&#160;module&#160;can't&#160;be&#160;found&#160;or&#160;isn't&#160;the&#160;proper&#160;version.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">if</span><span class="p_default">&#160;</span><span class="p_identifier">name</span><span class="p_default">&#160;</span><span class="p_operator">==</span><span class="p_default">&#160;</span><span class="p_character">'wx'</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">url</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_string">"<a href="http://wxpython.org">http://wxpython.org</a>/"</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">wx</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">except</span><span class="p_default">&#160;</span><span class="p_identifier">ImportError</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">Exception</span><span class="p_operator">(</span><span class="p_string">"Need&#160;wxPython,&#160;from&#160;"</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">url</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">if</span><span class="p_default">&#160;</span><span class="p_word">not</span><span class="p_default">&#160;</span><span class="p_identifier">hasattr</span><span class="p_operator">(</span><span class="p_identifier">wx</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_character">'BitmapFromBuffer'</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">Exception</span><span class="p_operator">(</span><span class="p_string">"Need&#160;wxPython&#160;2.8&#160;or&#160;greater,&#160;from&#160;"</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">url</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">return</span><span class="p_default">&#160;</span><span class="p_identifier">wx</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">elif</span><span class="p_default">&#160;</span><span class="p_identifier">name</span><span class="p_default">&#160;</span><span class="p_operator">==</span><span class="p_default">&#160;</span><span class="p_character">'numpy'</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">url</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_string">"<a href="http://numpy.scipy.org">http://numpy.scipy.org</a>/"</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">numpy</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">except</span><span class="p_default">&#160;</span><span class="p_identifier">ImportError</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">Exception</span><span class="p_operator">(</span><span class="p_string">"Need&#160;numpy,&#160;from&#160;"</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">url</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">return</span><span class="p_default">&#160;</span><span class="p_identifier">numpy</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">elif</span><span class="p_default">&#160;</span><span class="p_identifier">name</span><span class="p_default">&#160;</span><span class="p_operator">==</span><span class="p_default">&#160;</span><span class="p_character">'Image'</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">url</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_string">"<a href="http://pythonware.com/products/pil">http://pythonware.com/products/pil</a>/"</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">Image</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">except</span><span class="p_default">&#160;</span><span class="p_identifier">ImportError</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">Exception</span><span class="p_operator">(</span><span class="p_string">"Need&#160;PIL,&#160;from&#160;"</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">url</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">if</span><span class="p_default">&#160;</span><span class="p_word">not</span><span class="p_default">&#160;</span><span class="p_identifier">hasattr</span><span class="p_operator">(</span><span class="p_identifier">Image</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_character">'fromarray'</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">Exception</span><span class="p_operator">(</span><span class="p_string">"Need&#160;PIL&#160;1.1.6&#160;or&#160;greater,&#160;from&#160;"</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">url</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">return</span><span class="p_default">&#160;</span><span class="p_identifier">Image</span><br/>
</tt></blockquote><p>Then, instead of "import wx", I use:</p><blockquote class="code"><tt><span class="p_identifier">wx</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">importer</span><span class="p_operator">(</span><span class="p_string">"wx"</span><span class="p_operator">)</span><br/>
</tt></blockquote><p>and if anything goes wrong, the exception includes helpful details.</p><p>This technique still suffers from the problem of detecting that the module
is actually missing.  Because of Python's
<a href="http://nedbatchelder.com/blog/200609/impoverished_exceptions.html">impoverished exceptions</a>,
catching ImportError doesn't necessarily mean that the module was missing, although
that's the most likely reason.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/iron_man_videos.html">
        <title>Iron Man videos</title>
        <link>http://nedbatchelder.com/blog/200806/iron_man_videos.html</link>
		
        <dc:date>2008-06-16T20:38:02-04:00</dc:date>
        <description><![CDATA[<p>I saw Iron Man on Sunday, and enjoyed it.  Here are two funny videos related to the
film have been making the rounds:</p><ul>
    <li><a class="offsite" href="http://www.mtv.com/overdrive/?vid=237158">Tropic Thunder Viral Video</a>
    shows Ben Stiller corralling Robert Downey, Jr and Jack Black into making a
    viral video for their upcoming movie. Hilarious.</li>
    
    <li>The Onion story: <a class="offsite" href="http://www.theonion.com/content/video/wildly_popular_iron_man_trailer">Wildly Popular 'Iron Man' Trailer To Be Adapted Into Full-Length Film</a>
    perfectly captures the tone of breathless entertainment reporters trying
    to make the most of a story, while at the same time lampooning the rise of
    trailers over the movies themselves. Genius as always.</li>

    <li>And a funny superhero-movie-related bonus: A
    <a class="offsite" href="http://film.guardian.co.uk/News_Story/Critic_Review/Guardian_review/0,,2285042,00.html">review
    of The Hulk in Hulk-speak</a>. Rarrrr.</li>
</ul>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/biology_factoid_of_the_day.html">
        <title>Biology factoid of the day</title>
        <link>http://nedbatchelder.com/blog/200806/biology_factoid_of_the_day.html</link>
		
        <dc:date>2008-06-16T07:00:58-04:00</dc:date>
        <description><![CDATA[<p>Number of human <a class="offsite" href="http://en.wikipedia.org/wiki/Chromosomes">chromosomes</a>: 46.
</p><p>If anyone has a good <a href="http://nedbatchelder.com/blog/200606/math_factoid_of_the_day.html">math</a>
<a class="offsite" href="http://nedbatchelder.com/blog/200706/math_factoid_of_the_day.html">factoid</a>
for 46, I'd love to hear it...</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/bad_web_typography_full_justify.html">
        <title>Bad web typography: full justify</title>
        <link>http://nedbatchelder.com/blog/200806/bad_web_typography_full_justify.html</link>
		
        <dc:date>2008-06-15T14:03:11-04:00</dc:date>
        <description><![CDATA[<p>Full justification on the web is usually a bad idea.</p><p>Typographers use full justification to get an elegant-looking block of
type.  The straight right edge is a strong visual element on the page, and can
add to the controlled overall look. But typographers care about more than just
the outline of the rectangle. They care about the evenness of the type within
the rectangle, something they call "color". The goal is to get an evenly filled
area, with no large changes in density.</p><p>Because full justification involves stretching word spaces, if a line has to
be stretched too much, the spaces become wide enough to be noticeable white blobs
on the page.  The line of text is then "too loose", and interrupts the flow of reading.</p><p>In traditional typography, hyphenation is used to reduce the need to make loose
lines.  By breaking words into smaller chunks, the lines can be filled more
naturally, and they don't have to be stretched too far.</p><p>But web browsers don't hyphenate. As a result, paragraphs often suffer.
Here are some examples from the <a class="offsite" href="http://openid.net/2008/02/">OpenID news for February 2008</a>:</p><p align="center"><img src="http://nedbatchelder.com/pix/justify1.png" alt="A full-justified paragraph" width="354" height="129"></p><p>(I've blurred it a bit to emphasize the color.)  This paragraph is OK, with just
two problem lines: the fourth ("some of the top ...") and fourth from the bottom ("to support the community ...").
These lines are loose enough that I stumble when reading them, as if they were typed "some .. of .. the .. top .."</p><p>But then we come to the other problem with full justification on the web:</p><p align="center"><img src="http://nedbatchelder.com/pix/justify2.png" alt="A full-justified paragraph with a URL in it" width="355" height="71"></p><p>Occasionally URLs appear in paragraphs, and these are very large "words" that
completely screw up the line before them.  Technical writing is especially prone to
this as other non-word content appears in running text, such as function names.</p><p>I think full justification is one of those technology hold-overs: the new technology
trying to mimic the old.  Books and newspapers use full justification, so we try to
do it on the web also.  But content on the web rarely appears in a constrained rectangle.
Full justification in print is appealing partly because the justified right edge of the
text is a good echo of the right edge of the paper, or of the left edge of the next column
in a newspaper.  In a single column of text in the middle of a browser window,
full justification isn't gaining you much, and brings you pain in the form of loose lines.
</p><p>Except in specialized cases, or where you know very clearly what type of content
will appear, you shouldn't use full justification on the web.  The lack of hyphenation
is a killer.</p><p>As it happens, there are
<a class="offsite" href="http://ejohn.org/blog/injecting-word-breaks-with-javascript/">browser-side hyphenation solutions</a>,
but they also have their drawbacks: code size and execution time.</p><p>BTW: it isn't just the web that suffers from hyphen-less justification.
Amazon's Kindle <a class="offsite" href="http://db.tidbits.com/article/9329">has the same problem</a>,
something I noticed right away when I first tried one out. I'm not sure why they wouldn't
have built hyphenation into a reading device.  And I'm reading a Salman Rushdie
book published by Penguin which uses no hyphenation.  Why would a traditionally-published
book forgo the tried and true technology of good-looking pages?</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/pylint.html">
        <title>Pylint</title>
        <link>http://nedbatchelder.com/blog/200806/pylint.html</link>
		
        <dc:date>2008-06-14T08:51:01-04:00</dc:date>
        <description><![CDATA[<p>Being a developer, I'm a sucker for rules to follow to improve my code, and
for tools that can help me to follow them.  Being a Python developer, I don't
have a static type checking compiler to help me.
<a class="offsite" href="http://www.logilab.org/857">Pylint</a> aims to fill some of those gaps.
</p><p>It examines your Python source code and reports on all sorts of things it doesn't like.
Like most tools of this sort (its name is a reference to the classic <a class="offsite" href="http://en.wikipedia.org/wiki/Lint_programming_tool">lint tool for C</a>),
it can be annoyingly picky.  Since its job is to flag things that might be a problem,
it errs on the alarmist noisy side.
</p><p>Pylint tries to apply light type checking to methods and variables, so it will
complain about constructs simply because they interfere with that goal:</p><blockquote class="code"><tt>W0142: 26:MyClass.my_method: Used * or ** magic</tt></blockquote><p>Excuse me, but ** is not magic, it's a powerful language feature.  Reading pylint's
warnings, you get the feeling that it won't be completely happy until you are coding
within the intersection of Python and Java.</p><p>But pylint's best feature is its configurability.  In the settings file, you
can disable individual messages:</p><blockquote class="code"><tt># Disable the message(s) with the given id(s).<br>disable-msg=C0103,R0903,W0142,C0324,R0201,C0111,W0232,W0702,W0703,W0201<br></tt></blockquote><p>and also configure all sorts of other settings.  This is important because
pylint also natters on about style issues: valid names, line length, number of
statements per method, and so on. Pylint also lets you disable a particular message
in specific files, classes, or methods, which is extremely useful for overriding
warnings about tricky cases, or simple misdiagnoses.</p><p>As with every other lint-like tool I've ever used, the first order of business
is deciding what you really want pylint to tell you about.  Initially,
its reporting will be about things that just don't matter, and you'll disable a
ton of messages.  Then you'll get to the things that you'll agree are minor issues
and you'll want to clean up, like unused imports.</p><p>The next rung of messages are helpful because they get you to think about the
way you've written your code.  For example, this code:</p><blockquote class="code"><tt><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">my_method</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">arg1</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">extras</span><span class="p_operator">=[]):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">//</span><span class="p_default">&#160;</span><span class="p_identifier">blah</span><span class="p_default">&#160;</span><span class="p_identifier">blah</span><span class="p_operator">...</span><br/>
</tt></blockquote><p>will get you this warning:</p><blockquote class="code"><tt>W0102:247:MyClass.my_method: Dangerous default value [] as argument<br></tt></blockquote><p>Pylint warns about this because you could append to extras in the body of the
method, and that would modify the single list object that is used as the default
value for every invocation of the method, something you almost certainly didn't
intend.  Changing the code to this avoids the possibility and the warning:</p><blockquote class="code"><tt><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">my_method</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">arg1</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">extras</span><span class="p_operator">=</span><span class="p_word">None</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">extras</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">extras</span><span class="p_default">&#160;</span><span class="p_word">or</span><span class="p_default">&#160;</span><span class="p_operator">[]</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">//</span><span class="p_default">&#160;</span><span class="p_identifier">blah</span><span class="p_default">&#160;</span><span class="p_identifier">blah</span><span class="p_operator">...</span><br/>
</tt></blockquote><p>Whether you want to adopt this idiom uniformly, or stick with the more common
extras=[] is something you'll have to decide.  Pylint did you the favor of bringing
it to your attention so that you could think through the issue and decide.
In this case, you may be able to simply leave extras as None and use it as is in
the body of the function, but you get the point.
</p><p>Occasionally, you'll get unambiguous value from the pylint output.
I ran pylint on a large actively developed code base, and it reported on an instance
of an undefined variable.  I looked, and sure enough, that code shouldn't work.
Digging further, I looked at who called that code, and once I was done pulling on
all of the threads I discovered, I had a couple hundred lines of code that were
not used any more, and I could <a href="http://nedbatchelder.com//text/deleting-code.html">delete</a> them.
</p><p>I don't know whether I'll stick with pylint.  It's a tricky balance to get it
set properly so that it warns about things I genuinely believe to be issues.
</p><p>The other minor downside to pylint is that you have to install three separate
packages to get it to work.  Logilab would do well to provide a single installer
for pylint and its dependencies.</p><p>BTW: There are other tools for static checking Python code, but I haven't used them
recently: <a class="offsite" href="http://divmod.org/trac/wiki/DivmodPyflakes">PyFlakes</a> and
<a class="offsite" href="http://pychecker.sourceforge.net/">PyChecker</a>.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200806/one_of_the_worst_decisions_in_history.html">
        <title>One of the worst decisions in history</title>
        <link>http://nedbatchelder.com/blog/200806/one_of_the_worst_decisions_in_history.html</link>
		
        <dc:date>2008-06-14T06:28:44-04:00</dc:date>
        <description><![CDATA[<p>Conservatives are wailing and wringing their hands over the Supreme Court's
ruling that Guantanamo detainees are
<a class="offsite" href="http://www.washingtonpost.com/wp-dyn/content/article/2008/06/13/AR2008061303462.html">entitled to challenge their detention</a>.
John McCain hyperbolically called it
<a class="offsite" href="http://elections.foxnews.com/2008/06/13/mccain-guantanamo-ruling-one-of-the-worst-decisions-in-history/">one of the worst decisions in history</a>.
</p><p>I'm not sure why people (even conservatives) think this is so horrible.
If, as John McCain asserts, these are "bad people", then judges will agree that
they can be detained.  It will take time to hear those cases, and the government
will have to defend their detention.  But if these men are as bad as we say,
then judges will agree to hold them.  We are not in danger here of some kind of
Get Out of Jail Free card.  Mention has been made a few times of their food being
an issue in these cases.  Oh, please.  Let's give federal judges some credit here.
</p><p>McCain said it was "terribly unfortunate because we need to go ahead and adjudicate
these cases".  We've been holding some of these people for <em>six years</em>.
As near as I can tell, the only thing keeping us from adjudicating their cases
is the extensive process the Bush Administration has undertaken to invent a
parallel legal system outside the U.S. Constitution.</p><p>Perhaps it is no surprise, but I agree with what Obama said about the decision:
</p><blockquote><div><p>This is an important step toward re-establishing our credibility as a
nation committed to the rule of law and rejecting a false choice between
fighting terrorism and respecting habeas corpus.</p></div></blockquote>
]]></description>
    </item>
    
</rdf:RDF>
