<?xml version='1.0' encoding='utf-8' ?>
<!--  If you are running a bot please visit this policy page outlining rules you must respect. http://www.livejournal.com/bots/  -->
<rss version='2.0' xmlns:lj='http://www.livejournal.org/rss/lj/1.0/'>
<channel>
  <title>Handwriting on the Sky</title>
  <link>http://glyf.livejournal.com/</link>
  <description>Handwriting on the Sky - LiveJournal.com</description>
  <lastBuildDate>Tue, 08 Apr 2008 05:29:59 GMT</lastBuildDate>
  <generator>LiveJournal / LiveJournal.com</generator>
  <lj:journal>glyf</lj:journal>
  <lj:journaltype>personal</lj:journaltype>
  <image>
    <url>http://p-userpic.livejournal.com/7543601/1373923</url>
    <title>Handwriting on the Sky</title>
    <link>http://glyf.livejournal.com/</link>
    <width>90</width>
    <height>90</height>
  </image>

<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/76917.html</guid>
  <pubDate>Tue, 08 Apr 2008 05:29:59 GMT</pubDate>
  <title>New Blog</title>
  <link>http://glyf.livejournal.com/76917.html</link>
  <description>Those of you &lt;a href=&quot;http://blendix.com/users/glyph/&quot;&gt;following me on blendix&lt;/a&gt; already know this, but &lt;a href=&quot;http://glyph.twistedmatrix.com/&quot;&gt;I have a new blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;While I may post something here occasionally from now on, it will be a much more personal flavor; my writing about work, about programming, and about technology will move there.&lt;br /&gt;&lt;br /&gt;Masters of the planets (both &lt;a href=&quot;http://planetpython.org/&quot;&gt;python&lt;/a&gt; and &lt;a href=&quot;http://planet.twistedmatrix.com/&quot;&gt;twisted&lt;/a&gt;) please update your feed sources; you can feel free to drop this livejournal in favor of the new URL.</description>
  <comments>http://glyf.livejournal.com/76917.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/76551.html</guid>
  <pubDate>Fri, 28 Mar 2008 00:05:11 GMT</pubDate>
  <title>Divmod: Reloaded</title>
  <link>http://glyf.livejournal.com/76551.html</link>
  <description>Hot on the heels of the Twisted release, Divmod has a new, and hopefully much more comprehensible, sight design and layout.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://divmod.org&quot;&gt;Check it out over at divmod.org&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I&apos;ve long been ashamed of the default-Trac look and the opaque information layout on Divmod&apos;s site, and I&apos;m really happy to have the way we greet the world be spruced up.&lt;br /&gt;&lt;br /&gt;This is mostly the work of the unstoppable &lt;a href=&quot;http://oubiwann.blogspot.com/&quot;&gt;Duncan McGreggor&lt;/a&gt;; this is just his latest work in improving Divmod&apos;s communication with our community and our customers — and it won&apos;t be his last.&lt;br /&gt;&lt;br /&gt;(As with any new site design, the topic isn&apos;t entirely a joke: your browser&apos;s probably cached some stuff it wasn&apos;t supposed to, so if you&apos;ve been visiting our site a lot, re-load for the full effect...)</description>
  <comments>http://glyf.livejournal.com/76551.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/76523.html</guid>
  <pubDate>Thu, 27 Mar 2008 19:35:13 GMT</pubDate>
  <title>Upgrade now!</title>
  <link>http://glyf.livejournal.com/76523.html</link>
  <description>In case you haven&apos;t heard through &lt;a href=&quot;http://radix.twistedmatrix.com/2008/03/twisted-801-is-out.html&quot;&gt;some other channel&lt;/a&gt; already, &lt;a href=&quot;http://labs.twistedmatrix.com/2008/03/twisted-80-released.html&quot;&gt;Twisted 8 is out&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In addition to numerous fixes and features, this release also includes a new release system for Twisted itself; this (hopefully) means that we won&apos;t have another year-long release drought.&amp;nbsp; We&apos;re planning to do another release in less than 3 months.&lt;br /&gt;&lt;br /&gt;This means that new Twisted features will be available faster, but it also means that if you&apos;re writing some software that uses Twisted, &lt;em&gt;upgrade now!&lt;/em&gt;&amp;nbsp; We try very hard to make sure that each new release is &lt;a href=&quot;http://twistedmatrix.com/trac/wiki/CompatibilityPolicy&quot;&gt;mostly compatible with the one that comes before it&lt;/a&gt;, so that your upgrade should be painless.&amp;nbsp; Especially if you have good unit tests.&lt;br /&gt;&lt;br /&gt;However, this compatibility doesn&apos;t extend infinitely.&amp;nbsp; There are at least a few &lt;a href=&quot;http://mumak.net/&quot;&gt;twisted developer&lt;/a&gt;s who would really like to drop some of our years of &lt;a href=&quot;http://twistedmatrix.com/trac/browser/trunk/twisted/trial/util.py?rev=23101#L170&quot;&gt;accumulated cruft&lt;/a&gt; and break compatibility with older versions.&lt;br /&gt;&lt;br /&gt;If you upgrade now, your migration process will be gradually fixing a few deprecation warnings.&amp;nbsp; If you wait for 3 or 4 more minor releases, upgrading all at once will mean that anything which has changed will start off broken, and your tests might not even run until you&apos;ve fixed a bunch of things.&lt;br /&gt;&lt;br /&gt;Of course, by &quot;will&quot;, I mean &quot;should&quot; - we&apos;re not perfect, but we&apos;ll fix upgrade issues in micro releases if you find them and &lt;a href=&quot;http://twistedmatrix.com/trac/newticket&quot;&gt;report them&lt;/a&gt;.</description>
  <comments>http://glyf.livejournal.com/76523.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/76076.html</guid>
  <pubDate>Thu, 20 Mar 2008 02:34:07 GMT</pubDate>
  <title>Open Source 3D Massively Multiplayer Game Infrastructure using Twisted</title>
  <link>http://glyf.livejournal.com/76076.html</link>
  <description>&lt;span class=&apos;ljuser&apos; lj:user=&apos;sirgolan&apos; style=&apos;white-space: nowrap;&apos;&gt;&lt;a href=&apos;http://sirgolan.livejournal.com/profile&apos;&gt;&lt;img src=&apos;http://p-stat.livejournal.com/img/userinfo.gif&apos; alt=&apos;[info]&apos; width=&apos;17&apos; height=&apos;17&apos; style=&apos;vertical-align: bottom; border: 0; padding-right: 1px;&apos; /&gt;&lt;/a&gt;&lt;a href=&apos;http://sirgolan.livejournal.com/&apos;&gt;&lt;b&gt;sirgolan&lt;/b&gt;&lt;/a&gt;&lt;/span&gt; wasn&apos;t at PyCon, but he totally should have been.&lt;br /&gt;&lt;br /&gt;I (and others) have been working on him for &lt;em&gt;years&lt;/em&gt; to release this stuff as open source, and he&apos;s finally done it!&lt;br /&gt;&lt;br /&gt;Go check it out the &lt;a href=&quot;http://trac.mv3d.com/trac&quot;&gt;Multiverse 3D trac site&lt;/a&gt;, and get the code!&lt;br /&gt;&lt;br /&gt;Do it!&lt;br /&gt;&lt;br /&gt;Do it &lt;em&gt;now!&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;small&gt;&lt;small&gt;(and then start submitting patches to get its persistence layer ported back over to Axiom because I told Mike not to block the release on that but it should &lt;em&gt;totally&lt;/em&gt; be using Axiom and he only switched away from it because he didn&apos;t understand quite how it worked...)&lt;/small&gt;&lt;/small&gt;</description>
  <comments>http://glyf.livejournal.com/76076.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/75979.html</guid>
  <pubDate>Wed, 19 Mar 2008 07:22:37 GMT</pubDate>
  <title>Against the Alexandria Library Migration Strategy</title>
  <link>http://glyf.livejournal.com/75979.html</link>
  <description>Some library maintainers, when faced with the impending incompatible changes in Py3K, decide that it&apos;s time to burn their library down and start over with a new, incompatible version.&amp;nbsp; &quot;Python is changing&quot;, they say, &quot;so people are going to have to do a bunch of maintenance anyway.&amp;nbsp; What a great opportunity to force them to do all that maintenance that they should be doing for our library too!&quot;&lt;br /&gt;&lt;br /&gt;I&apos;m saying it&apos;s wrong.&amp;nbsp; But don&apos;t take my word for it: &lt;a href=&quot;http://www.artima.com/weblogs/viewpost.jsp?thread=227041&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;Guido&lt;/span&gt; says it&apos;s wrong&lt;/a&gt;.&amp;nbsp; Before it became cool to do it, Martijn Faassen was &lt;a href=&quot;http://faassen.n--tree.net/blog/view/weblog/2008/03/05/0&quot;&gt;saying it was wrong&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Guido didn&apos;t just blog that it was wrong, though.&amp;nbsp; He was so concerned that this message get out publicly that he &lt;a href=&quot;http://mail.python.org/pipermail/python-dev/2008-March/077720.html&quot;&gt;repeated himself in a mailing list message to python-dev&lt;/a&gt; to make sure that people who don&apos;t read blogs would get the message.&lt;br /&gt;&lt;br /&gt;This might strike some library maintainers as unfair.&amp;nbsp; If Py3K is just breaking compatibility, why can&apos;t you?&lt;br /&gt;&lt;br /&gt;First of all, even if Py3K were really &quot;just breaking compatibility&quot;, there is still the issue of careful timing.&amp;nbsp; You should read Guido&apos;s post and understand Ima Lumberjack&apos;s plight; he explains it exactly as I would.&amp;nbsp; When your users are doing maintenance to upgrade something, they only want to upgrade&amp;nbsp;  &lt;span style=&quot;font-style: italic;&quot;&gt;one &lt;/span&gt;thing, so they know what&apos;s going wrong when they encounter problems.&amp;nbsp; And if you&apos;re making incompatible changes, they &lt;span style=&quot;font-style: italic;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;will&lt;/span&gt;&lt;/span&gt; encounter problems, regardless of how cool and well-documented your new API is.&lt;br /&gt;&lt;br /&gt;But, if you look closely, you will find that there&apos;s another reason that your library doesn&apos;t play by the same rules that Py3K does.&amp;nbsp; It&apos;s because Py3K is actually doing a lot more than just breaking compatibility.&lt;br /&gt;&lt;br /&gt;I&apos;ve been a critic of this effort in the past (and I still occasionally grumble about a thing or two) but the bottom line is that the core Python team is not just willy-nilly breaking stuff.&amp;nbsp; Let me enumerate the huge amount of work they&apos;re doing to make sure that people can have a reasonable migration experience to Python 3:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The Python core team have written and are maintaining a source-to-source translation tool to assist in the transition.&amp;nbsp; Does your compatibility-breaking project have source-to-source translation, or in fact any tool support for migrating between different versions of the library?&lt;/li&gt;&lt;li&gt;The Python core team are developing a compatible backport of 99% of their features: Python 2.6 is effectively &quot;python 3 lite&quot;; you don&apos;t need to upgrade all the way to the incompatible version to get a lot of the new features.&amp;nbsp; Does your compatibility-breaking project include a (at least mostly) compatible backport of all of your new features to an actively developed, &quot;older&quot; version?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The Python core team is providing long-term support for the previous version so that people can migrate at their own pace and not be left out in the cold.&amp;nbsp; Is your compatibility-breaking project planning to provide a decade worth of bugfixes, security patches, and feature backports to your older versions?&lt;/li&gt;&lt;li&gt;The Python core team is providing comprehensive deprecation warnings explaining each new feature, and how you get there from the old feature.&amp;nbsp; Is your project going to provide documentation like that?&lt;/li&gt;&lt;/ol&gt;It&apos;s also worth noting that Python&apos;s dependencies are not going through any kind of compatibility earthquake while they&apos;re doing this, so they are not in the same position that you (as a Python application author) are.&lt;br /&gt;&lt;br /&gt;So, if you answered &quot;yes&quot; to all four of those questions — you still don&apos;t have the same excuse that Python does, because their dependencies are not changing incompatibly.&amp;nbsp; So don&apos;t do it.&amp;nbsp; But if you were thinking about breaking compatibility at the same time as a dependency, then you probably didn&apos;t.&lt;br /&gt;&lt;br /&gt;Of course, you can drop deprecated stuff and break compatibility if your user community will tolerate that.&amp;nbsp; Just don&apos;t do it &lt;span style=&quot;font-style: italic;&quot;&gt;in the same version&lt;/span&gt; where you decide to support Py3K.&amp;nbsp; Users should have the ability to get a compatible version that will work in 2.x and 3.x so that when they translate their own source code, they don&apos;t have to learn new methods right at that moment.</description>
  <comments>http://glyf.livejournal.com/75979.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/75748.html</guid>
  <pubDate>Wed, 19 Mar 2008 01:47:06 GMT</pubDate>
  <title>Back from PyCon</title>
  <link>http://glyf.livejournal.com/75748.html</link>
  <description>Summary: PyCon 2008 was a great time.&amp;nbsp; I didn&apos;t go to a single talk, except a few of the keynotes; I spent pretty much my entire time cross-pollinating with other projects and plugging the until-recently-secret Twisted Software Foundation - our nickname for the Twisted project&apos;s membership of the Software Freedom Conservancy (TSF/SFC).&lt;br /&gt;&lt;br /&gt;I briefly addressed the audience (of &lt;a href=&quot;http://holdenweb.blogspot.com/2008/03/pycon-plus-change.html&quot;&gt;over one thousand&lt;/a&gt; python users) to kick off the TSF announcement, mostly to introduce Duncan.&amp;nbsp; However, someone managed to &lt;a href=&quot;http://flickr.com/photos/mikepirnat/2343274788/sizes/l/&quot;&gt;snap a picture of me&lt;/a&gt; that I think captured the feeling of awe that we&apos;ve come so far in such a relatively short time.&lt;br /&gt;&lt;br /&gt;I spoke to about 400 people at the conference, and I have a &lt;em&gt;lot&lt;/em&gt; to follow up on.&amp;nbsp; I also have a day job, 阿as those of you that I spoke to about Mantissa rather than Twisted know ;-).&lt;br /&gt;&lt;br /&gt;If I talked to you about something at the conference, &lt;em&gt;please&lt;/em&gt; don&apos;t hesitate to send me email reminding me about it.&amp;nbsp; Ideally, send me email reminding me in one to three weeks.&amp;nbsp; I have almost 1000 unread emails right now and while I try to be rigorous about using some unique features of Divmod&apos;s mail system to make sure I reply to each one, there is a certain volume of communication which no tool can help me cope with.&lt;br /&gt;&lt;br /&gt;My first priority is blogging about interesting aspects of the conference in the next few days, before I&apos;ve forgotten.</description>
  <comments>http://glyf.livejournal.com/75748.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/75509.html</guid>
  <pubDate>Thu, 06 Mar 2008 11:19:27 GMT</pubDate>
  <title>That Ain&apos;t Workin&apos;</title>
  <link>http://glyf.livejournal.com/75509.html</link>
  <description>I&apos;m a big fan of Nine Inch Nails.&amp;nbsp; Not quite to the degree of buying every Halo, but I have a number of his albums.&amp;nbsp; So of course I&apos;ve been intrigued by his latest offering.&amp;nbsp; Today, while looking at &lt;a href=&quot;http://ghosts.nin.com/main/order_options&quot;&gt;Nine Inch Nails &quot;Ghosts I-IV&apos; website&lt;/a&gt;, I noticed an interesting bit of information:&lt;br /&gt;  &lt;blockquote&gt;&lt;b style=&quot;color: rgb(255, 0, 0);&quot;&gt;We have SOLD OUT of the 2500 Limited Edition Packages.&lt;/b&gt;&lt;br /&gt;&lt;/blockquote&gt;My great-uncle is fond of a saying.&amp;nbsp; &quot;It is better to be rich and healthy than to be poor and sick.&quot;&amp;nbsp; Seeing this, I was reminded of it.&amp;nbsp; It&apos;s not quite as catchy, but it&apos;s better to be customer-friendly and a huge success than reviled as corrupt and a failure.&lt;br /&gt;&lt;br /&gt;The music and movie industries have been telling us for the last few years that digital restrictions are required in order to save their businesses from destruction.&amp;nbsp; Well, Mr. Reznor has proven them wrong quite dramatically.&amp;nbsp; Before I continue, let me address an obvious objection up front - I realize he&apos;s a superstar.&amp;nbsp; However, the spokespeople for the RIAA that support their claims are also superstars: Lars Ulrich is hardly a starving artist laboring in obscurity.&amp;nbsp;&amp;nbsp; I&apos;m not saying everyone can do what he did, only that the people who are already rich in the music industry can continue to be rich without the bullshit that they claim is critical.&lt;br /&gt;&lt;br /&gt;The &quot;Limited Edition&quot;, for those of you not up on the latest NIN happenings, is a &lt;strong&gt;&lt;em&gt;three hundred dollar&lt;/em&gt;&lt;/strong&gt; version of the album, containing a bunch of extras and a signature from Mr. Reznor himself.&amp;nbsp; The full album, in lossless, non-restricted format, costs &lt;strong&gt;&lt;em&gt;five dollars&lt;/em&gt;&lt;/strong&gt;.&amp;nbsp; There were 2500 copies of the limited edition.&lt;br /&gt;&lt;br /&gt;Let me emphasize for those of you who might not be quite as up on the terminology that &quot;lossless&quot; formats (which NIN is selling for $5 here) are the highest quality format that it is possible to distribute over the internet.&amp;nbsp; Other music producers, out of fear for eating their CD revenues, have mostly refused to provide digital copies of their music of this quality.&lt;br /&gt;&lt;br /&gt;Also, to compare pricing: Apple typically charges 99¢ for a DRM-free song: it&apos;s not lossless, but it&apos;s 256kbps, which is fairly high quality.&amp;nbsp; (I would not believe it if someone told me they could hear the difference, but there is a marginal difference in the perception of value here.)&amp;nbsp; There are 36 songs on &quot;Ghosts&quot;.&amp;nbsp; $5 is roughly 14% of $35.64.&lt;br /&gt;&lt;br /&gt;So now that I&apos;ve established that NIN is selling higher quality goods, in a customer-friendly way, for a fraction of the price of the competition, let&apos;s do some math:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;march 6, today,&lt;br /&gt;&lt;/li&gt;&lt;li&gt;minus the march 4th (the date that the &quot;ghosts&quot; website became fully operational (according to wikipedia),&lt;br /&gt;&lt;/li&gt;&lt;li&gt;is two days, times&lt;br /&gt;&lt;/li&gt;&lt;li&gt;2500 copies&lt;/li&gt;&lt;li&gt;times 300 dollars&lt;/li&gt;&lt;li&gt;equals &lt;b&gt;&lt;i&gt;SEVEN HUNDRED AND FIFTY THOUSAND DOLLARS IN TWO DAYS.&lt;br /&gt;&lt;/i&gt;&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;Trent has now proven that if you are a superstar, you can make three quarters of a million dollars in two days, &lt;em&gt;on a ridiculously expensive premium edition alone&lt;/em&gt;.&amp;nbsp; This is to say nothing of the people who bought, and continue to buy, the $75 version, the $10 version, or the $5 version.&amp;nbsp; This says nothing of the people who are buying it through Amazon.&lt;br /&gt;&lt;br /&gt;Coincidentally, it also proves that you don&apos;t need any RIAA thugs to help you do this, or &quot;market&quot; your work, assuming people already know who you are.&amp;nbsp; You just need a web server, and a swimming pool big enough to put a million dollars.</description>
  <comments>http://glyf.livejournal.com/75509.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/75142.html</guid>
  <pubDate>Thu, 07 Feb 2008 10:17:55 GMT</pubDate>
  <title>Highlighting buried treasure in Twisted</title>
  <link>http://glyf.livejournal.com/75142.html</link>
  <description>I&apos;ve previously &lt;a href=&quot;http://glyf.livejournal.com/2007/08/23/&quot;&gt;blogged about twisted.python.modules&lt;/a&gt;, but it assumes you know about another API inside Twisted, &lt;a href=&quot;http://twistedmatrix.com/documents/current/api/twisted.python.filepath.FilePath.html&quot;&gt;twisted.python.filepath&lt;/a&gt;.&amp;nbsp; Unfortunately this module is rather under-documented and under-publicized, despite being extremely useful.&amp;nbsp; Unlike a lot of Twisted, much of the code in twisted.python can be extracted and used by itself, regardless of whether the program in question is networked or even event-driven.&amp;nbsp; This is especially true of FilePath, which is completely blocking, although sometimes I wish there were at least a version of it that wasn&apos;t.&lt;br /&gt;&lt;br /&gt;A common sort of script that deals with a filesystem is to open each file in a directory hierarchy with a given path and do something to its contents.&amp;nbsp; For example, let&apos;s write a program that prints out a list of all Python modules (with a .py extension) in a tree which contain shebang lines.&lt;br /&gt;&lt;br /&gt;Here&apos;s the script using good old os.path:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;import sys
import os

def os_shebangs(pathname):
    for dirpath, dirnames, filenames in os.walk(pathname):
        for filename in filenames:
            fullpath = os.path.join(dirpath, filename)
            if (fullpath.endswith(&quot;.py&quot;) and
                file(fullpath, &quot;rb&quot;).readline().startswith(&quot;#!&quot;)):
                yield fullpath

def os_show_shebangs(pathname):
    for path in os_shebangs(pathname):
        sys.stdout.write(&quot;%s: %s\n&quot; % (
                path,
                file(path, &quot;rb&quot;).readline()[2:].strip()))

if __name__ == &apos;__main__&apos;:
    os_show_shebangs(sys.argv[1])
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Pretty normal looking python code; not too much wrong with it.&amp;nbsp; At 20 lines and 596 characters long, it&apos;s not too complex.&lt;br /&gt;&lt;br /&gt;Now let&apos;s have a look at a similarly idiomatic version using FilePath:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;import sys
from twisted.python.filepath import FilePath

def shebangs(path):
    for p in path.walk():
        if (p.basename().endswith(&quot;.py&quot;) and
            p.open().readline().startswith(&quot;#!&quot;)):
            yield p

def showShebangs(pathobj):
    for path in shebangs(pathobj):
        sys.stdout.write(&quot;%s: %s\n&quot; % (
                path.path,
                path.open().readline()[2:].strip()))

if __name__ == &apos;__main__&apos;:
    showShebangs(FilePath(sys.argv[1]))
&lt;/pre&gt;&lt;/blockquote&gt;At 18 lines and 471 characters, it&apos;s almost exactly 20% smaller than the version that uses os.path.&amp;nbsp; However, a small space savings is hardly the most interesting property of this code.&amp;nbsp; The advantages over the version that uses os.path:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It&apos;s easier to test.&amp;nbsp; You can use a fake FilePath object rather than needing to replace the whole &quot;os&quot; module and the &quot;file&quot; builtin.&lt;/li&gt;&lt;li&gt;It&apos;s easier to read.&amp;nbsp; You need fewer names; rather than os, os.path, and builtins, the code talks mainly to one object.&lt;/li&gt;&lt;li&gt;It&apos;s easier to write.&amp;nbsp; How many of you honestly remembered that &quot;dirpath, dirnames, filenames&quot; is the order of the tuples yielded from os.walk?&lt;/li&gt;&lt;li&gt;It&apos;s easier to secure.&amp;nbsp; If you wanted to allow untrusted users to supply input to the os.path version, you need to be very, very careful.&amp;nbsp; What about &quot;/&quot;?&amp;nbsp; What about &quot;..&quot;?&amp;nbsp; With FilePath, you simply supply the input to the &apos;child&apos; method, and...&lt;blockquote&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; from twisted.python.filepath import FilePath
&amp;gt;&amp;gt;&amp;gt; fp = FilePath(&quot;.&quot;)
&amp;gt;&amp;gt;&amp;gt; x = fp.child(&quot;okay&quot;)
&amp;gt;&amp;gt;&amp;gt; y = fp.child(&quot;..&quot;)
Traceback (most recent call last):
  File &quot;&amp;lt;stdin&amp;gt;&quot;, line 1, in &amp;lt;module&amp;gt;
  File &quot;twisted/python/filepath.py&quot;, line 308, in child
    raise InsecurePath(&quot;%r is not a child of %s&quot; % (newpath, self.path))
twisted.python.filepath.InsecurePath: &apos;/home&apos; is not a child of /home/glyph
&amp;gt;&amp;gt;&amp;gt; z = fp.child(&quot;hello/world&quot;)
Traceback (most recent call last):
  File &quot;&amp;lt;stdin&amp;gt;&quot;, line 1, in &amp;lt;module&amp;gt;
  File &quot;twisted/python/filepath.py&quot;, line 305, in child
    raise InsecurePath(&quot;%r contains one or more directory separators&quot; % (path,))
twisted.python.filepath.InsecurePath: &apos;hello/world&apos; contains one or more directory separators&lt;/pre&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;It&apos;s easier to extend.&amp;nbsp; As of &lt;a href=&quot;http://twistedmatrix.com/trac/changeset/22464&quot;&gt;revision 22464&lt;/a&gt; of Twisted (i.e. the next release)  you can replace twisted.python.filepath.FilePath with twisted.python.zippath.ZipArchive, and this exact same code can operate on zip files.&lt;/li&gt;&lt;/ul&gt;Not only does FilePath provide these benefits, it has very few dependencies.&amp;nbsp; Even if you don&apos;t like Twisted much, you can use twisted.python.filepath by copying only 3 modules into your project (twisted.python.filepath, twisted.python.win32, and twisted.python.runtime) and twiddling the appropriate imports to be relative.&amp;nbsp; Since FilePath is only one import for your code, and mostly consists of method calls, it will easily work with Twisted&apos;s version or your own.&amp;nbsp; So, share and enjoy!</description>
  <comments>http://glyf.livejournal.com/75142.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/74877.html</guid>
  <pubDate>Thu, 31 Jan 2008 14:31:34 GMT</pubDate>
  <title>Do Not Want</title>
  <link>http://glyf.livejournal.com/74877.html</link>
  <description>I do not want a book called &quot;The Ghost Brigades&quot;, but someone thought I did, for a minute.&amp;nbsp; Those of you following this &lt;a href=&quot;http://blendix.com/users/glyph/&quot;&gt;via my Blendix activity page&lt;/a&gt; will know what I mean.&lt;br /&gt;&lt;br /&gt;One of the interesting things about working on Blendix is that we get to see all the ways in which other services export bad data.&amp;nbsp; Amazon is particularly weird about it, though.&amp;nbsp; As we were testing our code we saw a variety of bits of bad data intermittently published, some of which were fusions of the names of random products, some of which were actual products that had nothing to do with the user in question.&amp;nbsp; I&apos;m a little sad that my first bogus entry was a real product, though.&amp;nbsp; Some of the incorrect entries we saw during testing were pretty hilarious.&lt;br /&gt;&lt;br /&gt;Has anyone else out there used to using Amazon&apos;s various APIs to pull wishlist data and seen similar results?&amp;nbsp; Is there any way to work around it, or to recognize the bogus data?&amp;nbsp; Advice is welcome.</description>
  <comments>http://glyf.livejournal.com/74877.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/74513.html</guid>
  <pubDate>Fri, 18 Jan 2008 23:25:43 GMT</pubDate>
  <title>It Will Blend</title>
  <link>http://glyf.livejournal.com/74513.html</link>
  <description>So, as you &lt;a href=&quot;http://oubiwann.blogspot.com/2008/01/divmod-new-product-blendix.html&quot;&gt;may already have heard&lt;/a&gt;, &lt;a href=&quot;http://blendix.com/&quot;&gt;we did a thing&lt;/a&gt;.&amp;nbsp; While I&apos;ve been able to tell a few people, I&apos;ve been waiting to talk about it publicly for a while.&lt;br /&gt;&lt;br /&gt;We&apos;ve gradually begun to launch &lt;a href=&quot;http://blendix.com/&quot;&gt;Blendix&lt;/a&gt;.&amp;nbsp; Blendix aims to be the one site that you need to visit every day to tell you what&apos;s happening in your world.&amp;nbsp; Right now, it&apos;s an aggregator for various kinds of data around the web.&amp;nbsp; You can pull in the usual suspects (RSS, ATOM), but it&apos;s a bit more than just an RSS aggregator: today, it understands a few more specific things (last.fm tracks, amazon wishlists, yahoo weather, flickr photos), and lots more are planned.&amp;nbsp; Although I&apos;m practically bursting with awesome ideas for its future development, I will try to refrain from commenting too much on those plans.&amp;nbsp; As I&apos;ve said before, software is like frisbee — predictions more specific than &quot;hey, watch this!&quot; can be dangerous :-).&amp;nbsp; However, since I know it&apos;s the first thing you&apos;ll all suggest, I can say that yes, there will be richer integration with social networks.&lt;br /&gt;&lt;br /&gt;In brief, it works like this.&amp;nbsp; You log in, and you create some people.&amp;nbsp; Some feeds may be automatically discovered based on their email addresses, and you can add your own.&amp;nbsp; Maybe you subscribe to some people: if you&apos;re looking for one to subscribe to, may I suggest &quot;&lt;a href=&quot;http://blendix.com/users/glyph/&quot;&gt;Glyph Lefkowitz&lt;/a&gt;&quot;.&amp;nbsp; I hear he&apos;s pretty interesting.&amp;nbsp; Finally, you visit your &quot;dashboard&quot; page, which — thanks to the magic of &lt;a href=&quot;http://divmod.org/trac/wiki/DivmodNevow/Athena&quot;&gt;Athena&lt;/a&gt; — will update whenever blendix detects that one of the people you maintain or are subscribed to publishes some new data.&amp;nbsp; You can expect to see more of that magic as it develops.&lt;br /&gt;&lt;br /&gt;A word of warning, though: &lt;a href=&quot;http://divmod.org/trac/ticket/2478&quot;&gt;it doesn&apos;t work with Safari&lt;/a&gt; (or IE, but I don&apos;t imagine that a lot of you are using IE).&amp;nbsp; We&apos;re working on it, but for the time being, Firefox is strongly recommended.&amp;nbsp; (Firefox for the mac works fine.)&amp;nbsp; &lt;a href=&quot;http://divmod.org/trac/ticket/2478&quot;&gt;Most of the work involved in supporting Safari is in Nevow&lt;/a&gt;, which is all open source, so if you are familiar with these sorts of problems, please submit patches!&lt;br /&gt;&lt;br /&gt;This is our first live, fully public deployment of a &lt;a href=&quot;http://divmod.org/trac/wiki/DivmodMantissa&quot;&gt;Mantissa&lt;/a&gt; server, and I&apos;m really glad to have it out there.&amp;nbsp; We are, of course, working through the usual kinks of getting our first batch of users (&quot;beta&quot; isn&apos;t a web 2.0 buzzword for nothing), but I&apos;m fairly pleased with it so far.&lt;br /&gt;&lt;br /&gt;Blendix itself isn&apos;t open source — yet.&amp;nbsp; We&apos;ve mainly been keeping the product as a whole behind closed doors as a matter of expediency.&amp;nbsp; We didn&apos;t want to support an API that was heavily in development.&amp;nbsp; However, one of my goals as we get closer to a bigger launch is to get enough of the code out there for you folks in the community to write extensions and improvements for it.&amp;nbsp; There&apos;s already enough for some things (like supporting Safari!), even at the application level.&amp;nbsp; For example, a big chunk of Blendix is the &quot;&lt;a href=&quot;http://divmod.org/trac/browser/trunk/Mantissa/xmantissa/people.py?rev=14772#L431&quot;&gt;Person&lt;/a&gt;&quot; object, which is available in the public Mantissa code, along with UI for editing, browsing, and viewing.&lt;br /&gt;&lt;br /&gt;I&apos;m really glad to have something &quot;out there&quot; to share with you all, and I&apos;d like to encourage you to share back.&amp;nbsp; Please check out Blendix, and make liberal use of the information you find under the &quot;Contact&quot; link at the bottom of every page.&amp;nbsp; Let me know what you think, especially if you&apos;re a programmer and you&apos;ve got some ideas for hacking on the code. This will be especially useful as get into the initial phase of pushing the core out to the community.&amp;nbsp; Also, we &lt;em&gt;really&lt;/em&gt; want to make sure the experience is as bug-free as possible, so let us know about any problems you have.</description>
  <comments>http://glyf.livejournal.com/74513.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/74356.html</guid>
  <pubDate>Wed, 02 Jan 2008 06:43:31 GMT</pubDate>
  <title>Export for Python</title>
  <link>http://glyf.livejournal.com/74356.html</link>
  <description>I&apos;ve started playing around with minor projects in my &lt;a href=&quot;https://code.launchpad.net/%7Eglyph&quot;&gt;personal launchpad space&lt;/a&gt;, partially to try out bzr.&lt;br /&gt;&lt;br /&gt;Most recently I wrote &lt;a href=&quot;http://codebrowse.launchpad.net/%7Eglyph/+junk/pyexport/files&quot;&gt;a hack, temporarily named &quot;pyexport&quot;, which allows you to control the names which your library module namespaces export&lt;/a&gt; to application code.&lt;br /&gt;&lt;br /&gt;So far, I&apos;ve implemented a few features.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;export.alias()&lt;/code&gt;, which registers an alias for a method in another module that will not be imported until that module is imported,&lt;/li&gt;&lt;li&gt;&lt;code&gt;export.explicitly()&lt;/code&gt;, a convenience function which makes cooperating with &lt;code&gt;__all__&lt;/code&gt; easy&lt;/li&gt;&lt;li&gt;&lt;code&gt;export.internal()&lt;/code&gt;, which marks a module as &quot;internal&quot;, and warns any application code (code outside the package which defines the module) which tries to import it&lt;/li&gt;&lt;li&gt;&lt;code&gt;export.restrict()&lt;/code&gt;, a method which prevents &quot;leakage&quot; of extraneous imported or private names - for example, if you have a module &apos;foo&apos; which imports &apos;sys&apos;, you can normally do &apos;&lt;code&gt;from foo import sys&lt;/code&gt;&apos; in Python and get a result.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;export.singleton()&lt;/code&gt;, which replaces the calling module with a proxy that shares a namespace between the given singleton and the module itself.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;It&apos;ll be some work to turn this rough prototype into something really usable for a large system like Twisted; at the very least it will need to be rebuilt test-first and integrated with the pydoctor and pydoc documentation tools.&amp;nbsp; Let me know what you think, and if I should pursue it!</description>
  <comments>http://glyf.livejournal.com/74356.html</comments>
  <category>twisted</category>
  <category>python</category>
  <category>code</category>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/74154.html</guid>
  <pubDate>Wed, 02 Jan 2008 06:32:11 GMT</pubDate>
  <title>Twenty Post-Dollars</title>
  <link>http://glyf.livejournal.com/74154.html</link>
  <description>I finally hit the limit of my flickr account; I&apos;ve posted 200 pictures, and so, starting from the beginning of my photo stream, some of them are going to start disappearing.&lt;br /&gt;&lt;br /&gt;I have about 100 more pictures to post, but I&apos;ve been intentionally keeping my account free, because I&apos;m not sure if it&apos;s actually worth my time (or money) to use the features of a &quot;pro&quot; account if nobody is really listening.&lt;br /&gt;&lt;br /&gt;So I&apos;m wondering, are there 20 people out there — one for each of the dollars it would take to upgrade my account — who actually care about what I&apos;m posting on flickr?&amp;nbsp; No need to get effusive, just ping me in the comments (or email) to let me know I should upgrade and keep posting pictures.</description>
  <comments>http://glyf.livejournal.com/74154.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/73806.html</guid>
  <pubDate>Mon, 31 Dec 2007 21:11:14 GMT</pubDate>
  <title>It Was A Bullet</title>
  <link>http://glyf.livejournal.com/73806.html</link>
  <description>&lt;a href=&quot;http://www.penny-arcade.com/2007/12/31&quot;&gt;Tycho says&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;More than anything else, &lt;strong&gt;I think it was installing Vista that made me hate PC gaming&lt;/strong&gt;. The constant, system-level interruptions, the impaired compatibility, and most of all the savage &lt;em&gt;kick&lt;/em&gt; to my framerate&apos;s &lt;em&gt;exposed groin&lt;/em&gt; made me wonder what precisely in the fucking fuck I was doing screwing around with this onyx monolith. I knew I was just going to have to upgrade eventually (no), and I wanted to see if there was anything &lt;em&gt;to&lt;/em&gt; this DirectX 10 thing (no), and I wanted to see what the Windows version of Live was like (a warcrime) so I bit the bullet. I shouldn&apos;t have. It was a bullet! That should have been my first clue.&lt;br /&gt;&lt;/blockquote&gt;Is this Microsoft&apos;s secret strategy with Vista?&amp;nbsp; To make everyone by an Xbox because they&apos;re sucking the life out of the PC ecosystem?&amp;nbsp; It hadn&apos;t yet occurred to me that Vista was &lt;em&gt;supposed&lt;/em&gt; to be bad.</description>
  <comments>http://glyf.livejournal.com/73806.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/73589.html</guid>
  <pubDate>Tue, 18 Dec 2007 04:23:42 GMT</pubDate>
  <title>Looks like I&apos;ll be going to PyCon after all</title>
  <link>http://glyf.livejournal.com/73589.html</link>
  <description>Ying and I had been planning to have our wedding on the 11th anniversary of our first date: on march 15th, 2008.&amp;nbsp; I&apos;ve told a few people about our plans, although invitations haven&apos;t gone out yet.&lt;br /&gt;&lt;br /&gt;However, we&apos;re going to have to delay our plans.&amp;nbsp; In addition to the usual logistical issues and scheduling conflicts, one thing we didn&apos;t count on was that planners tend to be exceptionally busy during the holiday season and couldn&apos;t really return our calls.&lt;br /&gt;&lt;br /&gt;I&apos;ll let you all know as soon as I can when the new date is, but we don&apos;t want to set one until we&apos;re sure that we can actually make sure the plans will happen on time.&amp;nbsp; I appreciate all the offers of help — and believe me, we &lt;em&gt;will&lt;/em&gt; call all of you with favors when the time comes — but we can&apos;t really &lt;em&gt;wait&lt;/em&gt; any faster, no matter how much help we have ;).</description>
  <comments>http://glyf.livejournal.com/73589.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/72862.html</guid>
  <pubDate>Mon, 26 Nov 2007 08:25:00 GMT</pubDate>
  <title>The Fear That Haunts My Dreams</title>
  <link>http://glyf.livejournal.com/72862.html</link>
  <description>The good news is that in 2015 we will have a good distributed operating system and programming language; the bad news is that they will be Firefox and JavaScript.</description>
  <comments>http://glyf.livejournal.com/72862.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/72505.html</guid>
  <pubDate>Thu, 13 Sep 2007 13:04:28 GMT</pubDate>
  <title>The xUnit Paradox</title>
  <link>http://glyf.livejournal.com/72505.html</link>
  <description>Billions of years ago, at the dawn of the digital age, Kent Beck invented the idea of testing computer programs and wrote a paper about it called &lt;a href=&quot;http://www.xprogramming.com/testfram.htm&quot;&gt;&quot;Simple Smalltalk Testing: With Patterns&quot;&lt;/a&gt;.&amp;nbsp; In it, he expressed an API &lt;a href=&quot;http://en.wikipedia.org/wiki/XUnit&quot;&gt;which is now colloquially known as &quot;xUnit&quot;&lt;/a&gt;, because there are implementations for various languages called &lt;strong&gt;x&lt;/strong&gt;unit, where &lt;strong&gt;x&lt;/strong&gt; is a reference to the language of implementation: for example, Java&apos;s &quot;JUnit&quot;, Python&apos;s &quot;PyUnit&quot;, and the C++ &quot;CppUnit&quot;.&lt;br /&gt;&lt;br /&gt;Many test-driven developers who use an xUnit implementation eventually become test-framework developers.&amp;nbsp; After some time using an xUnit implementation, they realize that it lacks certain features which would make it more useful, and then writing their own testing tool.&amp;nbsp; Such attempts, in Python, include:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Twisted&apos;s own &lt;code&gt;trial&lt;/code&gt; tool, for whose genesis I am to blame.&lt;sup&gt;&lt;a href=&quot;#trial-defense&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;py.test&lt;/li&gt;&lt;li&gt;TestOOB&lt;/li&gt;&lt;li&gt;nose&lt;/li&gt;&lt;li&gt;doctest&lt;/li&gt;&lt;/ul&gt;Unfortunately, many of these test frameworks are written only looking at the problems with xUnit, and not realizing its benefits.&lt;br /&gt;&lt;br /&gt;There are two things that every potential test framework developer needs to know about xUnit.&lt;br /&gt;&lt;div align=&quot;center&quot;&gt;&lt;img src=&quot;http://www.justlaugh.com/online/vol3issue1/wc142.gif&quot; alt=&quot;The Toast Paradox&quot; /&gt;&lt;/div&gt;&lt;b&gt;The xUnit API is great.&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;You need a structured, well-specified way to manipulate tests.&amp;nbsp; Maybe you don&apos;t realize it yet, but when you have a test suite with a hundred thousand tests, you will want to selectively run parts of it.&amp;nbsp; You will want to be able to arbitrarily group tests differently, change their grouping, ordering, and runtime environment.&amp;nbsp; Most importantly you&apos;ll want to do all this &lt;em&gt;in a program&lt;/em&gt;, not necessarily with a GUI or command-line tool.&amp;nbsp; To write that program, you need a coherent, well-designed, stable API for interacting with tests as first-class objects.&lt;br /&gt;&lt;br /&gt;The biggest thing that xUnit does right is that it &lt;em&gt;exists&lt;/em&gt;.&amp;nbsp; It &lt;em&gt;is&lt;/em&gt; a structured API for interacting with tests as first class objects.&amp;nbsp; Many attempts to implement specific features end up architecturally breaking or ignoring this API, or adding extra, implicit stuff, which will &quot;just work&quot; if you use the particular TestCase class that came with your tool of choice, but break if you try to customize it too heavily or start overriding internal methods.&lt;br /&gt;&lt;br /&gt;xUnit also factors some important responsibilities away from each other.&amp;nbsp; A test &lt;em&gt;case&lt;/em&gt; is different from a test &lt;em&gt;result&lt;/em&gt;; a test &lt;em&gt;suite&lt;/em&gt; contains multiple tests.&amp;nbsp; The magic that is often associated with the &apos;testXXX&apos; naming convention aside, a single test case object represents a single test, and multiple runs of the same tests must create multiple test objects.&amp;nbsp; These might seem obvious, but they are all important insights which must be preserved: and problems occur when they don&apos;t seem quite so obvious.&amp;nbsp; As James Newkirk said on his blog a few years ago: &quot;&lt;font face=&quot;Verdana&quot;&gt;&lt;font face=&quot;Courier New&quot; size=&quot;2&quot;&gt;&lt;font face=&quot;Verdana&quot;&gt;&lt;a href=&quot;http://blogs.msdn.com/jamesnewkirk/archive/2004/12/04/275172.aspx&quot;&gt;I think one of the biggest screw-ups that was made when we wrote NUnit V2.0 was to not create a new instance of the test fixture class for each contained test method.&lt;/a&gt;&quot;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&amp;nbsp; Trial had this screw-up as well, and while it has been fixed, the &apos;-u&apos; option will still re-use a TestCase object to run its own method again for the second invocation.&lt;br /&gt;&lt;br /&gt;Sadly, xUnit is not the solution to all of your problems.&amp;nbsp; There is something else you need to know about it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The xUnit API is terrible.&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;The xUnit API is missing a lot of features that it really needs to be a generally useful test-manipulation layer.&amp;nbsp; It is because of these deficiencies that it is constantly being re-invented or worked around.&amp;nbsp; If it did some more of these things right, then the ad-hoc extensions which don&apos;t conceptually work with it properly wouldn&apos;t keep springing up.&lt;br /&gt;&lt;br /&gt;The main problem with xUnit that is not simply a missing feature is that it uses the &quot;composite&quot; pattern, rather than the &quot;visitor&quot; pattern, to implement test suites.&amp;nbsp; If you want to discover what test cases that a test suite contains, you are supposed to call &apos;run&apos; and it will run them for you.&amp;nbsp; It is impossible to programmatically decompose a suite without stepping outside of the official xUnit API.&lt;br /&gt;&lt;br /&gt;For example, let&apos;s say you wanted to have a &quot;runner&quot; object, which would selectively identify tests to be run in different subprocesses.&amp;nbsp; Without getting involved in the particular implementation details of an xUnit implementation, there&apos;s no way to figure out how many tests are going to be run when you have discovered and invoked a TestCase; you just call &apos;run&apos; and hope for the best.&amp;nbsp; Of course, just about every actual xUnit implementation cheats a little bit in order to do things like generate progress bars to figure out how far done with the test run you are; but it&apos;s always possible - and intentionally supported, even - to generate tests on the fly within &apos;run&apos; and run them.&lt;br /&gt;&lt;br /&gt;xUnit has no intermediary representation of a test result.&amp;nbsp; A vanilla xUnit API assumes that a result is the same thing as a reporter: in other words, when you call a method on the result, it reports it to the user immediately.&amp;nbsp; This absence of a defined way to manipulate test results for later display means that you have to take over the running of the tests if you want to do something interesting with the report; it&apos;s not possible in a strict xUnit implementation to cooperatively define two separate tools which analyze or report data about the &lt;em&gt;same&lt;/em&gt; test run.&lt;br /&gt;&lt;br /&gt;The use of the composite pattern is linked to the lack of an intermediary &quot;test discovery&quot; step.&amp;nbsp; In Beck&apos;s original framework, you have to manually construct your suites out of individual test case objects and call a top-level function that then calls them all, although most xUnit implementations accepted as &quot;standard&quot; these days will provide some level of convenience functionality there.&amp;nbsp; For example, all the implementations I&apos;m aware of will introspect for methods that begin with &quot;test&quot; and automatically create suites that contain one test case instance per method.&lt;br /&gt;&lt;br /&gt;xUnit has no notion of cooperating hooks.&amp;nbsp; If you want to provide some common steps for setUp and tearDown in both library A and library B, you have no recourse but to build complicated diamond inheritance structures around &quot;a.testsupport.TestCase&quot; and &quot;b.testsupport.TestCase&quot;, or give up and manually call functions from setUp and tearDown.&amp;nbsp; This is where Trial has gotten into the most trouble, because it sets up and tears down Twisted-specific state as part of the tool rather than allowing test cases to independently set it up.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Alternative Is Worse&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;Those who have not learned from the benefits that xUnit provides, however, will be doomed to repeat its mistakes, and often make even more.&amp;nbsp; The alternative - let&apos;s call it &quot;AdHocUnit&quot; - is very popular.&amp;nbsp; It&apos;s to start glomming features into random parts of the test manipulation API to support specific use-cases without attention to the overall design of the system.&lt;br /&gt;&lt;br /&gt;Twisted&apos;s Trial, the Zope Test Runner, Nose, and I&apos;m sure quite a few other testing tools in Python all do this, and the result is a situation where the concepts are all basically compatible, but you can&apos;t write a test that uses features from two of these systems at once - and if you&apos;re a person (as there are many such people) who write code that relies heavily on both Twisted and Zope, this can be painful.&lt;br /&gt;&lt;br /&gt;You can definitely tell when you&apos;ve got an AdHocUnit implementation when your APIs are internally throwing around strings that represent file names, module names, and method names, without reference to test objects; when you&apos;ve got global, non-optional start and stop steps (not in setUp or tearDown methods) which twiddle various pieces of framework state, or when you&apos;ve started coupling the semantics of &apos;run&apos; to some ad-hoc internal lists of tests or suites.&amp;nbsp; You can tell you have an AdHocUnit implementation when your test discovery is convenient, but hard-coded to a particular idiom of paths and filenames.&amp;nbsp; Most of all, you can tell you&apos;ve got an AdHocUnit implementation when you&apos;ve got some things called &quot;TestCase&quot; objects which claim to be xUnit objects that subclass from a TestCase class but mysteriously cannot be run by another xUnit framework in the same language.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What To Do Now&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;The object-oriented programming community needs a better API which is as high-level and generic as xUnit.&amp;nbsp; Anyone looking to &quot;fix&quot; xUnit should be careful to create a well-defined structure for tests and document the API and the reasons for every decision in it.&amp;nbsp; It&apos;s interesting to note that the (very successful) Beck Testing Framework was originally presented as a &lt;em&gt;paper&lt;/em&gt;, not as an open source project.&lt;br /&gt;&lt;br /&gt;It might seem like testing APIs don&apos;t require this kind of rigor.&amp;nbsp; They seem deceptively simple to design: at first, all you need to do is run this bit of code, then that bit of code, and make sure they both work.&amp;nbsp; For a while all you&apos;re doing is piling on more and more tests; making sure they all work.&lt;br /&gt;&lt;br /&gt;Then, one day, you want to start &lt;em&gt;doing&lt;/em&gt; things with all these tests.&amp;nbsp; You want to know how long they take to run, how much disk space they use, how many of them call a certain API.&amp;nbsp; You want to run them in different orders to make sure that they are in fact isolated, and don&apos;t interact with each other.&amp;nbsp; You&apos;ll find out, as I did, that huge numbers of your tests are badly written because your first ad-hoc attempt at the framework was wrong.&lt;br /&gt;&lt;br /&gt;Unlike most frameworks, you can&apos;t gradually evolve them and depend on your tests to verify their behavior, because &lt;em&gt;by changing the testing framework, you might have changed the meaning of the tests themselves&lt;/em&gt;.&amp;nbsp; Having a well-tested test framework can help, of course, but while you can test the test framework, you won&apos;t be testing your tests.&lt;br /&gt;&lt;br /&gt;Of course, everything is possible in this world of infinite possibilities.&amp;nbsp; Test frameworks can, and do, evolve; but the process is slower, and more painful than other kinds of evolution.&amp;nbsp; So, when you&apos;re looking to write your own conveniences for testing, don&apos;t throw the baby out with the bathwater: keep what your xUnit implementation does well, retain compatibility with it, and build upon it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Acknowledgments&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;I&apos;d like to thank &lt;a href=&quot;http://mumak.net&quot;&gt;Jonathan Lange&lt;/a&gt;, who encouraged me to consider the benefits of xUnit in the first place, and the Massachusetts Bay Transportation Authority, without whose constant delays and breakdowns I wouldn&apos;t have had time to have written this at all.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;a name=&quot;trial-defense&quot;&gt;1:&amp;nbsp; I didn&apos;t really start &apos;trial&apos;, but a few nasty hacks in the redistribution of pyunit.&amp;nbsp; In my defense, it predated &apos;unittest&apos; as a standard-library module.&amp;nbsp; Jonathan Lange, its current maintainer, was the one who made it an independent tool.&amp;nbsp; Thanks to him, it is now actually compatible to a large extent with the standard &apos;unittest&apos; module.&lt;br /&gt;&lt;br /&gt;&lt;/a&gt;</description>
  <comments>http://glyf.livejournal.com/72505.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/72417.html</guid>
  <pubDate>Sun, 02 Sep 2007 20:33:10 GMT</pubDate>
  <title>Thank You, Microsoft!</title>
  <link>http://glyf.livejournal.com/72417.html</link>
  <description>It looks like the ever-popular Vista may be the impetus which drives game developers towards open platforms.&amp;nbsp; Jonathan Blow, the author of the l&lt;a href=&quot;http://www.arsecast.com/index.php?page=014&quot;&gt;ong-delayed, very original, and kind of quirky indie game &quot;Braid&quot;&lt;/a&gt; has decided that,&lt;a href=&quot;http://braid-game.com/news/?p=26&quot;&gt; thanks to Vista being so annoying, he is going to develop and release Braid on Ubuntu&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Until now, only &lt;a href=&quot;http://zerowing.idsoftware.com/linux/doom/&quot;&gt;relatively unknown developers such as Id Software&lt;/a&gt; have bothered to release or develop games on the Linux platform, but now that the more popular indie developers are starting to look at it, the commercial mainstream might not be far behind.&amp;nbsp; Hooray!</description>
  <comments>http://glyf.livejournal.com/72417.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/72036.html</guid>
  <pubDate>Sat, 01 Sep 2007 09:19:52 GMT</pubDate>
  <title>There is a flash of light!  Your PYTHON has evolved into PSYDUCK!</title>
  <link>http://glyf.livejournal.com/72036.html</link>
  <description>I guess if you don&apos;t read any other python-related blogs (or news, or mailing lists, for that matter) you might not already know that &lt;a href=&quot;http://www.python.org/download/releases/3.0/&quot;&gt;the first alpha of Python 3.0 has been released&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A sample of the release notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There are a few memory leaks&lt;/li&gt;&lt;li&gt;SSL support is disabled&lt;/li&gt;&lt;li&gt;Platform support is reduced&lt;/li&gt;&lt;li&gt;There may be additional issues on 64-bit architectures&lt;/li&gt;&lt;li&gt;There are still some open issues on Windows&lt;/li&gt;&lt;li&gt;Some new features are very fresh, and probably contain bugs&lt;/li&gt;&lt;li&gt;IDLE still has some open issues&lt;/li&gt;&lt;/ul&gt;In other words, this is &lt;em&gt;not&lt;/em&gt; a product for general consumption, and is not labeled as such.&amp;nbsp; Do not use it expecting to be able to get real work done with it.&amp;nbsp; This release is to help the Python development team find bugs and get feedback from the community.&lt;br /&gt;&lt;br /&gt;I&apos;ve already blogged about &lt;a href=&quot;http://glyf.livejournal.com/61347.html&quot;&gt;my inability to get excited about Python 3.0&lt;/a&gt;.&amp;nbsp; Now that it&apos;s begun to arrive, I can more clearly see the scope of the work required to get in sync with it, and the new features that it is actually going to encompass.&amp;nbsp; It&apos;s a staggering amount of work.&lt;br /&gt;&lt;br /&gt;Jean-Paul Calderone has prepared &lt;a href=&quot;http://twistedmatrix.com/%7Eexarkun/py3k/&quot;&gt;a preliminary run of the 2to3 tool over the Twisted codebase&lt;/a&gt;.&amp;nbsp; This includes a 820 kilobyte diff, which took 12 minutes to produce (on a fairly fast, modern piece of hardware).&amp;nbsp; However, this is not even a complete run, because the 2to3 tool cannot even parse two of the files in the repository, despite the fact that they are all valid python.&amp;nbsp; Many of the transformations (especially in the area of the unicode/str conversion) are almost certainly going to drastically change the semantics our test suite, if not break it - although enough of Twisted&apos;s dependencies are missing on 3.0 that I haven&apos;t even had an opportunity to try.&lt;br /&gt;&lt;br /&gt;I still hold out hope that the 3.0 branch will gradually be abandoned, as these changes are rolled back into older versions (2.6+)  of Python and gradually phased in, with their deprecated alternatives being gradually phased out.&amp;nbsp; Right now, though, the plan is to continue parallel development in the 2.x series until 3.0 is ready to &quot;take over&quot;, although I&apos;m not sure how that determination will be made.&lt;br /&gt;&lt;br /&gt;While I wish I could be more excited about something in the 3.0 roadmap, it worries me that some of the excitement I see from others is &lt;a href=&quot;http://www.daa.com.au/pipermail/pygtk/2007-August/014209.html&quot;&gt;enthusiasm for the idea of using it as an excuse to break their own users&apos; software too&lt;/a&gt;.&amp;nbsp; If you&apos;ve written a library for Python, please consider that its users are going to be having a hard enough time upgrading from python 2.x to 3.x; you should really try to provide the smoothest migration path from here to there, and keep your APIs as compatible as possible.&lt;br /&gt;&lt;br /&gt;Although I&apos;d like to say something nice and congratulatory, the thought of spending a year just pushing little piles of syntax into other little piles of syntax, even with the help of a tool like a hypothetically-much-more-advanced 2to3, is honestly just depressing.&amp;nbsp; I&apos;d have to get Twisted (and Axiom and Nevow and Mantissa and Quotient and a handful of proprietary projects that I work on) to work on Python 3.0 before I can use it.&amp;nbsp; If I&apos;m going to work on Twisted, I have &lt;a href=&quot;http://twistedmatrix.com/trac/report/13&quot;&gt;a lot of other things&lt;/a&gt; I&apos;d rather be doing.&amp;nbsp; So my plan, for the moment, is to ignore Python 3.0 as long as I possibly can.&lt;br /&gt;&lt;br /&gt;But hey, that&apos;s what the magic of open source is supposed to be about, right?&amp;nbsp; Do &lt;em&gt;you&lt;/em&gt; like Python 3.0?&amp;nbsp; Do you want to see Twisted (and the various Divmod projects) eventually support it?&amp;nbsp; A fairly substantial portion of the diff in question is a litany of non-controversial stylistic changes to update old, and sometimes creaky parts of the codebase.&amp;nbsp; For example, there are a bunch of usages of the &apos;print&apos; statement that need to be transformed; you might consider submitting a patch which simply removes all usages of &quot;print&quot;, since we probably shouldn&apos;t be using that syntax anyway.&amp;nbsp; That will reduce the size of the changes that we need to consider, generate, and apply in order to be 3.0 compliant. and probably improve the code&apos;s cleanliness quite a bit.&amp;nbsp; Once those parts are applied, and thereby removed from the output of 2to3, we can have a better view of what work is &lt;em&gt;actually&lt;/em&gt; necessary to support 2.x and 3.0 versions simultaneously from the same codebase.&lt;br /&gt;&lt;br /&gt;Of course, comprehensive test coverage is something else frequently brought up when talking about the 2-to-3 migration.&amp;nbsp; Any patches which increase or improve our test coverage will alway be greatly appreciated regardless of any migration issues.&lt;br /&gt;&lt;br /&gt;The one prospect that appeals to me is that, if 3.0 successfully adheres to the vague promise of  breaking backwards compatibility &quot;just this once&quot;, Python may move towards being a real platform instead of simply a tool that you run on another platform.&amp;nbsp; Of course, backwards incompatible changes can be a bit like potato chips - &quot;you can&apos;t eat just one&quot; - but I trust that the Python team can stick to it if they see the value in it and have made the incompatible changes they think are significant and necessary.&lt;br /&gt;&lt;br /&gt;A few days ago I ran Neverwinter Nights on my Ubuntu (feisty) machine and was pleased to discover that despite the fact that there are new versions of SDL, X11, the Linux kernel, GNOME, ALSA, and a dozen other dependencies (all of which are dynamically bound) since five years ago when it was written, it still runs beautifully, with no configuration or re-installation on my part.&amp;nbsp; Getting that kind of reliability from Python, and being able to provide it for Twisted, &lt;em&gt;would&lt;/em&gt; be worth a fair amount of time spent overhauling syntax.</description>
  <comments>http://glyf.livejournal.com/72036.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/71805.html</guid>
  <pubDate>Thu, 23 Aug 2007 04:33:24 GMT</pubDate>
  <title>Pondering Python Path Programming Problems</title>
  <link>http://glyf.livejournal.com/71805.html</link>
  <description>Most Python programmers are at least vaguely aware of sys.path, PYTHONPATH, and the effect they have on importing modules.&amp;nbsp; However, there&apos;s a lot of confusion about how to use them properly, and how powerful these concepts can be if you know how to apply them.&amp;nbsp; Twisted - and in particular the plugin system - make very nuanced use of the python path, which can sometimes make things that use them a bit hard to explain, since there isn&apos;t a well-defined common terminology or good library support for working with paths, except to the extent that they are used by importers.&lt;br /&gt;&lt;br /&gt;This article is really about two things: the general concept of paths, and the Twisted module &quot;twisted.python.modules&quot;, which provides some specific implementations of my ideas about the python path.&lt;br /&gt;&lt;br /&gt;First of all, why should you care about python paths?&amp;nbsp; To put it simply, because very bizarre problems can result if you use them incorrectly.&amp;nbsp; Also, you need to know about them in order to use Twisted&apos;s plugin system effectively, and of &lt;em&gt;course&lt;/em&gt; you want to use Twisted, right?&amp;nbsp; :)&lt;br /&gt;&lt;br /&gt;What kind of problems?&amp;nbsp; Even very popular, well-regarded Python packages by very experienced Python programmers sometimes mess this up pretty badly.&amp;nbsp; Here&apos;s a simple example of what can go wrong with a package you probably know of, the Python Imaging Library:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; import Image&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; import PIL.Image&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; img = PIL.Image.Image()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; Image.__file__&lt;br /&gt;&apos;/usr/lib/python2.5/site-packages/PIL/Image.pyc&apos;&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; PIL.Image.__file__&lt;br /&gt;&apos;/usr/lib/python2.5/site-packages/PIL/Image.pyc&apos;&lt;br /&gt;&lt;/blockquote&gt;Here we can see that you can import PIL&apos;s &quot;Image&quot; module as either &quot;PIL.Image&quot; or simply &quot;Image&quot;.&amp;nbsp; Both these modules are loaded from the same file.&amp;nbsp; On the face of it, this is simply a convenience.&amp;nbsp; But let&apos;s dig deeper:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; PIL.Image == Image&lt;br /&gt;False&lt;br /&gt;&lt;/blockquote&gt;The modules aren&apos;t the same object!&amp;nbsp; This has some nasty practical repercussions:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; isinstance(img, Image.Image)&lt;br /&gt;False&lt;br /&gt;&lt;/blockquote&gt;For example, Image objects created from one of these PIL modules do not register as instances from the other, even though they&apos;re all the same code.&amp;nbsp; Worse yet, this mistake can become &quot;sticky&quot; if you use them along with a module like pickle, which carries the module and class name into the data:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; from cPickle import dumps&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; img2 = Image.Image()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; dumps(img)&lt;br /&gt;&quot;(iPIL.Image\nImage\n ...&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; dumps(img2)&lt;br /&gt;&quot;(iImage\nImage\n ...&lt;br /&gt;&lt;/blockquote&gt;Many Python features and packages depend on matching types.&amp;nbsp; Zope Interface, for example, will not let you use adapters for one Image type for the other, the objects will not compare equivalent even if they really are, and so on.&amp;nbsp; And none of this is a bug in the code!&amp;nbsp; Why does it happen?&lt;br /&gt;&lt;br /&gt;PIL is a package; that is, a directory with Python source code and an &quot;__init__.py&quot; in it, named &quot;PIL&quot;.&amp;nbsp; However, it also installs a &quot;.pth&quot; file as part of its installation.&amp;nbsp; &quot;.pth&quot; files are one way to add entries to your sys.path.&amp;nbsp; This particular one adds the &quot;PIL&quot; directory to your path, which means it can be loaded from two entries: as a package, from your &quot;site-packages&quot; directory.&lt;br /&gt;&lt;br /&gt;This isn&apos;t to pick on PIL or the Effbot; I&apos;ve seen lots of projects which have a &quot;lib&quot; directory with an __init__.py and change its name at installation time, or inconsistently reference subpackages with relative and absolute imports, or do any number of things which are just as bad.&amp;nbsp; I hope that I&apos;ve convinced you not to do the same thing with &lt;em&gt;your&lt;/em&gt; project, but I won&apos;t dwell on the problem here, since I have a solution handy.&lt;br /&gt;&lt;br /&gt;Unless you already know what is going on (although I&apos;m sure many of you reading this already do), this can be a bit confusing to figure out.&amp;nbsp; You can use twisted.python.modules to ask this question rather directly.&amp;nbsp; Here&apos;s how:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; from twisted.python.modules import getModule&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; imageModule = getModule(&quot;Image&quot;)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; pilImageModule = getModule(&quot;PIL.Image&quot;)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; imageModule.pathEntry&lt;br /&gt;PathEntry&amp;lt;FilePath(&apos;/usr/lib/python2.5/site-packages/PIL&apos;)&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; pilImageModule.pathEntry&lt;br /&gt;PathEntry&amp;lt;FilePath(&apos;/usr/lib/python2.5/site-packages&apos;)&amp;gt;&lt;br /&gt;&lt;/blockquote&gt;Here we&apos;re asking twisted.python.modules to give us objects that represent metadata about two modules, without actually loading them.&amp;nbsp; The attribute here is the &apos;pathEntry&apos; attribute, which tells us what entry on sys.path the module would be loaded from, if it&apos;s imported.&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; import sys&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; pilImageModule.isLoaded()&lt;br /&gt;False&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; imageModule.isLoaded()&lt;br /&gt;False&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; &apos;PIL.Image&apos; in sys.modules&lt;br /&gt;False&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; &apos;Image&apos; in sys.modules&lt;br /&gt;False&lt;br /&gt;&lt;/blockquote&gt;Look, no modules!&lt;br /&gt;&lt;br /&gt;Of course, if we &lt;em&gt;wanted&lt;/em&gt; to load those modules, it&apos;s easy enough:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; pilImageModule.load()&lt;br /&gt;&amp;lt;module &apos;PIL.Image&apos; from &apos;/usr/lib/python2.5/site-packages/PIL/Image.pyc&apos;&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; imageModule.load()&lt;br /&gt;&amp;lt;module &apos;Image&apos; from &apos;/usr/lib/python2.5/site-packages/PIL/Image.pyc&apos;&amp;gt;&lt;br /&gt;&lt;/blockquote&gt;You can also get lists of modules.&amp;nbsp; For example, you can see that the list of modules in the &quot;PIL&quot; package is suspiciously similar to the list of top-level modules that comes from the path entry &lt;br /&gt;where the &quot;Image&quot; module was loaded:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; pilModule = getModule(&quot;PIL&quot;)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; pprint(list(pilModule.iterModules())[:5])&lt;br /&gt;[PythonModule&amp;lt;&apos;PIL.ArgImagePlugin&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;PIL.BdfFontFile&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;PIL.BmpImagePlugin&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;PIL.BufrStubImagePlugin&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;PIL.ContainerIO&apos;&amp;gt;]&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; pprint(list(imageModule.pathEntry.iterModules())[:5])&lt;br /&gt;[PythonModule&amp;lt;&apos;ArgImagePlugin&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;BdfFontFile&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;BmpImagePlugin&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;BufrStubImagePlugin&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;ContainerIO&apos;&amp;gt;]&lt;br /&gt;&lt;/blockquote&gt;As you might imagine, the ability to list modules and load the ones that seem interesting is a great way to load plugins - and that&apos;s exactly how Twisted&apos;s plugin system is implemented.&amp;nbsp; While the plugin system itself is a topic for another post (or perhaps you could just &lt;a href=&quot;http://twistedmatrix.com/projects/core/documentation/howto/plugin.html&quot;&gt;read the documentation&lt;/a&gt;) the way it finds plugins is interesting.&lt;br /&gt;&lt;br /&gt;For example, let&apos;s take a look at the list of Mantissa plugin modules I have installed:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; xmplugins = getModule(&apos;xmantissa.plugins&apos;)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; pprint(list(xmplugins.iterModules()))&lt;br /&gt;[PythonModule&amp;lt;&apos;xmantissa.plugins.adminoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.baseoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.free_signup&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.offerings&apos;&amp;gt;]&lt;br /&gt;&lt;/blockquote&gt;This simple query is actually an incomplete list.&amp;nbsp; It&apos;s just the modules that come with Mantissa itself.&amp;nbsp; Python has a special little-known rule when loading modules from packages, and twisted.python.plugins honors it: &lt;a href=&quot;http://docs.python.org/tut/node8.html#SECTION008430000000000000000&quot;&gt;if there is a special variable called &quot;__path__&quot; in a package, it is a list of path names to load modules from&lt;/a&gt;.&amp;nbsp; However, twisted.python.plugins doesn&apos;t load modules unless you ask it to, so it can&apos;t determine the value of that attribute.&amp;nbsp; As it so happens, twisted.plugins uses the __path__ attribute in order to allow you to keep your development installations separate, so twisted.python.plugins can&apos;t determine all the places you might need to look for plugins without some help.&amp;nbsp; Let&apos;s just load that package so we can look at its __path__ attribute:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; xmplugins.load()&lt;br /&gt;&amp;lt;module &apos;xmantissa.plugins&apos; from &apos;/home/glyph/Projects/Divmod/trunk/Mantissa/xmantissa/plugins/__init__.pyc&apos;&amp;gt;&lt;br /&gt;&lt;/blockquote&gt;Now that we&apos;ve loaded it, let&apos;s have a look at that list:&lt;br /&gt;&lt;blockquote&gt;&amp;gt;&amp;gt;&amp;gt; pprint(list(xmplugins.iterModules()))&lt;br /&gt;[PythonModule&amp;lt;&apos;xmantissa.plugins.adminoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.baseoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.free_signup&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.offerings&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.mailoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.radoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.sineoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.hyperbolaoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.imaginaryoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.blendix_offering&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.billed_signup&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.billoff&apos;&amp;gt;,&lt;br /&gt;&amp;nbsp;PythonModule&amp;lt;&apos;xmantissa.plugins.derivoff&apos;&amp;gt;]&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;That&apos;s my full list of Mantissa plugins, including my &lt;em&gt;super secret&lt;/em&gt; &lt;em&gt;Divmod proprietary &lt;/em&gt;plugins.&lt;br /&gt;&lt;br /&gt;This list is generated because plugins packages use a feature (which was previously kind of a gross hack but &lt;a href=&quot;http://twistedmatrix.com/trac/browser/trunk/twisted/plugin.py?rev=20920&quot;&gt;will be an officially supported feature of the next version of Twisted&lt;/a&gt;) to set their path to every directory with the same name as the plugin package &lt;em&gt;which is not also a package&lt;/em&gt; on your python path.&amp;nbsp; In other words, if you have 2 sys.path entries, a/ and b/, and one package, x.plugins, in b/x/plugins/__init__.py with this trick in it, then if you have a file b/x/plugins/foo.py, it will be considered to contain the module &quot;x.plugins.foo&quot;.&amp;nbsp; This requires that you &lt;em&gt;do not&lt;/em&gt; have a file b/x/__init__.py or b/x/plugins/__init__.py.&amp;nbsp; If you do, this hack will treat the two paths the same way that Python does: duplicate packages in your path, so the package in a/ is loaded and the package in b/ is ignored.&lt;br /&gt;&lt;br /&gt;The distinction between packages and path entries is why all the Twisted and Divmod projects conventionally have capitalized directory names but lowercase package names.&amp;nbsp; &quot;Twisted&quot; is where your &lt;em&gt;path entry&lt;/em&gt; should point; &quot;twisted&quot; is the &lt;em&gt;python package&lt;/em&gt; that is loaded from that &lt;em&gt;path entry&lt;/em&gt;.&amp;nbsp; &quot;Twisted&quot; should never have an __init__.py in it.&amp;nbsp; &quot;twisted&quot; always should.&amp;nbsp; This goes the same for &quot;Axiom&quot; and &quot;axiom&quot;, &quot;Mantissa&quot; and (the &lt;a href=&quot;http://divmod.org/trac/ticket/1086&quot;&gt;unfortunately named&lt;/a&gt;) &quot;xmantissa&quot;.&amp;nbsp; You will sometimes encounter other examples of this style of naming &lt;a href=&quot;http://slipgate.za.net/%7Emithrandi/darcs/FlyingCircus/&quot;&gt;floating around the web&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;When using Twisted and Divmod infrastructure, keeping this distinction is clear is critical, because otherwise it is difficult to develop plugins independently.&amp;nbsp; You probably don&apos;t want to copy your development plugins into your Twisted installation - they&apos;re part of your source repository, after all, not ours.&amp;nbsp; However, keeping the distinction clear in your mind will avoid lots of obscure problems with duplicate classes and naming, so it&apos;s generally a good idea even if you don&apos;t like our naming conventions.&lt;br /&gt;&lt;br /&gt;Please let me know in the comments which parts of this post you found useful, if any.&amp;nbsp; I know it&apos;s a bit rambling, and covers a number of different topics, some of which may be obvious and some of which might be inscrutable.&amp;nbsp; I&apos;ve experienced quite a bit of confusion when talking to other python programmers about this stuff, but I&apos;m not sure if it was my awkward explanation of Twisted&apos;s plugin system or some inherent issue in Python&apos;s path management.</description>
  <comments>http://glyf.livejournal.com/71805.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/71554.html</guid>
  <pubDate>Wed, 22 Aug 2007 01:54:29 GMT</pubDate>
  <title>Not Just The Faithful</title>
  <link>http://glyf.livejournal.com/71554.html</link>
  <description>As I&apos;ve said before, &lt;a href=&quot;http://glyf.livejournal.com/65724.html&quot;&gt;Microsoft Windows Vista is a terrible disaster&lt;/a&gt; which I hope I never have to deal with in any capacity, professional or otherwise.&amp;nbsp; I suspect that it is inevitable, but I will resist it for as long as possible.&lt;br /&gt;&lt;br /&gt;The FSF has a campaign, &quot;&lt;a href=&quot;http://badvista.fsf.org/&quot;&gt;&lt;strong&gt;BAD&lt;/strong&gt;VISTA&lt;/a&gt;&quot;, to educate end-users about the ways in which Vista is limiting your freedom more aggressively than any other commercial software product to date.&amp;nbsp; Unfortunately this can sometimes sound a bit ... overdramatic, even if it is pretty much all true.&amp;nbsp; For example, a prominently featured quotation:&lt;br /&gt;   &lt;blockquote&gt;Windows Vista includes an array of “features” that you don&apos;t want. These features will make your computer less reliable and less secure. They&apos;ll make your computer less stable and run slower. They will cause technical support problems. They may even require you to upgrade some of your peripheral hardware and existing software. And these features won&apos;t do anything useful. In fact, they&apos;re working against you.&lt;a href=&quot;http://www.schneier.com/blog/archives/2007/02/drm_in_windows_1.html&quot;&gt;&lt;br /&gt;&lt;/a&gt;&lt;div align=&quot;right&quot;&gt;&lt;a href=&quot;http://www.schneier.com/blog/archives/2007/02/drm_in_windows_1.html&quot;&gt;- Bruce Schneier, &quot;DRM in Windows Vista&quot;, from Schneier on Security&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;I recently had the experience of talking to a Regular User in a consumer electronics store about his vista &quot;upgrade&quot;.&amp;nbsp; His &quot;computer guy&quot; had told him that Vista was like XP, but better.&amp;nbsp; Little did he know that the &quot;better&quot; would mean that the computer ran visibly slower, had reduced functionality, and required the purchase of newer, more expensive hardware.&lt;br /&gt;&lt;br /&gt;Of course, I gave him my rant about the &lt;em&gt;other&lt;/em&gt; reasons he shouldn&apos;t have upgraded, and the poor guy turned white as a sheet.&amp;nbsp; I don&apos;t think he&apos;s going to be purchasing any more &quot;upgrades&quot; from his &quot;computer guy&quot;.&lt;br /&gt;&lt;br /&gt;But, what does the other side have to say about this fancy new operating system?&amp;nbsp; Surely there are some worthwhile new conveniences that we are trading this freedom for?&amp;nbsp; Let&apos;s see what one ex-Microsoft employee and prominent Windows developer has to say about it:&lt;br /&gt;&lt;blockquote&gt;&quot;I&apos;ve been using Vista on my home laptop since it shipped, and can say with some conviction that nobody should be using it as their primary operating system -- it simply has no redeeming merits to overcome the compatibility headaches it causes. Whenever anyone asks, my advice is to stay with Windows XP (and to purchase new systems with XP preinstalled).&quot;&lt;a href=&quot;http://www.joelonsoftware.com/items/2007/08/18.html&quot;&gt;&lt;br /&gt;&lt;/a&gt;&lt;div align=&quot;right&quot;&gt;&lt;a href=&quot;http://www.joelonsoftware.com/items/2007/08/18.html&quot;&gt;- Joel Spolsky, &quot;Even the Office 2007 box has a learning curve&quot;, from Joel On Software&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;... and there you have it.&amp;nbsp; Friends don&apos;t let friends use Vista.&lt;br /&gt;&lt;div align=&quot;right&quot;&gt;&lt;br /&gt;&lt;/div&gt;</description>
  <comments>http://glyf.livejournal.com/71554.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/71211.html</guid>
  <pubDate>Sun, 15 Jul 2007 19:22:34 GMT</pubDate>
  <title>Pet Peeve</title>
  <link>http://glyf.livejournal.com/71211.html</link>
  <description>The word &quot;depreciate&quot; means &quot;to lessen the price or value of&quot;.&amp;nbsp; This is an &lt;em&gt;accounting&lt;/em&gt; jargon term referring to the process by which assets lose value over time.&amp;nbsp; It is pronounced &apos;Dee Pree Shee Ate&quot;.&lt;br /&gt;&lt;br /&gt;The word &quot;deprecate&quot; means &quot;to express disapproval of&quot; or &quot;to urge reasons against; protest against&quot;.&amp;nbsp; This is a &lt;em&gt;programming&lt;/em&gt; jargon term describing the process by which APIs become less favorable over time.&amp;nbsp; It is pronounced &quot;Deh Preh Kayt&quot;.&lt;br /&gt;&lt;br /&gt;These words, while they have similar meanings, are not synonyms.&amp;nbsp; Please do not confuse them, especially when using their jargon senses.&amp;nbsp; It sounds like nails on a chalkboard to me, having worked on accounting software.&amp;nbsp; I would like to be able to use phrases like &quot;a deprecated depreciation function&quot; without eliciting bewilderment.&lt;br /&gt;&lt;br /&gt;Both Java and Python consistently use &quot;@deprecated&quot;, and &quot;DeprecationWarning&quot;.&amp;nbsp; English usage of these terms may be shifting, but &quot;DepreciationWarning&quot; or &quot;@depreciated&quot; will still get you runtime or compiler errors, so please stick to &quot;deprecate&quot; consistently while talking about code.&lt;br /&gt;&lt;br /&gt;Thank you.</description>
  <comments>http://glyf.livejournal.com/71211.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/71126.html</guid>
  <pubDate>Sat, 14 Jul 2007 17:45:13 GMT</pubDate>
  <title>Mindful Link Propagation</title>
  <link>http://glyf.livejournal.com/71126.html</link>
  <description>It occurs to me that there may still be a few Python people who read this blog but have not yet discovered &lt;a href=&quot;http://jcalderone.livejournal.com/&quot;&gt;JP Calderone&apos;s&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you are such a person, he just did an &lt;a href=&quot;http://jcalderone.livejournal.com/32837.html&quot;&gt;excellent write-up of the practical implications of Python&apos;s rich comparison operators&lt;/a&gt;.&amp;nbsp; Check it out.</description>
  <comments>http://glyf.livejournal.com/71126.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/70684.html</guid>
  <pubDate>Sun, 08 Jul 2007 02:47:17 GMT</pubDate>
  <title>Functional Functions and the Python Singleton Unpattern</title>
  <link>http://glyf.livejournal.com/70684.html</link>
  <description>Have you ever written a module that looked like this?&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;subscribers = []&lt;br /&gt;&lt;br /&gt;def addSubscriber(subscriber):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; subscribers.append(subscriber)&lt;br /&gt;&lt;br /&gt;def publish(message):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for subscriber in subscribers:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; subscriber.notify(message)&lt;br /&gt;&lt;/code&gt;&lt;/blockquote&gt;And then used it like this?&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;from publisher import publish&lt;br /&gt;&lt;br /&gt;class worker:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def work(self):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; publish(self)&lt;br /&gt;&lt;/code&gt;&lt;/blockquote&gt;I&apos;ve done this many times myself.&lt;br /&gt;&lt;br /&gt;I used to think that this was the &quot;right&quot; way to implement Singletons in Python.&amp;nbsp; Other languages had static members and synchronized static accessors and factory methods; all kinds of rigamarole to achieve this effect, but Python simply had modules.&lt;br /&gt;&lt;br /&gt;Now, however, I realize that there is no &quot;right&quot; way to implement Singleton in Python, because singletons are simply a bad thing to have.&amp;nbsp; As &lt;a href=&quot;http://en.wikipedia.org/wiki/Singleton_pattern&quot;&gt;Wikipedia points out&lt;/a&gt;, &quot;It is also considered an &lt;a href=&quot;http://en.wikipedia.org/wiki/Anti-pattern&quot; title=&quot;Anti-pattern&quot;&gt;anti-pattern&lt;/a&gt; since it is often used as a &lt;a href=&quot;http://en.wikipedia.org/wiki/Euphemism&quot; title=&quot;Euphemism&quot;&gt;euphemism&lt;/a&gt; for &lt;a href=&quot;http://en.wikipedia.org/wiki/Global_variable&quot; title=&quot;Global variable&quot;&gt;global variable&lt;/a&gt;.&quot;&lt;br /&gt;&lt;br /&gt;The module above is brittle, and as a result, unpleasant to test and extend.&lt;br /&gt;&lt;br /&gt;It&apos;s difficult to test because the call to &quot;publish&quot; cannot be indirected without monkeying around with the module&apos;s globals - generally recognized to be poor style, and prone to errors which will corrupt later, unrelated tests.&lt;br /&gt;&lt;br /&gt;It makes code that interacts with it difficult to test, because while you can temporary mangle global variables in the most egregious of whitebox tests, tests for code that is further away shouldn&apos;t need to know about the implementation detail of &quot;publish&quot;.&amp;nbsp; Furthermore, code which adds subscribers to the global list will destructively change the behavior of later tests (or later code, if you try to invoke your tests in a running environment, since we all know running environments are where the interesting bugs occur).&lt;br /&gt;&lt;br /&gt;It&apos;s difficult to extend because there is no explicit integration point with &apos;publish&apos;, and all instances share the same look-up.&amp;nbsp; If you want to override the behavior of &quot;work&quot; and send it to a different publisher, you can&apos;t call to the superclass&apos;s implementation.&lt;br /&gt;&lt;br /&gt;Unfortunately, this probably doesn&apos;t seem particularly bad, because bad examples abound.&amp;nbsp; It&apos;s just the status quo.&amp;nbsp; Twisted&apos;s twisted.python.log module is used everywhere like this.&amp;nbsp; The standard library&apos;s sys.path, sys.stdin/out/err, warnings.warn_explicit, and probably a dozen examples I can&apos;t think of off the top of my head, all work like this.&lt;br /&gt;&lt;br /&gt;And there&apos;s a good reason that this keeps happening.&amp;nbsp; Sometimes, you feel as though your program really does need a &quot;global&quot; registry for some reason; you find yourself wanting access to the same central object in a variety of different places.&amp;nbsp; It seems convenient to have it available, and it basically works.&lt;br /&gt;&lt;br /&gt;Here&apos;s a technique for implementing that convenience, while still allowing for a clean point of integration with other code.&lt;br /&gt;&lt;br /&gt;First, make your &quot;global&quot; thing be a class.&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;class Publisher:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def __init__(self):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.subscribers = []&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def addSubscriber(self, subscriber):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.subscribers.append(subscriber)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def publish(self, message):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for subscriber in self.subscribers:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; subscriber.notify(message)&lt;br /&gt;&lt;br /&gt;thePublisher = Publisher()&lt;/code&gt;&lt;/blockquote&gt;Second, decide and document how &quot;global&quot; you mean.&amp;nbsp; Is it global to your process?&amp;nbsp; Global to a particular group of objects?&amp;nbsp; Global to a certain kind of class?&amp;nbsp; Document that, and make sure it is clear who should use the singleton you&apos;ve created.&amp;nbsp; At some point in the future, someone will almost certainly come along with a surprising requirement which makes them want a different, or wrapped version of your global thing,&amp;nbsp; Documentation is always important, but it is particularly important when dealing with globals, because there&apos;s really no such thing as completely global, and it is difficult to determine from context just how global you intend for something to be.&lt;br /&gt;&lt;br /&gt;Third, and finally, encourage using your singleton by using it as a default, rather than accessing it directly.&amp;nbsp; For example:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;from publisher import thePublisher&lt;br /&gt;&lt;br /&gt;class Worker:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; publisher = thePublisher&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def work(self):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.publisher.publish(self)&lt;br /&gt;&lt;/code&gt;&lt;/blockquote&gt;In this example, you now have a clean point of integration for testing and extending this code.&amp;nbsp; You can make a single Worker instance, and change its &quot;publisher&quot; attribute before calling &quot;work&quot;.&amp;nbsp; Of course, if you&apos;re willing to burn a whole extra two lines of code, you can make it an optional argument to the constructor of Worker.&amp;nbsp; If you decide that in fact, your publisher isn&apos;t global at all, but system-specific, this vastly decreases the amount of code you have to change.&lt;br /&gt;&lt;br /&gt;Does this mean you should make everything into objects, and never use free functions?&amp;nbsp; No.&amp;nbsp; Free functions are fine, but functions in Python are for &lt;em&gt;functional&lt;/em&gt; programming.&amp;nbsp; The hint is right there in the name.&amp;nbsp; If you are performing computations which return values, and calling other functions which do the same thing, it makes perfect sense to use free functions and not bog yourself down with useless object allocations and &apos;self&apos; arguments.&lt;br /&gt;&lt;br /&gt;Once you&apos;ve started adding mutable state into the mix, you&apos;re into object territory.&amp;nbsp; If you&apos;re appending to a global list, if you&apos;re setting a global &quot;state&quot; variable, even if you&apos;re writing to a global file, it&apos;s time to make a class and give it some methods.</description>
  <comments>http://glyf.livejournal.com/70684.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/70423.html</guid>
  <pubDate>Fri, 15 Jun 2007 23:53:31 GMT</pubDate>
  <title>Ultimate Quality 功夫 System</title>
  <link>http://glyf.livejournal.com/70423.html</link>
  <description>&lt;blockquote&gt; &lt;i&gt;Often it is the  means  that  justify  the  ends:  Goals advance  technique  and  technique  survives  even when goal structures crumble.&lt;br /&gt;&lt;/i&gt;&lt;div align=&quot;right&quot;&gt;- Alan Perlis, &lt;u&gt;Epigram 64&lt;/u&gt;, &quot;Epigrams in Programming&quot;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;How does a computer programmer get better at computer programming?&lt;br /&gt;&lt;br /&gt;In a very broad sense, there is some consensus on the answer to this question.&amp;nbsp; It&apos;s like the answer any other skill.&amp;nbsp; &quot;Do a lot of it.&quot;&amp;nbsp; Much like other skills, though, while necessary, this condition is not sufficient.&amp;nbsp; If you want to get really good at punching, you can punch a brick with your thumb inside your first ten thousand times, and someone who has learned to do it correctly and had a fraction of the equivalent amount of practice will be a lot better at punching than you.&lt;br /&gt;&lt;br /&gt;So, to expand on that consensus, to get better at programming, you have to learn to do it right, and &lt;em&gt;then&lt;/em&gt; do a lot of it, hopefully with someone correcting your mistakes and helping you do it better each time.&amp;nbsp; Like any other kind of serious training.&lt;br /&gt;&lt;br /&gt;This concept is pretty old.&amp;nbsp; Shortly after the dawn of time, the dragon god-emperor pioneered this concept when he invented a martial art that is now popularly known as &quot;Kung Fu&quot;, or, literally, &quot;achievement through great effort&quot;.&amp;nbsp; The term does not necessarily refer specifically to martial arts, and can (in both Chinese and English) refer simply to the fact that someone has achieved something exceptional through a lot of effort and practice.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://web.archive.org/web/20060115041454/www.thinkgeek.com/tshirts/coder/28f4/back/&quot;&gt;Specious metaphors between computer programming and martial arts&lt;/a&gt; (and &lt;a href=&quot;http://www.amazon.com/Zen-Programming-Geoffrey-James/dp/0931137098&quot;&gt;various&lt;/a&gt; &lt;a href=&quot;http://www.canonical.org/%7Ekragen/tao-of-programming.html&quot;&gt;eastern&lt;/a&gt; &lt;a href=&quot;http://www.python.org/doc/Humor.html#zen&quot;&gt;philosophies&lt;/a&gt;) have existed for almost as long as computer programming itself.&amp;nbsp; As a cocky and callow youth, I occasionally had these metaphors generously applied to me, but they rang hollow even in my hubristic state of mind.&amp;nbsp; I knew that despite any talents I might have, at the very least, I &lt;em&gt;hadn&apos;t&lt;/em&gt; spent a lifetime of effort achieving a near-supernatural mastery of computers.&amp;nbsp; I had just messed around with C++ and Java a little bit.&lt;em&gt;&lt;/em&gt;&amp;nbsp; The appellation of &quot;master&quot; was inappropriate, but I didn&apos;t know how one really did go about getting to that point.&amp;nbsp; I had already met a few folks in the programming profession who were &lt;a href=&quot;http://www.microfocus.com/products/more/ObjectCOBOLDeveloperSuite/index.asp&quot;&gt;old, but certainly not yet wise&lt;/a&gt;, and I wanted to learn how &lt;em&gt;not&lt;/em&gt; to end up like that.&lt;br /&gt;&lt;blockquote&gt;&lt;em&gt;Whenever  two  programmers  meet  to  criticize  their programs, both are silent.&lt;/em&gt;&lt;br /&gt;&lt;div align=&quot;right&quot;&gt;- Alan Perlis, &lt;u&gt;Epigram 101&lt;/u&gt;, &quot;Epigrams in Programming&quot;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;I first noticed that the parallel between UQDS and these various arts and philosophies when a friend observing Divmod from afar mentioned that our review process was intimidating.&amp;nbsp; Intrigued, I conducted a straw poll among my programming friends who are not deeply immersed in the eternal struggle of the Twisted or Divmod codebases, and found that there was a fairly consistent impression: the exchange of comments between authors and reviewers was highly unusual, intimidating, and impressive.&lt;br /&gt;&lt;br /&gt;It&apos;s unusual because most people cannot read code well enough to comment in as much depth.&amp;nbsp; Code review is becoming more popular, but it&apos;s still rare.&amp;nbsp; Even intense, newfangled &quot;Agile&quot; processes like Extreme Programming focus more on discussing code as it&apos;s being created than reading code which has already been written.&lt;br /&gt;&lt;br /&gt;The exchange is intimidating because many of the comments are extremely fine-grained and not very forgiving.&amp;nbsp; It isn&apos;t uncommon to see comments about a tiny private method missing documentation, or test coverage doesn&apos;t hit a single error condition.&amp;nbsp; This causes the reader to muse on the ways in which their own code would fail if it were subjected to such intense scrutiny.&lt;br /&gt;&lt;br /&gt;Finally, the exchange is impressive because the factors which make it intimidating make the programmers doing it seem extremely skilled.&amp;nbsp; If you&apos;re looking at work which is being subjected to criticism only on details which seem insignificant, it implies that it is in every other way correct.&amp;nbsp; To some extent this impression is illusory, because the reviewers are only human and may be missing more important things.&amp;nbsp; For something as complex and intricate as software, though, it&apos;s impressive assuming that the reviewers tried at all.&lt;br /&gt;&lt;br /&gt;I can&apos;t pretend that UQDS is an entirely unique process in this regard.&amp;nbsp; The Python and Subversion projects practice regular code reviews in public, and those are just the ones I know off the top of my head.&amp;nbsp; This isn&apos;t a phenomenon unique to open source either - I know that both Canonical and Google do fairly comprehensive code review on their closed-source products.&amp;nbsp; I&apos;ve heard milder expressions of awe - although perhaps they were only milder because they came from friends - about those projects and companies.&lt;br /&gt;&lt;br /&gt;While it isn&apos;t unique, UQDS is particularly intense.&amp;nbsp; Some other processes try to be &quot;reasonable&quot; - which is, on the face of it, a reasonable thing to do.&amp;nbsp; Small, &quot;obviously correct&quot; fixes are sometimes allowed without tests.&amp;nbsp; &quot;Security&quot; fixes are sometimes allowed in for expediency because of their urgency.&amp;nbsp; Some committers are sometimes exempt.&amp;nbsp; Some reviews are done after the fact.&amp;nbsp; Documentation is often optional, since reviews are meant to enforce correctness, not every aspect of quality.&amp;nbsp; For better or worse, UQDS sets out a much stricter guidelines: &lt;em&gt;everything&lt;/em&gt; must be tested.&amp;nbsp; &lt;em&gt;Everything&lt;/em&gt; must be documented.&amp;nbsp; &lt;em&gt;Nobody&lt;/em&gt; is allowed to bypass the process.&lt;br /&gt;&lt;br /&gt;It isn&apos;t all roses.&amp;nbsp; Especially when a contributor needs to update an area of code &lt;em&gt;not&lt;/em&gt; originally developed under this regime, it can be frustrating to need to document and test and correct code that isn&apos;t really related to their goal.&amp;nbsp; Sometimes the overabundance of the opportunity for review creates interminable discussions about a design, when there is really a need to just go with a solution that works for now.&amp;nbsp; There may be some room to adjust the constraints to place less of a burden on those who want to contribute small improvements, and that&apos;s difficult without compromising on overall quality.&lt;br /&gt;&lt;br /&gt;Despite their drawbacks, these rules plus a public ticket tracker add up to something that the industry of martial arts has known about the power of for quite some time: the exhibition.&amp;nbsp; If you&apos;re going to run a martial arts studio you need to attract students, and to do that you need to &quot;wow&quot; people every so often.&amp;nbsp; Martial arts are about defense, mind-body integration, health and fitness, and self-discipline, but putting that on a poster isn&apos;t going to fill up the thursday evening class.&amp;nbsp; Sometimes you need to get your school together and show off just how bad you would be able to &lt;em&gt;kick some ass&lt;/em&gt; with your skills, you know, just in case anybody was wondering.&lt;br /&gt;&lt;br /&gt;I am quite proud of the fact that I&apos;ve received numerous unsolicited comments about the quality of the code itself within certain parts of Divmod and Twisted.&amp;nbsp; Like any programmer I&apos;ve received my share of complaints about my work, too (especially the stuff that precedes all this intense review), but &lt;a href=&quot;http://steve-yegge.blogspot.com/2006/07/get-famous-by-not-programming.html&quot;&gt;as Steve Yegge famously observed&lt;/a&gt;, most programmers who are famous for their programs don&apos;t actually write programs; they write &lt;em&gt;about&lt;/em&gt; programs, or they wrote some programs nobody has the code to.&lt;br /&gt;&lt;br /&gt;I&apos;m almost more pleased with the idea that I could help pave the way for programming to have some real rock-stars on the merit of their code than the idea of being a rock-star myself.&lt;br /&gt;&lt;blockquote&gt;&lt;em&gt;Dealing with failure is easy:  Work  hard  to  improve. Success  is  also  easy  to  handle: You&apos;ve solved the wrong problem. Work hard to improve.&lt;/em&gt;&lt;br /&gt;&lt;div align=&quot;right&quot;&gt;- Alan Perlis, &lt;u&gt;Epigram 101&lt;/u&gt;, &quot;Epigrams in Programming&quot;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div align=&quot;right&quot;&gt;&lt;div align=&quot;left&quot;&gt;To close, there are a couple of skills that I think UQDS promotes which there might be other ways to promote.&amp;nbsp; If you want to develop a competing &quot;school&quot;, these are things to think about:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Reading code.&lt;/strong&gt;&amp;nbsp; Open source is great because you &lt;em&gt;can&lt;/em&gt; read the code, but UQDS gives you a reason to &lt;em&gt;do&lt;/em&gt; the reading.&amp;nbsp; Not just skim, not participate in the writing, but really read and analyze.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Communicating with other programmers.&lt;/strong&gt;&amp;nbsp; Another thing that UQDS has in common with a martial art is that it&apos;s adversarial.&amp;nbsp; You throw the &quot;punch&quot; of a patch, the reviewer &quot;blocks&quot; with a branch, and so on.&amp;nbsp; You don&apos;t need to be a social butterfly to get this right, but you do need to be very focused on the technical communication skills required to explain your ideas.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Testing.&lt;/strong&gt;&amp;nbsp; Test-driven development is an excellent trend.&amp;nbsp; UQDS doesn&apos;t have much to add to that beyond enforcing it, and making the application of TDD the easiest way to get through reviews which deal with issues like test coverage.&amp;nbsp; (If you test everything before you write it, of course your tests are going to cover everything before you put it into review.)&lt;/li&gt;&lt;/ul&gt;I&apos;m not a martial artist.&amp;nbsp; The closest I&apos;ve come was years ago when I was a fencer, and although I hope to pick up a sword again some time soon, I am no expert.&amp;nbsp; Still, I have a great deal of respect for Kung Fu, and I hear a fair amount about it from &lt;a href=&quot;http://ordinalten.livejournal.com/&quot;&gt;a fellow I know who did some training in it&lt;/a&gt;, and I think this is a bit more accurate of a comparison than the earlier attempts that I mentioned; so if you&apos;re a twelfth dan grand master in ultimate ninjitsu, please don&apos;t explode my head with your psychic powers if you think I&apos;m wrong.&lt;br /&gt;&lt;br /&gt;A disagreement in the comments would be sufficient.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;</description>
  <comments>http://glyf.livejournal.com/70423.html</comments>
  <lj:security>public</lj:security>
</item>
<item>
  <guid isPermaLink='true'>http://glyf.livejournal.com/70218.html</guid>
  <pubDate>Tue, 12 Jun 2007 18:51:19 GMT</pubDate>
  <title>50/50 again!</title>
  <link>http://glyf.livejournal.com/70218.html</link>
  <description>Or, 72/72 to be precise.&lt;br /&gt;&lt;br /&gt;Twisted is &lt;a href=&quot;http://jyte.com/cl/twistedmatrix.com-is-the-engine-of-your-internet&quot;&gt;once again in danger of losing the engineship of your Internet&lt;/a&gt;!&amp;nbsp; If you haven&apos;t voted on this Jyte claim already, please do so now!</description>
  <comments>http://glyf.livejournal.com/70218.html</comments>
  <lj:security>public</lj:security>
</item>
</channel>
</rss>
