PyChoose: Switch between installed versions of Python

You can switch between different installed versions of Python by simply prepending to your PATH. However, this goes wrong in some scenarios, such as when a tool like 'virtualenv' is installed in one Python version, but not in another. Pretty soon you will be executing your project with one Python version, but referencing the site-packages of another. When switching, other versions of Python need to be stripped from your PATH.

As a solution, I present a new Python module, pychoose. For the moment this is Windows only:

>python -V
Python 2.6.4
>easy_install pychoose
>pychoose 24
(Py24) >python -V
Python 2.4.4
(Py24) >exit
>python -V
Python 2.6.4

It prepends to the PATH as above, but it also filters other Python versions out the PATH. It modifies the PROMPT to show the user they are operating in a modified environment. It works by spawning a new shell with the modified environment. To return to your default Python interpreter, type 'exit'.

PyPI page:

Subversion repository:

Preview the module source code.

It seems to work for simple scenarios, but there are a bunch of known problems with it (see the PyPI page above), which I'd like to fix if people think this is useful. I just whipped this up and don't really understand the issues surrounding this. In particular is spawning a new shell the best way to modify the current console's environment? Is a less-instrusive alternative to generate .bat file which can be executed to change the current shell's environment?

Feedback & suggestions very welcome.

A Layman's Philosophical Musings

A quick response to to polymath Chris DeLeon's thoughtful post exhorting the virtues of Philosophy.

A position of ignorance

Right off the bat I should qualify all this by saying that I don't know anything about Philosophy. I've never studied it. It seems at times to be an alien mismatch to the sensibilities of my left-brained mind, and worse, one that claims to be about logic and reasoning, topics that ostensibly should be right up my alley. I read Bertrand Russell's History of Western Philosophy last year, and although that was a fabulous, educational romp through the field, my complaints about it still stand. Now I'm reading a second book, Think : A Compelling Introduction to Philosophy by Simon Blackburn.

The Process

The process of thinking about things is a valuable and worthwhile endeavour. Some people enjoy it, some people don't. Amongst those who enjoy it, it becomes an end unto itself, and thought and discussions typically stray towards the more abstract, the more meta, the more foundational. Anyone who has gleefully participated in a pub discussion about religion, death, reality, consciousness, government, or any of a million other topics has participated in this process. It is joyful, sometimes enlightening, entertaining, and refines the individual's ability to meaningfully reason on all kinds of topics, from the abstract to the prosaic. In this sense, and especially from the perspective of the individual's active participation in the activity, I'm all in favour of philosophy. Presumably this is why I have been drawn back to read this second book.

The Content

If anything, I have had more objections to Think than I had to Russell's book. At first, I thought that my issue was simply that the discussed historical philosophers produced curiously shoddy work. Their premises seem shaky. Their arguments full of holes at best. Their conclusions entirely suspect. While there are some genuinely insightful ideas and arguments, they are often mutually contradictory, and the signal to noise is terrible.

Chris points out the importance of reading a decent translation of the original sources to gain enough insight to benefit from the arguments, and in this I defer to his experience. There certainly are some historical philosophers who seem to make a lot more sense to me than the others. I had not expected to have to pick and choose based so much upon my own personal preferences, so in some way my frustration here is perhaps indicative of an expectation mismatch.

As for Think, as I continue to read, it has slowly dawned on me that perhaps this impression is actually because the author himself is an appalling philosopher (sorry Simon!) Every few pages he makes what seems to me like a terrible logical blunder. If this were correct, that would presumably make his interpretation of historical philosophers equally suspect, and perhaps this is why they so often seem simply ludicrous to me.

I realise it is arrogant and bonkers for me to assume I can outclass the author, a man apparently of considerable knowledge and experience, at his own game. But this is how it seems to me, so what am I to make of that?

It seems to me that there are only three possibilities:

  1. Professor Blackburn's thinking is entirely defective. I am sure this cannot be the case. He is a professor of his subject, his book, while quite understandably not comparable to Russell, has garnered great reviews on Amazon and in the press, for whatever that's worth. As a layman I shall accord him the deference that is entirely his due.
  2. My own thinking is entirely defective. This is, of course, entirely possible, however distasteful it may be to me. How would I know? In defence of my sanity, I note that I seem to get by, more or less, in everyday life. Or at least, I don't seem to be any more confounded by the state of the world than the people around me.
  3. Perhaps, then, the problem lies not solely with myself, but is shared by us all. Perhaps human reasoning as expressed in the English language is generally not sufficiently precise, well-defined or powerful to support consistent philosophical arguments of the type that are being put forward. More generally, we aren't remotely as good at thinking as we think we are. Pretty much I'm saying that (1) and (2) are both true, but that both the professor and I can derive some comfort from the idea that the same applies to pretty much everybody else.

