Wednesday, October 20, 2010

Switchpy

One of the consequences of the 2.x-to-3.x Python changeover is that I need to keep both versions around for a while on my Windows dev workstation.

Actually, strike that: I need to keep many versions around:
  • 2.5.4, because that's the earliest version we support at work for some internal tools
  • 2.6.6, because one particular internal tool jumped the gun and started using the "with" statement before we migrated to...
  • 2.7, because that's what we're migrating those internal tools to (slowly)
  • 3.1.2, because that's what we're targeting for new development
  • A "special" 3.1.2, which mimics the version we've modified for use in our embedded devices
  • The most recent 3.2 alpha, for testing
  • A 3.2 trunk install, for testing patches
Virtualenv doesn't exactly do what I want: you have to install it from within an already-installed version of Python, and it doesn't support Python 3 yet (although there is a fork that does). Plus it doesn't handle anything other than environment variables--it doesn't understand Windows' defaults.

Ned Batchelder wrote a neat script that does some of that, but again, it doesn't handle everything.

So starting from Ned's script, I came up with switchpy:
  • Supports Windows Python versions from 2.5 up to 3.2
  • Changes the local PATH environment in the current shell (via the same batchfile trick as mpath)
  • Updates the Registry-based associations (via code from Ned's script)
  • Pings Explorer so that if you run "python.exe" from the Start | Run command, it notices the update
  • Automatically reads installed official versions from the Registry, so you can say "switchpy 31" instead of "switchpy c:\python31"
So now, testing scripts in multiple versions of Python is as easy as:


C:\src\myscript>switchpy 25
Switching to Python at C:\Python25\...
Python is now C:\Python25\

C:\src\myscript>py.test
============================= test session starts =============================
python: platform win32 -- Python 2.5.4 -- pytest-1.3.0
test object 1: C:\src\myscript

myscript\tests\test_script.py ...

========================== 3 passed in 0.03 seconds ===========================

C:\src\myscript>switchpy 31
Switching to Python at C:\Python31\...
Python is now C:\Python31\

C:\src\myscript>py.test
============================= test session starts =============================
platform win32 -- Python 3.1.2 -- pytest-1.3.1
test object 1: C:\src\myscript

myscript\tests\test_script.py ...

========================== 3 passed in 0.03 seconds ===========================




For now, you can find switchpy in the same bitbucket repo as mpath; if I add any more scripts, I'll probably end up making it a more general repo.

Monday, May 10, 2010

Language of the Year

Several some many years ago, I decided to learn a new programming language a year. I don't subscribe to the "once you're a programmer, you can learn any new language in a few weeks" theory, so I didn't intend to get to guru status on any of them in 52 weekends and a few nights. Instead, the idea was to get good enough to be able to read code well, synthesize code at a decent level of competency, and understand the ideas behind the language, then move on.

If memory serves me correctly, I started with Java, then moved through REBOL, Python, Ruby, Scheme, and Lua before I dropped the practice. I tried getting back into it with Haskell a few years ago, but didn't take it to the "ok, I get it now" level of proficiency--mostly because of time constraints of changing jobs rather than any issue with the language.

I did learn something from each of these, which is the real point of the exercise. Now I'm thinking about starting a (belated) Language of the Year for 2010. The contenders are:

Erlang
Pro: interesting concurrency (independent processes) and data (immutable) models.
Con: might be too big to get my head around in nights and weekends

Io
Pro: Small surface area; looks like "Lua with a prototype-based object model" so far; might be useful practically.
Con: Not sure there's enough new there after doing Lua and REBOL.

Clojure
Pro: Interesting transactional memory model. Also, parentheses.
Con: I'm wondering how much time I'll spend re-learning the Java environment vs. learning Clojure.

Go
Pro: Interesting concurrency model; might be useful practically.
Con: Not sure there's enough different there, compared to Clojure or Erlang.

Part of the problem is picking a language that different enough to make it worth learning, but not so different that I can't use it for small, practical tools that I can actually use day-to-day. The Haskell and Scheme experiences showed me that if I can't use what I'm practicing day-to-day, I find it hard to keep devoting time to the project.

At the moment, it's looking like Clojure > Go > Erlang > Io. I'll probably pick up a distribution for each, get as far as "hello world", and then decide.

Tuesday, February 16, 2010

Mpath: command-line path manipulation for Windows

I'm a command line geek. Windows' style of installing everything in its own directory makes it easier to clean up after uninstallation, but it makes for very long PATH environment variables. If I put every directory containing command line tools in the system path, it gets too long for Windows to handle. So I usually end up doing "PATH=%PATH%;c:\somethingelse\bin" just before I use it. That also makes for long paths over long runtimes, especially when you use it in batch files (since you end up with PATH=c:\somethingelse\bin;c:\somethingelse\bin;c:\somethingelse\bin;[rest of path] after multiple invocations).

So I wrote mpath. Mpath is a combination batch file and Python script that takes advantage of some quirks of the Windows command shell, to let a child process alter the environment of a parent command shell process (something that you typically can't do in win32, but mpath gets around it by creating a temporary batch file that gets executed in the parent process).

Syntax:

  • mpath pathname : prepends pathname to the current command shell's PATH, if it doesn't already exist.
  • mpath + pathname : appends pathname to the current command shell's PATH, if it doesn't already exist.
  • mpath - pathname : removes pathname from the current command shell's PATH, if it exists.

A quick demo:

C:\> PATH=C:

C:\> PATH
PATH=C:

C:\> mpath c:\foo (prepend c:\foo to the path)

C:\> PATH
PATH=c:\foo;C:

C:\> mpath - C:\FOO (take it off the path--note case insensitivity)

C:\> PATH
PATH=C:

C:\> mpath + c:\foo (append c:\foo to the path)

C:\> PATH
PATH=C:\;c:\foo

C:\> mpath c:\foo (try to prepend it again--mpath knows it's already there)
c:\foo already in path.

C:\> PATH=%PATH%;c:\foo (silly user should have used mpath...)

C:\> PATH
PATH=C:\;c:\foo;c:\foo (now there are two copies!)

C:\> mpath - C:\FOO (but mpath takes care of that.)

C:\> PATH
PATH=C:


I've tested Mpath with Windows XP running Python 2.5 and 2.6. I know it doesn't work on 3.x; I plan on fixing that at some point when I need it.



Update: mpath is now tested on 2.5, 2.6, 2.7, and 3.1.