<?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/200810/five_thirty_eight.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200810/aptus_20.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200810/python_registry_grepper.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200810/switching_python_versions_on_windows.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200809/a_server_memory_leak.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200809/cisco_minus_t.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200809/evil_apple.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200809/honda_civic_hybrid.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200809/competition_inside_corporations.html"/>
                
                    <rdf:li resource="http://nedbatchelder.com/blog/200809/selfdiagnosing_software.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/200810/five_thirty_eight.html">
        <title>Five thirty eight</title>
        <link>http://nedbatchelder.com/blog/200810/five_thirty_eight.html</link>
		
        <dc:date>2008-10-06T21:57:00-04:00</dc:date>
        <description><![CDATA[<p>We are in full swing now in the presidential campaign, and we are constantly
bombarded with poll numbers. Funny thing is, most of those polls are just
national polls, a prediction of how the nation-wide popular vote will turn out.
But as the 2000 election underscored, that doesn't matter at all: what matters
is the electoral vote. To predict that, you'd have to track individual
state-by-state polls to see who wins the popular vote in each state, and compute
the electoral vote totals. Sounds like a lot of work, but
<a class="offsite" href="http://fivethirtyeight.com">FiveThirtyEight.com</a>
(Electoral Predictions Done Right) has done all the work already.  They also
run statistical simulations to predict the likelihood of various outcomes (for
example: the chance of McCain losing the popular vote but winning the election
is 1.7%).</p><p>Add extensive tables of data detailing the poll data, the simulations,
their predictions, maps of outcomes, more of the same for congressional races,
and so on, and you have a quantitative political junkie's dream site.</p><p>BTW, as of this moment, they predict an Obama win, with 339 electoral votes to McCain's 199.</p><p>And they aren't the only game in town: there's also <a class="offsite" href="http://www.electoral-vote.com/">Electoral-vote.com</a>
(currently predicting a 329 over 194 win for Obama), and <a class="offsite" href="http://www.electionprojection.com">Election Projection</a>
(364 to 174 for Obama).</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200810/aptus_20.html">
        <title>Aptus 2.0</title>
        <link>http://nedbatchelder.com/blog/200810/aptus_20.html</link>
		
        <dc:date>2008-10-05T20:52:43-04:00</dc:date>
        <description><![CDATA[<p><a href="http://nedbatchelder.com/code/aptus">Aptus 2.0</a>, the latest version of my Mandelbrot
explorer, is now available.  It's got a lot of improvements over the previous
version, including speed improvements, multiple top-level windows, tool windows
for displaying information and Julia set support.
</p><p align="center"><a href="code/aptus"><img src="http://nedbatchelder.com/pix/CocoonLace2_med.png" alt="fractal image from the Mandelbrot set" width="400" height="296"></a></p><p>It's built with <a class="offsite" href="http://wxpython.org">wxPython</a>, so it runs on Windows,
Linux, and Mac.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200810/python_registry_grepper.html">
        <title>Python registry grepper</title>
        <link>http://nedbatchelder.com/blog/200810/python_registry_grepper.html</link>
		
        <dc:date>2008-10-02T22:08:50-04:00</dc:date>
        <description><![CDATA[<p>In writing the <a href="http://nedbatchelder.com/blog/200810/switching_python_versions_on_windows.html">python registry switcher</a>,
I needed to search the registry for references to my old Python version.
Another good use for a Python script:
</p><blockquote class="code"><tt><span class="p_tripledouble">"""&#160;Search&#160;the&#160;Windows&#160;registry.</span><br/>
<span class="p_tripledouble">"""</span><br/>
<br/>
<span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">_winreg</span><span class="p_default">&#160;</span><span class="p_word">as</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><br/>
<span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">itertools</span><br/>
<br/>
<span class="p_identifier">RegRoots</span><span class="p_default">&#160;</span><span class="p_operator">=</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_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">HKEY_CLASSES_ROOT</span><span class="p_operator">:</span><span class="p_default">&#160;&#160;&#160;</span><span class="p_character">'HKEY_CLASSES_ROOT'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">HKEY_CURRENT_USER</span><span class="p_operator">:</span><span class="p_default">&#160;&#160;&#160;</span><span class="p_character">'HKEY_CURRENT_USER'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">HKEY_LOCAL_MACHINE</span><span class="p_operator">:</span><span class="p_default">&#160;&#160;</span><span class="p_character">'HKEY_LOCAL_MACHINE'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">HKEY_USERS</span><span class="p_operator">:</span><span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'HKEY_USERS'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">}</span><br/>
<br/>
<span class="p_word">class</span><span class="p_default">&#160;</span><span class="p_classname">RegKey</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;A&#160;handy&#160;wrapper&#160;around&#160;the&#160;raw&#160;stuff&#160;in&#160;the&#160;_winreg&#160;module.</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">def</span><span class="p_default">&#160;</span><span class="p_defname">__init__</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">rawkey</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">key</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">rawkey</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">root</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">root</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">path</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">__str__</span><span class="p_operator">(</span><span class="p_identifier">self</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_string">"%s\\%s"</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">RegRoots</span><span class="p_operator">.</span><span class="p_identifier">get</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">hex</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">root</span><span class="p_operator">)),</span><span class="p_default">&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">path</span><span class="p_operator">)</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">def</span><span class="p_default">&#160;</span><span class="p_defname">close</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">CloseKey</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">key</span><span class="p_operator">)</span><br/>
<br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">values</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Enumerate&#160;the&#160;values&#160;in&#160;this&#160;key.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">for</span><span class="p_default">&#160;</span><span class="p_identifier">ikey</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">itertools</span><span class="p_operator">.</span><span class="p_identifier">count</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">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">yield</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">EnumValue</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">key</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">ikey</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">except</span><span class="p_default">&#160;</span><span class="p_identifier">EnvironmentError</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">break</span><br/>
<br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">subkey_names</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Enumerate&#160;the&#160;names&#160;of&#160;the&#160;subkeys&#160;in&#160;this&#160;key.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">for</span><span class="p_default">&#160;</span><span class="p_identifier">ikey</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">itertools</span><span class="p_operator">.</span><span class="p_identifier">count</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">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">yield</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">EnumKey</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">key</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">ikey</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">except</span><span class="p_default">&#160;</span><span class="p_identifier">EnvironmentError</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">break</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">subkeys</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Enumerate&#160;the&#160;subkeys&#160;in&#160;this&#160;key.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">for</span><span class="p_default">&#160;</span><span class="p_identifier">subkey_name</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">subkey_names</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">if</span><span class="p_default">&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">path</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">sub</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">path</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_character">'\\'</span><span class="p_default">&#160;</span><span class="p_operator">+</span><span class="p_default">&#160;</span><span class="p_identifier">subkey_name</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#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;&#160;&#160;&#160;&#160;</span><span class="p_identifier">sub</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">subkey_name</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_word">yield</span><span class="p_default">&#160;</span><span class="p_identifier">OpenRegKey</span><span class="p_operator">(</span><span class="p_identifier">self</span><span class="p_operator">.</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">sub</span><span class="p_operator">)</span><br/>
<br/>
<span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">OpenRegKey</span><span class="p_operator">(</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><span class="p_operator">):</span><br/>
<span class="p_default">&#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;</span><span class="p_identifier">rawkey</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">OpenKey</span><span class="p_operator">(</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">except</span><span class="p_default">&#160;</span><span class="p_identifier">Exception</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">e</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_commentline">#print&#160;"Couldn't&#160;open&#160;%r&#160;%r:&#160;%s"&#160;%&#160;(root,&#160;path,&#160;e)</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_word">None</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">RegKey</span><span class="p_operator">(</span><span class="p_identifier">rawkey</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><span class="p_operator">)</span><br/>
<br/>
<span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">grep_key</span><span class="p_operator">(</span><span class="p_identifier">key</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">target</span><span class="p_operator">):</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">name</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">value</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">typ</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">key</span><span class="p_operator">.</span><span class="p_identifier">values</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">isinstance</span><span class="p_operator">(</span><span class="p_identifier">value</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">basestring</span><span class="p_operator">)</span><span class="p_default">&#160;</span><span class="p_word">and</span><span class="p_default">&#160;</span><span class="p_identifier">target</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">value</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">print</span><span class="p_default">&#160;</span><span class="p_string">"%s\\%s&#160;=&#160;%r"</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">key</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">name</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">value</span><span class="p_operator">)</span><br/>
<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">subkey</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">key</span><span class="p_operator">.</span><span class="p_identifier">subkeys</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">subkey</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">continue</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">grep_key</span><span class="p_operator">(</span><span class="p_identifier">subkey</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">target</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">subkey</span><span class="p_operator">.</span><span class="p_identifier">close</span><span class="p_operator">()</span><br/>
<br/>
<span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">grep_registry</span><span class="p_operator">(</span><span class="p_identifier">args</span><span class="p_operator">):</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">root</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">RegRoots</span><span class="p_operator">.</span><span class="p_identifier">keys</span><span class="p_operator">():</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">grep_key</span><span class="p_operator">(</span><span class="p_identifier">OpenRegKey</span><span class="p_operator">(</span><span class="p_identifier">root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_string">""</span><span class="p_operator">),</span><span class="p_default">&#160;</span><span class="p_identifier">args</span><span class="p_operator">[</span><span class="p_number">1</span><span class="p_operator">])</span><br/>
<br/>
<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">'__main__'</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">sys</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">grep_registry</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><br/>
</tt></blockquote><p>Most of this is a pythonic wrapper around the _winreg module, with a few simple
functions at the end to actually search the registry.
</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200810/switching_python_versions_on_windows.html">
        <title>Switching python versions on windows</title>
        <link>http://nedbatchelder.com/blog/200810/switching_python_versions_on_windows.html</link>
		
        <dc:date>2008-10-01T21:01:02-04:00</dc:date>
        <description><![CDATA[<p>I forget what software first set up these associations, but I have .py files
registered with Windows so that they can execute directly.  The registry defines
.py as a Python.File, which has a shell open command of:
</p><blockquote class="code"><tt>"C:\Python24\python.exe" "%1" %*<br></tt></blockquote><p>My PATHEXT environment variable includes .py, so the command prompt will
attempt to execute .py files, using the registry associations to find the
executable.</p><p>But: I wanted to switch from Python 2.4 to Python 2.5.  That meant updating
the registry in a handful of places.  A Python script to the rescue!</p><blockquote class="code"><tt><span class="p_tripledouble">"""&#160;Change&#160;the&#160;.py&#160;file&#160;extension&#160;to&#160;point&#160;to&#160;a&#160;different</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;Python&#160;installation.</span><br/>
<span class="p_tripledouble">"""</span><br/>
<span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">_winreg</span><span class="p_default">&#160;</span><span class="p_word">as</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><br/>
<span class="p_word">import</span><span class="p_default">&#160;</span><span class="p_identifier">sys</span><br/>
<br/>
<span class="p_identifier">pydir</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/>
<br/>
<span class="p_identifier">todo</span><span class="p_default">&#160;</span><span class="p_operator">=</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_character">'Applications\python.exe\shell\open\command'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'"PYDIR\\python.exe"&#160;"%1"&#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_character">'Applications\pythonw.exe\shell\open\command'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'"PYDIR\\pythonw.exe"&#160;"%1"&#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_character">'Python.CompiledFile\DefaultIcon'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'PYDIR\\pyc.ico'</span><span class="p_operator">),</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">(</span><span class="p_character">'Python.CompiledFile\shell\open\command'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'"PYDIR\\python.exe"&#160;"%1"&#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_character">'Python.File\DefaultIcon'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'PYDIR\\py.ico'</span><span class="p_operator">),</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">(</span><span class="p_character">'Python.File\shell\open\command'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'"PYDIR\\python.exe"&#160;"%1"&#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_character">'Python.NoConFile\DefaultIcon'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'PYDIR\\py.ico'</span><span class="p_operator">),</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">(</span><span class="p_character">'Python.NoConFile\shell\open\command'</span><span class="p_operator">,</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_character">'"PYDIR\\pythonw.exe"&#160;"%1"&#160;%*'</span><span class="p_operator">),</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_operator">]</span><br/>
<br/>
<span class="p_identifier">classes_root</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">OpenKey</span><span class="p_operator">(</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">HKEY_CLASSES_ROOT</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_string">""</span><span class="p_operator">)</span><br/>
<span class="p_word">for</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">value</span><span class="p_default">&#160;</span><span class="p_word">in</span><span class="p_default">&#160;</span><span class="p_identifier">todo</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">key</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">OpenKey</span><span class="p_operator">(</span><span class="p_identifier">classes_root</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">path</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_number">0</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">KEY_SET_VALUE</span><span class="p_operator">)</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">SetValue</span><span class="p_operator">(</span><span class="p_identifier">key</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_character">''</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">reg</span><span class="p_operator">.</span><span class="p_identifier">REG_SZ</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">value</span><span class="p_operator">.</span><span class="p_identifier">replace</span><span class="p_operator">(</span><span class="p_character">'PYDIR'</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">pydir</span><span class="p_operator">))</span><br/>
</tt></blockquote><p>Invoke this with your desired Python installation directory, and the registry
is updated to point to it.</p><p>Note that this doesn't affect what the command "python" means, that's determined
by your PATH enviroment variable.  These registry settings change which Python
executable is found when you invoke a .py file as a command.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200809/a_server_memory_leak.html">
        <title>A server memory leak</title>
        <link>http://nedbatchelder.com/blog/200809/a_server_memory_leak.html</link>
		
        <dc:date>2008-09-27T10:30:44-04:00</dc:date>
        <description><![CDATA[<p>We pushed new code to our <a class="offsite" href="http://www.tabblo.com">production
servers</a> last week. There were a lot of changes, including our upgrade to
<a class="offsite" href="http://www.djangoproject.com/weblog/2008/sep/03/1/">Django 1.0</a>. As soon
as the servers restarted, they immediately suffered, with Python processes bloated
to 2Gb or more memory each. Yikes! We reverted to the old code, and began the process of finding the
leak.</p><p>These are details on what we (<a class="offsite" href="http://www.tabblo.com/studio/person/dcs">Dave</a>,
<a class="offsite" href="http://www.tabblo.com/studio/person/PeterAJP/">Peter</a>, and I, mostly them)
did to find and fix the problem.</p><a name="more"></a><p>We used <a class="offsite" href="http://guppy-pe.sourceforge.net/">Guppy</a>, a very capable
Python memory diagnostic tool. It showed that the Python heap was much smaller than
the memory footprint of the server process, so the leak seemed to be in memory
managed by C extensions.</p><p>We identified these C extensions:</p><ul>
<li><a class="offsite" href="http://www.pythonware.com/library/pil/handbook/index.htm">Python Imaging Library</a></li>
<li><a class="offsite" href="http://www.pdflib.com/">PDFlib</a></li>
<li><a class="offsite" href="http://numpy.scipy.org/">numpy</a></li>
</ul><p>We tried to keep these possibilities in mind as we worked through our next steps.
PIL and PDFlib in particular seemed likely given how heavily we use them, and because
they traffic in large data (high-res images).</p><p>We had some unit tests that showed fat memory behavior.  We ran
<a class="offsite" href="http://valgrind.org/">valgrind</a> on them hoping they would demonstrate
a leak that we could fix.  Valgrind is a very heavy-weight tool, requiring
<a class="offsite" href="http://svn.python.org/projects/python/trunk/Misc/README.valgrind">re-compiling
the Python interpreter</a> to get good results, and even so, we were overwhelmed
with data and noise. The tests took long enough to run that other techniques
proved more productive.</p><p>Our staging server had been running the code for over a week, and showed no ill
effects.  We tried to reason out what is the important difference between the staging server and the production
server? We figured the biggest difference is the traffic they each receive. We
tried to load up the staging server with traffic. An aggressive test downloading
many dynamic PDFs quickly ballooned the memory on the staging server, so we suspected PDFlib
as the culprit.</p><p>Closely reading the relevant code, we realized we had a memory leak if an exception occurred:</p><blockquote class="code"><tt><span class="p_identifier">p</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">PDF_new</span><span class="p_operator">()</span><br/>
<span class="p_commentline">#&#160;Lots&#160;of&#160;stuff,&#160;including&#160;an&#160;exception</span><br/>
<span class="p_identifier">PDF_delete</span><span class="p_operator">(</span><span class="p_identifier">p</span><span class="p_operator">)</span><span class="p_default">&#160;&#160;&#160;</span><span class="p_commentline">#&#160;Not&#160;called:&#160;leak!</span><br/>
</tt></blockquote><p>We felt pretty good about finding that, and fixed it up with a lot of unfortunate
try/finally clauses.  We put the code on our staging server, and it behaved much
better.  Lots of PDF downloads would still cause the memory to grow, but when the
requests were done, it would settle back down again. So we liked the theory that
this was the fix.  The only flaw in the theory was it didn't provide a reason why
our old code was good and our new code was bad.  We put the fixed code on
the production server: boom, the app server processes ballooned immediately.
Apparently as good as this exception fix was for our PDFlib code, it wasn't the real problem.</p><p>We tried chopping out functionality to isolate the problem.  Certain subsets of URLs
were removed from the URL map to remove the traffic from the server. We ran the code
for short five-minute bursts to see the behavior under real traffic, and it was no better.
To be sure it wasn't still PDFlib somehow, we tried removing PDFlib by raising an
exception at the one place in our code where PDF contexts are allocated. Memory
still exploded. We tried removing PIL by writing a dummy Image.py that raises
exceptions unconditionally. It didn't help.</p><p>We tried logging requests and memory footprints, but correlations elusive. We
tried changing the process architecture to use only one thread per process, no luck.</p><p>We tried reverting all the Django 1.0 changes, to move back to the Django version
we had been using before.  This changed back the Django code, and the adaptations
we'd made to that code, but (in theory) left in place all of the feature work
and bug fixes we had done.</p><p>We pushed that to the servers, and everything performed beautifully, the
server processes used reasonable amounts of memory, and didn't grow and shrink.
So now we know the leak is either in the Django 1.0 code, or in our botched
adaptation to it, or in some combination of the two. Many people are using
Django 1.0, so it seemed unlikely to be as simple as a Django leak, so we
focused on our Django-intensive code.</p><p>Now that we'd narrowed it down to the Django upgrade, how to find it?  We went
back to the request logs, examining them more closely for any clues. We found
one innocuous-seeming URL that appeared near a number of the memory explosions.</p><p>We took one app server out of rotation, so that it wasn't serving any live requests.
Our nginx load balancer is configured so that a URL parameter can direct a request
to a particular app server.  We used that to hit the isolated app server once with the
suspect request.  Sure enough, the process ballooned to 1Gb, and stayed there.
Then we killed that process, and did it again.  The Python process grew
to 1Gb again.  Yay! We had a single URL that reproduced the problem!</p><p>Now we could review the code that handled that URL, and eyeball everything for
suspects.  We found this:</p><blockquote class="code"><tt><span class="p_default">@</span><span class="p_identifier">memoize</span><span class="p_operator">()</span><br/>
<span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">getRecentStories</span><span class="p_operator">(</span><span class="p_identifier">num</span><span class="p_operator">=</span><span class="p_number">5</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Return&#160;num&#160;most&#160;recent&#160;stories.&#160;Only&#160;public&#160;stories&#160;are&#160;returned.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">stories</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">Story</span><span class="p_operator">.</span><span class="p_identifier">objects</span><span class="p_operator">.</span><span class="p_identifier">published</span><span class="p_operator">(</span><span class="p_identifier">access</span><span class="p_operator">=</span><span class="p_identifier">kAccess</span><span class="p_operator">.</span><span class="p_identifier">public</span><span class="p_operator">).</span><span class="p_default">\</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">exclude</span><span class="p_operator">(</span><span class="p_identifier">type</span><span class="p_operator">=</span><span class="p_identifier">kStoryType</span><span class="p_operator">.</span><span class="p_identifier">personal</span><span class="p_operator">).</span><span class="p_default">\</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">order_by</span><span class="p_operator">(</span><span class="p_character">'-published_date'</span><span class="p_operator">)</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">num</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">stories</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">stories</span><span class="p_operator">[:</span><span class="p_identifier">num</span><span class="p_operator">]</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">stories</span><br/>
</tt></blockquote><p>Our @memoize decorator here caches the result of the function, based on its
argument values.  The result of the function is a QuerySet.  Most of the code that
calls getRecentStories uses a specific num value, so it returns a QuerySet for a small
number of stories, and the caller simply uses that value (for example, in a template
context variable).</p><p>However, in this case, the getRecentStories function is called like this:</p><blockquote class="code"><tt><span class="p_identifier">next_story</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">getRecentStories</span><span class="p_operator">(</span><span class="p_number">0</span><span class="p_operator">).</span><span class="p_identifier">filter</span><span class="p_operator">(</span><span class="p_identifier">published_date__lt</span><span class="p_operator">=</span><span class="p_identifier">the_date</span><span class="p_operator">)[</span><span class="p_number">0</span><span class="p_operator">]</span><br/>
</tt></blockquote><p>The QuerySet is left unlimited until after it is filtered by published_date,
and then the first story is limited off.</p><p>Now we're getting to the heart of one of our mysteries: why was the old
Django code good, and the new Django code bad? The Django ORM changed a great
deal in 1.0, and one of the changes was in what happened when you pickle a QuerySet.</p><p>To cache a QuerySet, you have to pickle it.  Django's QuerySets are lazy: they
only actually query the database when they need to.  For as long as possible, they simply
collect up the parameters that define the query.  In Django 0.96, pickling a
QuerySet didn't force the query to execute, you simply got a pickled version
of the query parameters.  In Django 1.0, pickling the query causes it to query the database,
and the results of the query are part of the pickle.</p><p>Looking at how the getRecentStories function is called, you see that it returns
a QuerySet for all the public stories in the database, which is then narrowed by
the caller first on the published_date, but more importantly, with the [0] slice.</p><p>In Django 0.96, the query wasn't executed against the database until the [0] had
been applied, meaning the SQL query had a "LIMIT 1" clause added.  In Django 1.0,
the query is executed when cached, meaning we request a list of all public stories
from the database, then cache that result list.  Then the caller further filters
the query, and executes it again to get just one result.</p><p>So in Django 0.96, this code resulted in one query to the database, with a LIMIT 1
clause included, but in Django 1.0, this code resulted in two queries.  The first
was executed when the result was cached by the @memoize decorator, the second
when that result was further refined in the caller. The second query is the same
one the old code ran, but the first query is new, and it returns a <em>lot</em>
of results because it has no LIMIT clause at all.</p><p>The fix to reduce the database query was to split getRecentStories 
into two functions: one that caches its result, and is used when the result will not be
filtered further, and another uncached function to use when it will be filtered:</p><blockquote class="code"><tt><span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">getRecentStories</span><span class="p_operator">(</span><span class="p_identifier">num</span><span class="p_operator">=</span><span class="p_number">5</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Return&#160;num&#160;most&#160;recent&#160;stories.&#160;Only&#160;public&#160;stories&#160;are&#160;returned.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Use&#160;this&#160;function&#160;if&#160;you&#160;want&#160;to&#160;filter&#160;the&#160;results&#160;yourself.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Otherwise&#160;use&#160;getCachedRecentStories.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;"""</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">stories</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">Story</span><span class="p_operator">.</span><span class="p_identifier">objects</span><span class="p_operator">.</span><span class="p_identifier">published</span><span class="p_operator">(</span><span class="p_identifier">access</span><span class="p_operator">=</span><span class="p_identifier">kAccess</span><span class="p_operator">.</span><span class="p_identifier">public</span><span class="p_operator">).</span><span class="p_default">\</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">exclude</span><span class="p_operator">(</span><span class="p_identifier">type</span><span class="p_operator">=</span><span class="p_identifier">kStoryType</span><span class="p_operator">.</span><span class="p_identifier">personal</span><span class="p_operator">).</span><span class="p_default">\</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">order_by</span><span class="p_operator">(</span><span class="p_character">'-published_date'</span><span class="p_operator">)</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">num</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_identifier">stories</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">stories</span><span class="p_operator">[:</span><span class="p_identifier">num</span><span class="p_operator">]</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">stories</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><br/>
<span class="p_default">@</span><span class="p_identifier">memoize</span><span class="p_operator">()</span><br/>
<span class="p_word">def</span><span class="p_default">&#160;</span><span class="p_defname">getCachedRecentStories</span><span class="p_operator">(</span><span class="p_identifier">num</span><span class="p_operator">=</span><span class="p_number">5</span><span class="p_operator">):</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_tripledouble">"""&#160;Return&#160;num&#160;most&#160;recent&#160;stories.&#160;Only&#160;public&#160;stories&#160;are&#160;returned.</span><br/>
<span class="p_tripledouble">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;If&#160;you&#160;need&#160;to&#160;filter&#160;the&#160;results&#160;further,&#160;use&#160;getRecentStories.</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">return</span><span class="p_default">&#160;</span><span class="p_identifier">list</span><span class="p_operator">(</span><span class="p_identifier">getRecentStories</span><span class="p_operator">(</span><span class="p_identifier">num</span><span class="p_operator">=</span><span class="p_identifier">num</span><span class="p_operator">))</span><br/>
</tt></blockquote><p>One last point about the Django change: should we have known this from reading
the docs?  Neither the 
<a class="offsite" href="http://code.djangoproject.com/wiki/QuerysetRefactorBranch">QuerySet refactoring notes</a>
nor the <a class="offsite" href="http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges">1.0 backwards incompatible changes</a>
pages mention this change, or address the question of pickled QuerySets directly.
Interestingly, an <a class="offsite" href="http://google.com/search?q=cache:A__91wTdVPYJ:www.djangoproject.com/documentation/db-api/+django+pickle+queryset&amp;hl=en&amp;ct=clnk&amp;cd=1&amp;gl=us">older version of the docs</a>
does describe this exact behavior. This changes was explicitly made and <a class="offsite" href="http://blog.michaeltrier.com/2008/5/6/this-week-in-django-21-2008-05-04">discussed</a>,
but seems to have been misplaced in the 1.0 doc refactoring. Of course, we may not
have realized we had this behavior even if we had read about the change.</p><p>So we've found a big difference in the queries made using the old code and the
new code.  But why the leak?  The theory is that
<a class="offsite" href="http://jjinux.blogspot.com/2008/09/python-debugging-memory-leaks.html">MySQLdb has a leak which has been fixed on its trunk</a>.
Looking at the MySQLdb code, it's pretty clear that they've been developing for
a while since releasing version 1.2.2. Unfortunately, the MySQLdb trunk doesn't
work under Django yet, so we can't verify the theory that MySQLdb is the source
of the leak.</p><p>Ironically, MySQLdb was not on our list of C extensions to look at.  If it had been,
we might have identified it as the culprit with a Google search.  Since the MySQLdb
trunk doesn't work under Django, I guess we would have hacked MySQLdb or Django
to get them to work together. We would have run leak-free, but would be
unknowingly executing the giant database query.</p><p>The last mystery: why didn't the problem appear on our staging server?  Because it was running
with a much smaller database than our production servers, so the "all public stories"
query wasn't a big deal.  We learned a lesson there: sometimes subtle difference
can make all the difference.  We need to keep the staging server's database as
current as we can to make sure it's replicating the production environment as
much as possible.  It's impossible to make them identical (for example, the staging
server doesn't get traffic from search bots), but at times like this, it's important
to understand what all the differences are, and minimize them where you can.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200809/cisco_minus_t.html">
        <title>Cisco minus t</title>
        <link>http://nedbatchelder.com/blog/200809/cisco_minus_t.html</link>
		
        <dc:date>2008-09-26T07:25:25-04:00</dc:date>
        <description><![CDATA[<p>One of those simple typos that turns into an embarassing public mistake:
<a class="offsite" href="http://blog.dotsmart.net/2008/09/25/cisco-home-page-fail/">Cisco home page FAIL</a>,
where (it is theorized) a regex that should have had \t had only t, and as a
result, all lowercase t's were removed from the page, breaking it completely.
</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200809/evil_apple.html">
        <title>Evil apple</title>
        <link>http://nedbatchelder.com/blog/200809/evil_apple.html</link>
		
        <dc:date>2008-09-23T20:14:16-04:00</dc:date>
        <description><![CDATA[<p>I really don't know what Apple is thinking.  First they release a <a class="offsite" href="http://www.apple.com/iphone/">really
cool phone</a>, good.  Then they release an SDK for it, also good.  But developers
aren't allowed to talk to each other about developing for the phone.  That's
bad, doesn't Apple realize how developers learn?  Then Apple sets up a store
and keeps control over what apps can be sold there.  Partly good (no malware can
pollute the ecosystem), but partly bad (no one knows how Apple will decide what
can be sold).
</p><p>Then Apple started to reject apps from the app store, which is bad, because
app developers only find out they've been rejected after they've expended all the
effort to build the app, and it can be hard to predict whether an app will be
rejected or not, making it
<a class="offsite" href="http://daringfireball.net/2008/09/app_store_exclusion">risky to build iPhone apps</a>.</p><p>After this breathtaking descent into cluelessness, Apple has topped itself by
deciding that
<a class="offsite" href="http://www.macrumors.com/2008/09/23/apple-extends-non-disclosure-to-app-store-rejection-letters/">app rejections
are subject to the non-disclosure</a>, making it illegal for developers to
talk about the fact that their app has been rejected!  Is Apple actively trying
to discourage app development?  Is there any other company that could act this
way without raising the ire of the development community?  This is the company
that used <a class="offsite" href="http://images.apple.com/pr/photos/ads/GandhiBillboardLgLo.jpg">Gandhi in an ad</a>?
What exactly is Apple thinking?</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200809/honda_civic_hybrid.html">
        <title>Honda Civic hybrid</title>
        <link>http://nedbatchelder.com/blog/200809/honda_civic_hybrid.html</link>
		
        <dc:date>2008-09-20T20:49:36-04:00</dc:date>
        <description><![CDATA[<p>I've just bought a new car: a <a class="offsite" href="http://automobiles.honda.com/civic-hybrid/">Honda Civic hybrid</a>.
I don't buy cars that often.  The car I just replaced was a 1994 Civic.  To keep
the same pace, I'll add an entry to my calendar for 2022 to buy my next car.
</p><p>I like the Civic for its gas mileage, 45 mpg highway.  The extra expense over
a non-hybrid Civic is actually more than I'll save on gas over the life of the
car, but I like being the change I want to see in the world.</p><p>One thing that surprised me about this car is how familiar it felt after having
driven a 1994 Civic.  Lots of extra bells and whistles that I'd gotten used to
in my wife's larger cars are still absent in this car.</p><p>Features in the hybrid I didn't have in my 1994 Civic (other than the hybrid engine):</p><ul>
<li>A temperature setting in the climate control</li>
<li>Front seat map lights</li>
<li>A chime to alert me that I've left my headlights on</li>
<li>An auxilliary jack for the stereo</li>
<li>Electronic dashboard with thermometer, etc</li>
</ul><p>Things that work in the hybrid that used to work in the 1994 Civic, but no longer do:</p><ul>
<li>Remote entry buttons</li>
<li>Reliable low-speed wipers</li>
<li>Rear left passenger door handle</li>
<li>Exhaust system. The last thing that failed on the 94 was the exhaust. For
its last two days, it sounded like a four-door Harley.</li>
</ul><p>Fancy features the Hybrid doesn't have that my wife's car does:</p><ul>
<li>Motorized seat adjustments with memory</li>
<li>Heated seats</li>
<li>Lighted mirrors in visors</li>
<li>Fold-in side mirrors</li>
<li>Leather seats</li>
<li>Separate temperature settings for driver and passenger</li>
<li>Individual lights for rear passengers</li>
</ul><p>I'm pleased to have a new car that just works, and especially one that does
so well on gas.</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200809/competition_inside_corporations.html">
        <title>Competition inside corporations?</title>
        <link>http://nedbatchelder.com/blog/200809/competition_inside_corporations.html</link>
		
        <dc:date>2008-09-18T21:36:55-04:00</dc:date>
        <description><![CDATA[<p>Having observed Hewlett-Packard from the inside for <a href="http://nedbatchelder.com/blog/200703/hp_acquires_tabblo.html">almost 18 months now</a>,
I'm struck by a paradox: our economy is a chaotic marketplace of capitalist
competition, practiced and championed by corporations, but internally, companies
are run as top-down, centrally-planned dictatorships.  Why is that?  Why isn't a
company simply a microcosm of the larger economy?
</p><p>Take the case of IT services: inside HP, there is a large IT organization,
and they provide services to the rest of the company.  When my group joined HP,
we had no choice about how to get, for example, email service.  The IT group
provided email, and we used it.  When we need to buy a laptop, there is one
group that provides that service.   When we need servers hosted, we have only
one place to turn.
</p><p>I'm sure the reason for this is the efficiency gained by eliminating redundancy.
If there were two groups providing email services, surely one group could do the
job of both, with less total staff, equipment, and so on.</p><p>That's certainly true, but then why don't we apply the same logic to the larger
economy?  After all, HP's email group has a huge overlap with Dell's, IBM's, Sun's,
Microsoft's, and so on.  Couldn't our economy gain by eliminating the overlap?
When these questions are considered at the national level, we tout the increased
efficiency produced by competition.  The economy as a whole gains from the pressure
competition puts on each company.  Without competition, there is no incentive to
improve, no reason to do your best.  In a centrally-planned nationalized economy,
incompetence is not punished, incentives are mis-aligned, and apathy takes over.
There's no reason to improve because your customers have nowhere else to turn,
poor service will not lead to loss of business, there's no price pressure, and
your existence is guaranteed by the state.</p><p>That's logic that every capitalist believes, and we laugh at economies that
have tried central planning and failed.  So why doesn't the same logic hold inside
companies?  Why are monopolies and lack of competition not just accepted, but
enforced?  Don't we believe the same forces will be at work?  Is there any compelling
reason to improve if you have no competition?</p><p>Why couldn't a company have three IT groups (call them Red, Green, and Blue).
Each is separate, and lives or dies based on their ability to attract business
from the rest of the company. When my group needs servers hosted, we shop
around. Maybe Red is the deluxe service, and Blue is economy, and we've heard from
friends that Green has the best service.  For whatever reason, we choose one of
them, and spend our internal dollars with them.  The groups will compete, and
that competition will force them to optimize and find the best solutions for their
customers.  If they don't, they will go out of business.</p><p>I know it seems wasteful to have all that going on inside a company.  There will
be duplication. But remember the capitalist logic: without competition, there's
no reason to do your best.  Just as with the larger economy, the duplication will
be worth it because of the increased efficiency forced by competition.  And without
competition, your only option will be a poor one.</p><p>Of course, not all work inside corporations could be run this way. For
example, legal departments deal with the outside world, and the corporation must
speak with one voice there. But couldn't competition be used in at least some
parts of large companies?</p><p>Where's the flaw in this logic? Why isn't competition inside corporations a
good idea?</p>
]]></description>
    </item>
    
    <item rdf:about="http://nedbatchelder.com/blog/200809/selfdiagnosing_software.html">
        <title>Self-diagnosing software</title>
        <link>http://nedbatchelder.com/blog/200809/selfdiagnosing_software.html</link>
		
        <dc:date>2008-09-17T07:48:59-04:00</dc:date>
        <description><![CDATA[<p>At <a class="offsite" href="http://www.tabblo.com">work</a> we upgraded to the shiny-new
<a class="offsite" href="http://www.djangoproject.com/weblog/2008/sep/03/1/">Django 1.0</a>,
and we had to make a lot of small changes in the process.  Most were what you
would expect: adapting to the 1.0 way from the older 0.96 code we had been using.
</p><p>But some of them were undoing ad-hoc patches to Django that we had accreted
over the two years we'd been banging away at it.  Over the course of a week or
so, we'd found dozens of things broken, pointing to work yet to be done to finish
the 1.0 upgrade, just as you'd expect.  We have a large code base, and
<a class="offsite" href="http://docs.djangoproject.com/en/dev/releases/1.0-porting-guide/">many
things changed between 0.96 and 1.0</a>.</p><p>Yesterday, I couldn't log in on my dev server.  Everyone else had been working
just fine for the last few days, so it seemed mysterious.  I asked our main Django
guy Dave for help, and together we logged some session information, saw that
there was no session being established at all. He realized what the problem was.
"Oh, I changed SESSION_COOKIE_DOMAIN back to a string, we don't use the list any
more." Turns out it was one of our ad-hoc Django changes that we threw
overboard, and my settings file still had the old setting in it.</p><p>This is where the software should have diagnosed itself.  If the settings/main.py
file had these two lines added to it:</p><blockquote class="code"><tt><span class="p_word">if</span><span class="p_default">&#160;</span><span class="p_identifier">isinstance</span><span class="p_operator">(</span><span class="p_identifier">SESSION_COOKIE_DOMAIN</span><span class="p_operator">,</span><span class="p_default">&#160;</span><span class="p_identifier">list</span><span class="p_operator">):</span><br/>
<span class="p_default">&#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">"SESSION_COOKIE_DOMAIN&#160;should&#160;be&#160;just&#160;a&#160;string&#160;now."</span><span class="p_operator">)</span><br/>
</tt></blockquote><p>Then I would have immediately gotten an exception on my server console (and
browser) pointing to precisely what the problem was.  I could have fixed it,
and been running in two minutes, rather than being frustrated for half and hour,
and bother Dave for another ten minutes.</p><p>Our development team is small (five), and all sit next to each other most days
of the week, so the cost of this sort of out of band communication about changes
to infrastructure is small.  Also, I seem to have been the only developer who had
a list in their settings file.  So perhaps the cost here was a total of about an
hour.  Not so much, but adding those two lines in the first place would have cost
about five minutes.  And in addition to the five developers, there are probably
five other "development environments" floating around for other purposes: intern
work, demos, backups, evaluation tarballs sent to other groups, etc, and who
knows if those will have the same problem.
</p><p>And besides the simple time spent, there's the loss of focus, the distraction
of the other developers, the frustration, and so on. Developer attention is a
very valuable resource. A speed bump like this in the road is like a CPU cache
miss: your pipelines are flushed, and you have to re-focus. The time taken
doesn't tell the whole story.</p><p>Yesterday was just one of those days, because later, I was entering a zipcode
into my dev machine, and was consistently told that there were no facilities near
that zipcode, even though I knew there should be.</p><p>Turns out that somehow, my database table of zipcodes was empty.  We still
don't know how that happened, but it would have been great if the software could
have helped diagnose this anomalous condition. I changed this:</p><blockquote class="code"><tt><span class="p_word">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">z</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">ZipCode</span><span class="p_operator">.</span><span class="p_identifier">objects</span><span class="p_operator">.</span><span class="p_identifier">get</span><span class="p_operator">(</span><span class="p_identifier">pk</span><span class="p_operator">=</span><span class="p_identifier">zipcode</span><span class="p_operator">)</span><br/>
<span class="p_word">except</span><span class="p_default">&#160;</span><span class="p_identifier">ZipCode</span><span class="p_operator">.</span><span class="p_identifier">DoesNotExist</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">KeyError</span><br/>
</tt></blockquote><p>to this:</p><blockquote class="code"><tt><span class="p_word">try</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_identifier">z</span><span class="p_default">&#160;</span><span class="p_operator">=</span><span class="p_default">&#160;</span><span class="p_identifier">ZipCode</span><span class="p_operator">.</span><span class="p_identifier">objects</span><span class="p_operator">.</span><span class="p_identifier">get</span><span class="p_operator">(</span><span class="p_identifier">pk</span><span class="p_operator">=</span><span class="p_identifier">zipcode</span><span class="p_operator">)</span><br/>
<span class="p_word">except</span><span class="p_default">&#160;</span><span class="p_identifier">ZipCode</span><span class="p_operator">.</span><span class="p_identifier">DoesNotExist</span><span class="p_operator">:</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">settings</span><span class="p_operator">.</span><span class="p_identifier">DEBUG</span><span class="p_operator">:</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_commentline">#&#160;Sometimes&#160;the&#160;problem&#160;isn't&#160;one&#160;bad&#160;zipcode,&#160;but&#160;that&#160;there</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span><span class="p_commentline">#&#160;are&#160;no&#160;zipcodes&#160;in&#160;the&#160;db&#160;at&#160;all!</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">ZipCode</span><span class="p_operator">.</span><span class="p_identifier">objects</span><span class="p_operator">.</span><span class="p_identifier">all</span><span class="p_operator">().</span><span class="p_identifier">count</span><span class="p_operator">()</span><span class="p_default">&#160;</span><span class="p_operator">==</span><span class="p_default">&#160;</span><span class="p_number">0</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">print</span><span class="p_default">&#160;</span><span class="p_string">"***&#160;You&#160;have&#160;no&#160;zipcodes!&#160;Run&#160;bin/load_zipcodes.py"</span><br/>
<span class="p_default">&#160;&#160;&#160;&#160;</span><span class="p_word">raise</span><span class="p_default">&#160;</span><span class="p_identifier">KeyError</span><br/>
</tt></blockquote><p>It would have been another half-hour saved.  I don't know how the zipcodes
were deleted, so it's hard to guess how often someone will be in this position
again, but I know it is worth it to add these sorts of diagnostics.  I'll take
a guess that the next time the zipcodes are missing will be five minutes before
a critical demo, when everyone is panicky and no one will be able to think through
the possible causes clearly.  An unambiguous diagnostic will be very welcome.
</p><p>Take the time to make your software self-diagnosing.  The more you can automate
about the job of writing software, the better your software will be.</p>
]]></description>
    </item>
    
</rdf:RDF>