The Method

There is, of course, a method for dealing with the unreliability of our thoughts and perceptions - the scientific method, in its many forms and manifestations. I have friends who claim that the scientific method came out of philosophy, and while I have some reservations about the truth of this in the day-to-day reality of individual practitioners, of course in an abstract sense I can see that this lineage is real.

Application of the scientific method would be the normal way out of the morass of imprecise thoughts that, to date, characterise the majority of philosophy for me. Some aspects of ancient philosophy have proven tractable in this regard. These are the subjects that have subsequently calved off from philosophy to form whole new fields and sciences.

Of course, this has the curious effect of diluting the perceived value of philosophy. As soon as the application of the scientific method lets thinkers actually gain any traction and start to make real progress on their topic, we immediately snip that subject off from the realm of philosophy and give it a different name. Astronomy and cosmology, biology and psychology, chemistry, physics, and many more. Philosophy proper, then, consists of the left over bits - the topics upon which the scientific method has not proven applicable, and hence upon which no meaningful progress can be made.

Chris, and discussion with friends, has helped me see that in this dismissal, I was overlooking the value of philosophy as an incubator for other fields. This is of course critical to the development of human civilisation, and of interest to historians of the respective fields.

What it isn't

What I'm left with is the impression that my minor experiences of philosophy would have been much improved if my expectations had been very different at the outset.

My layman's impression of philosophy from the outside was that it claimed to be the study of tremendously insightful reasoning done by some of the most powerful minds in history. This expectation was naturally bound to lead me to be disappointed when my initial foray led me to discover that many philosophers, particularly ancient ones, distorted as they are by translation and interpretation, cultural mismatch, and lack of my modern knowledge of the world, appear to my sensibilities to be pretty poor. I hope to improve on this situation by taking on board Chris' advice about choosing particular philosophers who resonate for me personally, together with the idea of studying particular translations of original works, rather than quick summaries.

In addition, I had an expectation that all this intense thought might lead to some more definitive and objective conclusions. However ancient philosophers acted without the benefit of the scientific method, and modern philosophy is pruned of topics to which the scientific method is applicable. As a result, there is much bad mixed in with the good. Even the good parts cannot be trusted as a guide to any sorts of truth, since choosing 'the good parts' seems to be a subjective process.

This seems inevitable, since without the benefit of the scientific method, there is no utility function to be used as feedback, by which ideas could be judged and one school of thought meaningfully compared against another. There can be no definitive winners or losers, and without competition, ideas cannot improve by evolution. Instead, each contributor adds their own voice to the mutually contradictory babble, and it is left to the reader to sift through for the parts of value.

The lack of an objective means to prune the discussion of its less worthy branches mean that many philosophers are deified, their writings sacrosanct and studied for generations, even in cases where they were clearly absolutely wrong. (Plato's thoughts about the shapes of atoms, for example.) I accept that study of historical and ancient philosophers is valid for the perspective it gives on the process of philosophy, and as a study of the history of philosophy itself. However, I am appalled that so much of their actual, specific writings are studied and discussed in so much detail, in the same way I would be mortified by the prospect of scholars spending centuries reviewing the specifics of what me and my friends said in our philosophical discussions in the pub. There may be some value in there, but I came to this expecting a précis of the best minds in the world, not a mishmash of sometimes incoherent ideas.

The Upshot

I've done a lot of bitching, but don't take it as a wholesale rejection. In addition to the things I complain about, there are also a bunch of really great, stimulating ideas. I just didn't expect to have to do so much searching and interpretation to find them. Rather, interpret the above as the painful transitions in my understanding of what philosophy is, which parts of it are likely to be of value to me, and what I should expect from it. Thanks to Chris for his significant contributions towards my continuing education on this.

Programming on Windows : Use Console

Developing software on Microsoft Windows is a bloody awful experience compared to the boyish flamboyance of Macs or the mad scientist thrill-a-minute of wrestling Linux into shape. But fear no more, you no longer need to hide behind that fullscreen IDE, pretending that the rest of your OS doesn't exist. Help is at hand.

I often feel that with just a dozen small modifications, the UI of Microsoft Windows could be perfectly usable for me. However, successive releases of Windows seem to steadfastly ignore the minor changes I'd love to see. If anything, they move relentlessly in the opposite direction with every release.

This is the first in a series of tweaks to work around some of these deficiencies, to create something approaching a sane software development environment on Windows. If you are a *nix or command-line jockey, this might make your visits to Windows-land more comfortable. If you're a Visual Studio devotee, this might add some alternative tools to your repertoire.

My idea is that each post will be small enough that you can implement the change it suggests immediately, right now, as you're reading it.

The Console

The console, by which I mean the black DOS 'Command Prompt' window, is the pivot around which the rest of your software development activities rotate. Or at least it could be, if it was any good. The built-in Command Prompt, however, is absolute garbage. Replace it with the poorly-named 'Console' project from SourceForge:

Once installed, you could fiddle around with the settings endlessly, or just copy my console.xml config to your home directory:

console.xml (config for Console)console.xml

after which Console should look something like:

Console: It doesn't look like much

Console provides a few superficial benefits that prove to be indispensable in everyday use:

  • Cut and paste is no longer an unimaginable pain in the ass. What a concept. My config automatically copies selected text to the clipboard, so you don't even need to hit Ctrl-C. Because of this, I've reverted the action of Ctrl-C to its more venerable usage of 'kill the running command'.
  • Selecting text with the mouse now works sanely, although sadly you can't double/triple click to select words or lines.
  • Multiple tabs. My config defines keys: Ctrl-T (new), Ctrl-W (close), and Ctrl-tab (next).
  • PageUp and PageDown scroll, without needing to move your hand to the mouse. My config actually uses Shift-PageUp and Shift-PageDown, so that it's like old XTerms that my fingers apparently still remember.
  • We can choose any font. I like Deja Vu Sans Monospace, no doubt you like something totally different.
  • We can resize the window however we like.
  • Transparency. I don't like it, some people do. (Update: Redacted. I've been converted. Now I love it.)

The old built-in Command-Prompt will still pop up if you double-click a .bat file - I don't know how to change this magic built-in association. Otherwise though, you can now just use the new Console for everything you do.

Alright. We've taken our first step. Next up, we'll see some of the uses to which we can put our new toy.

Making a wix installer run a program by default

Guest post by my least favourite colleague, Tom.

Normally - but not always - people want to run a program just after the install it - you can help then to do this by using an opt-out system with a checkbox checked by default

To create the checkbox and control whether it is set you must use the following properties in your Product section:

Value="Launch the cool program"

The value of WIXUI_EXITDIALOGOPTIONALCHECKBOX controls the checkboxes checkedness.

You then need a custom action to do some launching:


and some UI wiring in the UI element under the Product element to get you custom action called.

NOT Installed

Huzzah - you now may have a working installer.

Python Deployment Sucks

I don't want to whinge just for the sake of whinging. But I think it's healthy to assess something I currently find difficult, that I might better understand where the deficiencies lie and how they might be addressed.

Deploying a Python program to fellow programmers is easy - just send them the script, they can install the interpreter if they haven't already, and figure it out from there. Use of PyPI helps with this.

But distributing Python as source code is only suitable when distributing to other programmers. For distributing to end-users, it is totally inappropriate. They have no idea what Python is nor whether it is installed, and they should never need to know. While it is arguably possible to lead a user through the process of installing Python so that they can run your script, it is an absolutely ghastly, terrifying experience for them, and they will never willingly use your software again. They want an icon they can double-click on which just runs, period. Considering the potential complications such as requiring side-by-side installations of multiple versions of Python, I don't see this as a workable solution for any serious software to be used by non-programmers.

Thus we have the projects to convert Python source into stand-alone executables. I've used py2exe and bbfreeze, and both do a brilliant jobs at what they do. But there are problems with the use of such tools.

With a compiled language, you can simply give the binary to someone with the right operating system, and they can double click it to run it. Better still, this process of compiling and linking the executable for an end-user is the exact same process that you perform all the time for yourself while you are developing the software. There is no additional overhead required to create an executable for end-users (presuming the simplest case of end-users on the same operating system that you are developing on.)

With Python this is not true. The whole 'create a distributable binary' step is an additional headache over and above whatever effort you've already gone to to make the program run in your own development environment. Because of this, in a very pragmatic sense, deployment of Python programs requires a whole extra set of work that deployment of compiled programs does not.

Presumably this is true for all interpreted languages. I've never worked seriously with one before. I should go read about how Ruby and more venerable languages approach and solve these problems.

This situation would not be quite so bad if there was a clear way to proceed, but for me, my first few binary distributable projects have been a real ramshackle hack through the docs of py2exe and the various projects which compete with it. Lots has to be figured out to choose a tool, and then to get it working with your project. This has to be done over for each OS you choose to deploy on. For me, it has been a real time-sink, and I'm still not really happy with the results.

The resulting executables are bulked out by including the interpreter with each one. This is not a suitable technique for including a bunch of lightweight command-line executables to augment your shell -something which, otherwise, you would think Python should be absolutely ideal for. I guess in some circumstances you could work around this somewhat by using an svn-style 'one executable, many commands' interface, although this isn't always suitable.

I'd prefer it if, instead of bundling an interpreter, py2exe looked to see if a suitable version of Python was already installed, and if not, downloaded and installed it side-by-side with any existing installations and then used it to execute your program.

I can't figure out how to tweak the output from py2exe such that my executable file isn't buried in a menagerie of various other .dll, .zip and .exe files. An end-user would have no idea what to click on. I want to make it easy for my users, by creating an executable by itself, with a subdirectory of binary dependencies (and a different subdirectory of data.) I could sidestep this by creating an installer that creates a shortcut to the relevant executable - but again, that would be yet another day of needless make-work, which has to be undertaken over again for each OS you plan to deploy on.

For large projects, these sorts of problems are surmountable - they have enough man hours to soak it up. But for small projects and one-off scripts, problems like this burn up a substantial proportion of time. Writing a hundred-line script to solve your friend's particular problem and emailing him the binary is awkward to say the least. Entries to the PyWeek 'game in a week' contest are substantially impacted - producing those binaries burns up hours and hours, when you have precious few to spare, and very few of the presumably 'average developers' taking part managed to create binaries that just worked for everyone.

In Python's favour, many of the wrinkles and complications that arise during the process are due to cross-platform issues. Getting hold of binary dependencies for other operating systems, stuff like that. The only reason this seems harder in Python than it is in other languages, is that when using other languages you often don't even attempt to deploy across multiple operating systems. The apparent difficulty of doing this in Python is in actual fact just an illusion caused by being able to attempt it in the first place.

Doubtless some of my other perceived problems lie in my own misunderstandings. Nobody else seems to struggle with this as much as I do. Comments are welcome.

Brought to you from the a-crap-post-is-better-than-no-post-at-all dept.

Copyright and the crowdsourcing of promotional materials

There was a rumour that Facebook was allowing third-party advertisers to use user's photos in promotional materials. I imagine a photo of me and my friends enjoying Bacardi responsibly being scraped for use in an advert.

It turns out that rumour is false. But it's not totally groundless - it's based on the fact that some third-party Facebook applications were behaving in this way. Facebook put a stop to it, and are to be commended for that. In the general case, though, it's entirely believable that this sort of thing may still happen, on Facebook or elsewhere, and it's fascinating for me to think about why I personally have an objection to it.

I mean, if some company does use your photo in their promotions, then of course some people would feel this is a privacy issue simply because they are shy about having their personal photos widely disseminated outside their immediate social circle - pasted up on billboards or whatever - and they have a negative emotional reaction to that, and that's understandable.

Many people, however, would instead have a positive emotional reaction. If my photo of me and friends enjoying Bacardi was chosen to be used in an ad, it would actually be kinda cool and funny. However, I *still* would object to them doing it. Even though I'm not losing anything on this deal, and in fact I'm gaining an amount of amusement and notoriety, I still don't want Bacardi to reap the benefit. Why is that?

For me, it's this: Bacardi, who incidentally, have done nothing wrong - they are my hypothetical example. In fact, let's just call them BigCorp instead. BigCorp expect to be able to use my photo, without reciprocating in kind. If I had tried to use BigCorp's images or music or logo, for my own purposes, and it had come to their attention, then they would have sued me into the ground.

It may be that they had little choice in the matter. That they were merely acting in a way that they perceived the prevailing legal and commercial environment obliged them to do. Regardless, the upshot from my perspective is that they would have sued me into the ground.

And that isn't cool. There is a groundswell of resentment in me for the way our cultural heritage - all the music and t.v. and movies and images and adverts and logos that surround us, the things that have come to form our whole cultural milieu, have been prised from our fingers, by gradually increasing legal boundaries, such that none of us as individuals, owns it any more.

Copyright used to be of limited scope. It was designed to prevent the wholesale appropriation of creative works such as books, which were being copied and republished and sold by third parties unconnected with the author. This is a fairly obnoxious behaviour, and one I'm prepared to condemn, and I accept that introducing copyright to prevent it seems like a good idea.

However, since the 1970's the scope of what copyright is applicable to has expanded and expanded, way beyond its original remit. Instead of being triggered by the commercial activity of a large book (re)publishing entity, it is now triggered and invoked by the tiniest of non-commercial personal behaviours, not just publishing books, but giving a song to a friend, or drawing a comic for your blog in your bedroom that incorporates the distinctive likeness of a character you like.

Things that used to be common and socially acceptable (eg. giving an album you like to a friend) are now clearly illegal and can get you sued for your life savings plus draconian life-changing conditions. We can no longer give the songs we like to our friends. We can't post parts of our culture on YouTube. We can't put it on our blogs, or use it in any way we feel like. We have been unwillingly converted into pure consumers of culture, instead of participants. We have been robbed of something we used to have.

I strongly agree with Banksy, who feels that if any corporate image or logo or advert is shoved into my face in a public space (e.g. billboards, but also tv or on the web), then it is, from that moment on, mine to do with as I wish. If I wish to appropriate the image, or augment it with daubings of my own, then why should anyone have the right to stop me? (*huge separate discussion reqd here to justify this, obviously. Maybe later.)

But this, obviously, has been utterly repudiated by the copyright industry, acting in concert with BigCorp lobbyists, has used legal strong-arm tactics to deny me the ability to so much as sing happy birthday in my own restaurant without paying royalties, never mind ripping off BigCorp advertising materials to form my own pastiche. I resent that. I've been putting up with it for years, and in return BigCorp has earned my ill-will on this topic.

So in answer to the question "Can Facebook's partners use your photos for promotional purposes? It'll be funny and cool!" I have to reply "No you can't. I agree that it would be kinda funny and cool, but on this topic you have seriously annoyed me. We are not friends. You cannot use my photos. Now piss off."

Home media center

I just bought a NetGear ReadyNas Duo to connect hard drives to my home network, to stream movies and the like to our fabulous Xbox Classic media center. In the process of researching, I was wondering whether the kind of hard drive connection matters. I mean, if you plug USB hard drives into a device like that, does it run fast enough to stream one or more movies simultaneously? How many simultaneous movies or audio streams would your average home ethernet carry? My first stab at answering these questions are below.

On the left are various network and hard drive connection technologies. On the right are various uses to which I might want to put them. You can't use a slower connection (eg. bluetooth) to drive a faster usage (eg. blu-ray quality movies). Centre column is the data rate in megabits per second (Mb/s):

EDGE mobile phone    0.23
                     0.3  cd audio
bluetooth1           0.7
                     1.3  minimal video
bluetooth2           2.1
wifi 802.11b         4.5
                     5.0  dvd mpeg-2 quality
ADSL1                8.0
ethernet 10baseT    10
USB1                12
                    15  hdtv video (from 8 to 15)
ADSL2+              24*
cable modem         30
                    40 blu-ray disc
wifi 802.11g        54
firewire800 act     65
ethernet 100baseT  100*
PCI                133
USB2 actual        240
firewire 400 theo  400
USB2 theoretical   480
wifi 802.11n       600
firewire 800 theo  800
Seagate Barracuda  960*
ethernet gigabit 1,000
SATA-150 theo    1,500
SATA-300 theo    3,000

* = my setup

I'm assuming that I don't have gigabit ethernet, because I've never paid it any attention in the past. Judging from the above, my 100BaseT should be more than adequate, but will be the weakest link. So that'll be the first thing I look at if streaming seems sub-par. Coolio!

Update: Everything works swimmingly. I've had no problem with streaming speeds. Problems *have* occurred with some .avi files which appeared to have invalid interleave cross-stream differential parity (or something) and efforts to reverse their polarity were to no avail (transcoding software generally wouldn't even read the files!) A quick visit or two to MiniNova fixed all that.

A Pythonic 'switch' statement

I've recently had the pleasure of providing some assistance to my lovely wife through her first serious Python coding, and one of many things she expressed surprise at was the lack of a 'switch' statement. At the time, I advised her that such a statement is superfluous, and that she should simply use an if...elif...else instead. I then forgot all about it.

Until today, when I found myself refactoring a Pythonic kind of switch into my own code.

I started with this ugly little lump:

def convert(self, params):
    action = params[0]
    if action == 'M':
        x, y = self.get_point(params)
        current_path = [(x, y)]
    elif action == 'L':
        x, y = self.get_point(params)
        current_path.append((x, y))
    elif action in 'zZ':
        if current_path[0] == current_path[-1]:
            current_path = current_path[:-1]
        if len(current_path) < 3:
            raise ParseError('loop needs 3 or more verts')
        current_path = None
        msg = 'unsupported svg path command: %s' % (action,)
        raise ParseError(msg)

This is from the guts of an SVG parsing module I was hacking up, but what it actually does isn't important. Its only salient feature for today is that it consists of a big switch-like if...elif...else statement. I was going to be adding plenty more cases to this logic, and it was sure going to get ugly. How can we make it better?

First, I extract the logic from each branch of the if into functions. In this case, I chose to make them methods of the current class. Standalone functions (outside the class, without a 'self' parameter) would also work, if they didn't need access to shared state.

def onMove(self, params):
    x, y = self.get_point(params)
    self.current_path = [(x, y)]

def onLine(self, params):
    x, y = self.get_point(params)
    self.current_path.append((x, y))

def onClose(self, params):
    if self.current_path[0] == self.current_path[-1]:
        self.current_path = self.current_path[:-1]
    if len(self.current_path) < 3:
        raise ParseError('loop needs 3 or more verts')
    self.current_path = None

def onBadCommand(self, action):
    msg = 'unsupported svg path command: %s' % (action,)
    raise ParseError(msg)

Again, don't worry too much about what these functions actually do. Just note that I've pulled the logic out of each branch of the if...elif...else statement into separate handler functions.

Second, I define a dictionary which maps action characters to one of the new handler functions:

def convert(self):
    lookup = {
        'M': self.onMove,
        'L': self.onLine,
        'Z': self.onClose,
        'z': self.onClose,

Notice how the methods are bound to self, so they operate on the current object as you would expect. If you used stand-alone functions instead, they would not need any 'self.' qualifier here.

Third, use the dictionary to lookup the function we want to call, and then call the returned function:

handler = lookup[action]

These two lines can be tidily combined into one:


Note that this is pleasantly succinct, but still very explicit about what's going on. We're looking up a value in a dictionary, using the d[key] syntax. The returned value is a function, and we are calling it, passing 'params', using the f() syntax.

Python tries very hard to always clearly expose the details of what is happening to the reader like this. Nothing magically happens behind the scenes. And yet, by the good judgement of Guido and the healthy process that surrounds the language's evolving design, the code remains concise, without becoming verbose or cluttered with obfuscating punctuation.

We haven't yet handled the final 'else' clause from the original code. It can't simply become another entry in our lookup dictionary, since it's unclear what key (left-hand value) would go into the lookup to correspond to this case. We're really talking about the case when the 'action' character can't be found in the lookup dictionary at all. The most explicit and readable way to handle this case is to modify the above line of code:

if action in lookup:

Saving these changes, running the tests shows it behaves identically to the original version. (Hint: Tests don't make code harder to change. Quite the opposite - tests enable more frequent and more intrusive change, by giving you the freedom to dabble with refactoring while remaining dead certain you aren't introducing new bugs.)

Let's take a look at the final code all together:

def convert(self, params):
    lookup = {
        'M': self.onMove,
        'L': self.onLine,
        'Z': self.onClose,
        'z': self.onClose,
    action = params[0]
    if action in lookup:

Including the new handler functions, this is considerably longer than the original version (19 vs 32 lines). However, it qualifies as preferable and 'more Pythonic' for a couple of reasons:

  1. It's much clearer in intent. Greater readability is introduced by separating out the code which chooses what to do (the lookup dict) from the actual doing (the new handler functions.) The naming of the new handler functions brings enormous clarity at a stroke. Of course, this could also be done with a switch statement, and frequently should be.
  2. It's easily extendible. The if..elif...else construct of the first version would soon have bloated to over a screen-full of garbled code when we added a few more cases. The new version could add 100 or so new cases without really becoming much less comprehensible.
  3. It's data-driven. The lookup structure could be generated by other means than simply hard-coding it locally like this. We could merge several dictionaries depending on context, or create it on the fly from application configuration.

This isn't very Earth shattering, and of course the idea that I should be preferring polymorphism over switch-statements tickling the back of my mind, but hopefully someone finds it useful.

Update: Wow! What a flurry of completely brilliant comments - every single one contains something of real merit. I feel compelled to rummage through for a sort-of best of breed conclusion based on all of them...

lookup = {
    'M': self.on_move,
    'L': self.on_line,
    'Z': self.on_close,
    'z': self.on_close,

def convert(self, params):
    action = params[0]
    handler = self.lookup.get(action, self.on_bad_command)

# or alternatively
def convert(self, params):
    self.lookup.get(params[0], self.on_bad_command)(params)

I marginally prefer the first version - the second alternative is a smidgeon too compact for my taste. I respect the idea to use exceptions, that makes a lot of sense too. Thanks for all the great ideas, everyone.

IronPython in Action

IronPython in Action cover

by Michael Foord and Christian Muirhead (2009)

Disclaimer: I'm friends with both the authors and was sent a freebie review copy by the publisher, so I'm bound to be breathlessly gushing in this review. Fortunately, that's easy to do, because the book really is great. (Except for Christian's chapters... Joke!)

Having spent some years working with .NET, and with a series of intriguing personal experiments in Python under my belt, I originally approached IronPython some years ago with a modicum of trepidation. I feared that the weld between the two would be intrusively visible, forming distracting differences from regular Python. I feared for the execution environment, the data types, and perhaps even the syntax itself.

Experience with IronPython showed these worries were needless. I have found IronPython to be a remarkably pleasant marriage - the same elegant language we know and love, given first-class status in the .NET runtime. Gifted with seamless interoperability with other .NET languages, the dowry from such an alliance turns out to be all the .NET libraries in the world, including the substantial and highly capable .NET standard libraries themselves.

IronPython is, to some extent, a niche implementation of a niche language. However, its position seems to potentially be one of importance and strength. Not only does it allow Python programmers to use .NET libraries - and does so admirably, but it also allows the existing legions of .NET programmers to be introduced to the joys of Python. They will fall in love with it, and will be able to introduce it into their workplaces in a way that is politically acceptable. After all, it is now simply another .NET language. Since .NET is orders of magnitude more popular than Python, this could turn out to be an important source of future Python adoption.

This book is aimed to satisfy programmers coming from both the Python and the .NET worlds, and in this it seems to succeed. It starts with quick overviews of concepts from each: 30 pages about Python as a language, and 17 pages about .NET as an environment (data types, events, delegates, Windows Forms, etc) - just enough to get everyone up to speed regardless of background, but without being so verbose as to turn anyone off with a surfeit of material they are already familiar with. Despite being brief, these sections are packed with detail and very pragmatic, focusing on real-world use such as inheriting from existing .NET types, and solving some common problems like creating Windows Forms applications from IronPython.

This style of practical and dense informative content is continued throughout. Straight after the opening sections, we dive right in with another rapid-fire chapter, demonstrating common IronPython techniques by writing a non-trivial application. Woven around this ongoing example, the chapter discusses many immediately important topics, including duck typing, Python protocols, MVC, using Windows Forms to build a GUI, tab pages, dialogs, menus, toolbars, images, saving text files, .NET Streams, text file encodings, Python exceptions and lambda functions. These diverse topics are covered rapidly but thoroughly, giving the reader enough information about each to be able to use them together from IronPython to create a useful project.

Having covered these foundations, the book then moves on to address some specific areas in more detail. The following chapter headings give you some idea of the topics which are explored in depth:

  • First-class functions in action with XML - demonstrates pragmatic use of functions as first-class objects, and higher-order functions (functions that take other functions as arguments and return modified versions.) and of course decorators. These are shown in use, appropriately paired up with the .NET XmlWriter and XmlReader classes, demonstrating event driven parsing of XML.
  • Properties, dialogs and Visual Studio - Python properties, .NET dialogs, and using IronPython in Visual Studio. This sounds like a straightforward chapter, but as you might guess, the book gets deep into the topics and is jammed full of information. By the end of the chapter you'll have added to the example application to create document observers, used BinaryFormatter to serialise objects, and touched on Python's pickle equivalent.
  • Agile Testing: where dynamic typing shines - From the unittest module and creating tests, through mock objects, listeners, monkey patching, dependency injection and functional testing. This is a dense chapter in a dense book, touching along the way on Python attribute lookup rules, bound and unbound methods, asynchronous execution for functional testing. My only criticism is that it's clearly hard for developers to 'get' testing until they have hands-on experience of it, so this single-chapter, while very thorough in explaining how to test, has an ambitious remit, and doesn't have enough space to explain much of why we test. I guess this is partially my own bias shining through here - I regard testing as quite literally the most important thing to happen in computer science since the invention of the compiler, and would encourage anyone interested to go and read as much as they can about it.
  • Metaprogramming, protocols and more - More Python protocols, dynamic attribute access, and metaclasses. The sorts of things that in a static language would be deep black magic, or else completely impossible, but here they are just the right sort of crazy. Read, enjoy, and unlearn. We see how to create a profiling decorator, that modifies the functions you pass to it, wrapping them in stopwatch timing calls. We also learn about some of the more advanced integration of IronPython with the .NET CLR, including static compilation of IronPython code into assemblies, and one of the very few additions to Python syntax that IronPython has been obliged to provide - the typing of .NET arrays and generics. You'll never need to use generics yourself (in Python, everything is a generic), and you'll never want to go back to typed containers if you can avoid it. However, you may need to deal with some from an existing C# API, and this is how you do it.

Whew! We're only halfway through! The remaining chapters are equally detailed, but I'm going to start skimming through them somewhat. They cover the interactions of IronPython with more advanced .NET topics such as:

  • Windows Presentation Foundation (WPF) and IronPython - WPF is the DirectX user interface library that is a successor to Windows Forms. This includes XAML, an XML dialect for describing user interfaces, decoupling their implementation from application logic.
  • Windows System Administration with IronPython - using IronPython as a scripting language for sysadmin automation tasks, from the simple, such as copying files, to the complex, such as Windows Management Instrumentation (WMI), administration of remote machines, and a substantial discussion on the uses of PowerShell with IronPython.
  • IronPython and ASP.NET - building a web-based front end to the sample application developed earlier. Reusable controls.
  • Databases and Web Services - using ADO.NET to work with databases, and using SOAP and REST.
  • Silverlight: IronPython in the browser - creating Silverlight applications, and accessing the browser DOM from them.
  • Extending IronPython with C#/.NET - all about creating C# class libraries for use in IronPython, calling unmanaged code from IronPython, and creating interfaces on your C# classes to provide dynamic, Pythonic behaviour. It also includes dynamic compilation of assemblies at runtime, which opens the door to advanced code-generation techniques.
  • Embedding the IronPython Engine - many developers might want to provide IronPython as a scripting language within their own application, and this chapter shows you how.

Alright, that's it! There are appendices:

  • A whirlwind tour of C# - in case anyone wants more guidance while looking at some of the C# code or concepts that are discussed throughout the book.
  • Python magic methods - a description of all the Python magic double-underscore methods, which is a fabulous resource, one which l haven't seen collected anywhere else, and have been referring back to ever since I read the book.

So there you have it. If you haven't inferred already, I learned absolutely heaps from this book, even though it's about a language and environment I've been using every day for years. I think I can say without any equivocation that this is the best IronPython book in the world. If you're a .NET person who is curious about Python (and believe me, you should be), or if you're a Python person who fancies .NET - maybe for DirectX or Silverlight or any number of other wonderful things, then you should absolutely go directly to the IronPython in Action book website right this second and buy it.

What are you still doing here?

Update: Good catch Carl, I forgot the all-important rating!

10/10 if you already use, or are curious about using, IronPython - then you need this book.

0/10 if dynamic languages make you break out in hives, or if .NET makes you think of Darth Vader, then you shouldn't touch this book with a barge pole.

On Estimates

There is a lot of room for miscommunication about estimates, as people have a startling tendency to think wishfully that the sentence:

I estimate that, if I really understand the problem, it is about 50% likely that we will be done in five weeks (if no one bothers us during that time).

really means:

I promise to have it all done five weeks from now.

from How to be a Programmer, by Robert L. Read.