You are here: Home

Two Voices

by Dan Fairs last modified Sep 30, 2010 02:53 PM

Installing GeoDjango with PostgreSQL and zc.buildout

by Dan Fairs last modified Apr 17, 2009 12:06 PM
The installation of the PostgreSQL requirements is somewhat daunting. I've spent a bit of time putting together a buildout.cfg to try to make this easier.

I've been wanting to play with GeoDjango for a while, since my database of choice (PostgreSQL) has excellent spatial support. However, getting all the dependencies up and running is pretty complicated.

I've been working on a buildout to get at least most of the steps done for you. There are a couple of manual steps at the end, which I hope to automate when I next have time to work on this.

The buildout installs the following items:

  • PostgreSQL
  • PostGIS
  • GDAL
  • Proj
  • GEOS
  • psycopg2
  • Django

It should also perform initial setup of the PostGIS database template, loading some sample SQL files, and sets up some convenience symlinks for the PostgreSQL command-line programs.

It's not finished - in particular, it just assumes that the user running the buildout is to be used as the database owner and such like. Anyway, here it is:

 

[buildout]
parts =
postgresql
postgis
gdal
init-pgsql
pgsql-symlinks
django

eggs =
psycopg2

[postgresql]
recipe = zc.recipe.cmmi
url = http://wwwmaster.postgresql.org/redir/198/h/source/v8.3.7/postgresql-8.3.7.tar.gz
extra_options =
--with-readline
--enable-thread-safety

[postgis]
recipe = hexagonit.recipe.cmmi
url = http://postgis.refractions.net/download/postgis-1.3.5.tar.gz
configure-options =
--with-pgsql=${postgresql:location}/bin/pg_config
--with-geos=${geos:location}/bin/geos-config
--with-proj=${proj:location}

[proj]
recipe = zc.recipe.cmmi
url = http://download.osgeo.org/proj/proj-4.6.1.tar.gz

[geos]
recipe = zc.recipe.cmmi
url = http://download.osgeo.org/geos/geos-3.0.3.tar.bz2

[gdal]
recipe = zc.recipe.cmmi
url = http://download.osgeo.org/gdal/gdal-1.6.0.tar.gz
extra_options =
--with-python
--with-geos=${geos:location}/bin/geos-config

[init-pgsql]
recipe = iw.recipe.cmd
on_install = true
on_update = false
cmds =
${postgresql:location}/bin/initdb -D ${postgresql:location}/var/data -E UNICODE
${postgresql:location}/bin/pg_ctl -D ${postgresql:location}/var/data start
sleep 30
${postgresql:location}/bin/createdb -E UTF8 template_postgis
${postgresql:location}/bin/createlang -d template_postgis plpgsql
${postgresql:location}/bin/psql -d template_postgis -f ${postgis:location}/share/lwpostgis.sql
${postgresql:location}/bin/psql -d template_postgis -f ${postgis:location}/share/spatial_ref_sys.sql
${postgresql:location}/bin/psql -d template_postgis -c "GRANT ALL ON geometry_columns TO PUBLIC;"
${postgresql:location}/bin/psql -d template_postgis -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"
${postgresql:location}/bin/pg_ctl -D ${postgresql:location}/var/data stop

[pgsql-symlinks]
recipe = cns.recipe.symlink
symlink_target = ${buildout:directory}/bin
symlink_base = ${postgresql:location}/bin
symlink =
clusterdb
createdb
createlang
createuser
dropdb
droplang
dropuser
ecpg
initdb
ipcclean
pg_config
pg_controldata
pg_ctl
pg_dump
pg_dumpall
pg_resetxlog
pg_restore
postgres
postmaster
psql
reindexdb
vacuumdb

[django]
recipe = djangorecipe
version = 1.0.2
project = project
eggs =
${buildout:eggs}

Note that running this will actually attempt to start up and shut down the database server, as it needs to be running in order for some of the initialisation scripts to run. That 'sleep 30' in the middle is to allow the database server to start, and (if you're on OS X and running it) to give you a change to enter your username and password for the firewall!

There are still some manual steps to be taken (which I'd like to automate in due course). These are the fairly standard things that you do when starting any Django project, plus an extra step for bootstrapping PostGIS.

Create your database

From the command line, you'll need to create the database for you application. You need to specify the PostGIS template, so use something like:

$ bin/createdb -T template_postgis <db name>

Change the settings for your application

Edit the settings.py for your application, and make sure that you're using 'postgresql_psycopg2' as the database engine. Set the database name as appropriate for your application. You should also add 'django.contrib.gis' to your INSTALLED_APPS setting, and you'll also need to add the following two lines to your settings.py:

GDAL_LIBRARY_PATH = '/path/to/buildout/parts/gdal/lib/libgdal.dylib'
GEOS_LIBRARY_PATH = '/path/to/buildout/parts/geos/lib/libgeos_c.dylib'

Add Google projection

I'll confess: I'm only doing this because the GeoDjango docs say you should! I don't know enough about GeoDjango yet to understand why. But you should do the following:

$ bin/django shell
>>> from django.contrib.gis.utils import add_postgis_srs
>>> add_postgis_srs(900913)
>>> ^D
$

If you get an error when importing add_postgis_srs, then double check you got the GDAL_LIBRARY_PATH and GEOS_LIBRARY_PATH correct, and that the files specified were built. (I'm on Mac OS X - I suspect the exact file name may change depending on platform.)

Done!

Once all that's done, you should hopefully be able to bin/django syncdb, start a new app (using fez.djangoskel, of course!) and start using GeoDjango.

I shall refine the above process over time (in particular, there are some modifications I'd like to make to djangorecipe to remove the manual steps at the end), and I'll post extra parts when I've done that.

 

VMWare Fusion guests with a static IP

by Dan Fairs last modified Apr 15, 2009 11:48 AM
The article that I followed to get a static IP for VMWare fusion guests seems to have been removed, so in the name of preserving this knowledge I'm reproducing the salient parts here.

It's not straightforward to assign static IP addresses to guests in VMWare Fusion, and the article from which I took my instructions has been removed, and lives on only in Google caches and the like. Since I don't want to lose this information, I'm reposting the technical content here. Thanks to the original author, Gary Day, for his research.

Gary, if you're reading this and want me to link to your canonical original version under a new URL, please contact me and I'll sort it out.

Gary's original content appears below. Apologies for the slight formatting issues, it's a kupu-n-paste job.


Guest Configuration Information

Open a Finder window and navigate to your Virtual Machines folder, probably /username/Documents/Virtual Machines. Locate the VM package for the guest you want to use for this procedure (Virtual Machines are represented by a single file which is a package containing multiple disk and configuration files).
CTRL-CLICK the Virtual Machine and select “Show Package Contents”, this displays the components of your Virtual Machine. Find Guest.vmx (in my case “UbuntuGnome.vmx”), CTRL-CLICK again and open with your text editor (in my case TextMate, can’t live without it). This action will show the default configuration of “UbuntuGnome”. Search this text file for “ethernet0.generatedAddress” and you fill find the following (similar) information:

ethernet0.generatedAddress = "00:0c:29:8b:a4:4f"

This is your Virtual Machine’s “MAC” or Ethernet Hardware Address.
Copy this information to a text file because I doubt most people can remember such things for more than a second or two.

Accessing VMWare Fusion’s DHCP Settings

VMware Fusion’s DHCP configuration file is located in “Application Support”.
Open a terminal and set a command to open this config file in your text editor of choice.

~ user$ mate "/Library/Application Support/VMware Fusion/vmnet8/dhcpd.conf"

This is what you should see:


# Configuration file for ISC 2.0b6pl1 vmnet-dhcpd operating on vmnet8.
#
# This file was automatically generated by the VMware configuration program.
# If you modify it, it will be backed up the next time you run the
# configuration program.
#
# We set domain-name-servers to make some DHCP clients happy
# (dhclient as configued in SuSE, TurboLinux, etc.).
# We also supply a domain name to make pump (Red Hat 6.x) happy.
#
allow unknown-clients;
default-lease-time 1800; # 30 minutes
max-lease-time 7200; # 2 hours
subnet 172.16.27.0 netmask 255.255.255.0 {
range 172.16.27.128 172.16.27.254;
option broadcast-address 172.16.27.255;
option domain-name-servers 172.16.27.2;
option netbios-name-servers 172.16.27.2;
option domain-name “localdomain”;
option routers 172.16.27.2;
}

Note the subnet range, we need to set a fixed address for our Virtual Machine outside of this range.
We do this like so:
Append the open file (dhcpd.conf) with the following, obviously using your own settings including the Ethernet Hardware Address you previously copied to a text file, the name of Guest.vmx and the IP address you wish to assign to this Virtual Machine.

host UbuntuGnome {
hardware ethernet 00:0c:29:8b:a4:4f;
fixed-address 172.16.27.20;
}

Save this file, you will prompted to enter your administrator password as we have opened dhcpd.conf as a read only file in TextMate.

We now need to restart networking for VMWare Fusion:

sudo "/Library/Application Support/VMware Fusion/boot.sh" --restart

Configuring Hosts In Linux Guest

That’s a confusing title I must admit.
Fire up your Virtual Machine..
NOTE:
I am using this Ubuntu Guest as it was a machine already on my system, the following configuration information will differ slightly between distros and interfaces. For my “Micro-Network” experiments I will be using minimal, command line installations of CentOS 5.02 and I will cover this in later posts.

If you are using the Gnome Desktop navigate to:

System> Administration> Network

Use your superuser password to unlock the network applet and select “Wired Connection (Properties)”.
Disable roaming mode (it’s set this way by default on a new Ubuntu installation) and enter the settings for Configuration (Static IP Address).

IP ADDRESS: (The Address You Set In The VMWare DHCP Settings)
SUBNET: (Usually 255.255.255.0)
GATEWAY: (The Address Of The VMware Fusion Server)*

* “option routers XXX.XX.XX.X” in dhcp.conf

Save these settings and restart networking (or your Virtual Machine).

This procedure can repeated for each Virtual Machine you want to add to your “Virtual Network” by adding a host entry for each guest machine in “/Library/Application Support/VMware Fusion/vmnet8/dhcpd.conf”.

BBC iPlayer "...temporarily unavailable. Please try again later"

by Dan Fairs last modified Apr 15, 2009 11:07 AM
I got the dreaded "(programme) is temporarily unavailable. Please try again later" while using the BBC iPlayer desktop AIR app. Fixing it was simple... once I found out how.

The iPlayer is great. Like the PVR, it changes the way you watch television. And so I was excited when the desktop version of the software became available. I was somewhat disappointed to discover it was an AIR app (I've been burned by bad Adobe installers and problems with Flash on non-Windows platforms in the past) but, for the functionality it offered, I was prepared to overlook these problems.

Unfortunately, after a while, it broke. Clicking on a downloaded title gave the unhelpful message: "(programme name) is temporarily unavailable. Please try again later."

OK, so I know the drill about not scaring end users with incomprehensible messages, but please, give us something to go on.

The solution, as it often is, was a simple delete and reinstall. What I didn't realise, however, was that merely deleting the application itself and the downloaded movies folder wasn't sufficient. There are some other directories you have to remove too, which are detailed in this BBC article on removal of the software.

To save you clicking through, the directories that you have to remove in addition to the application are:

  • /Users/[your user name]/Library/Preferences/BBCiPlayerDesktop.61DB7A798358575D6A969CCD73DDBBD723A6DA9D.1/
  • /Users/[your user name]/Library/Application Support/Adobe/AIR/ELS/BBCiPlayerDesktop.61DB7A798358575D6A969CCD73DDBBD723A6DA9D.1/

Perhaps I'm still a Mac newbie, but when I delete an application by dragging it to the trash, I expect all traces of it to go (aside from saved data files in my Documents folder). I've no idea whether this is an iPlayer thing or an Adobe AIR thing, but I would not be at all surprised to discover the latter.

Understanding imports and PYTHONPATH

by Dan Fairs last modified Mar 03, 2009 03:36 PM
An understanding of PYTHONPATH is key when developing new Python modules, or installing third-party packages and eggs. This article gives an overview of PYTHONPATH and the way Python imports modules.

Something I've heard a few times from developers coming to Python from languages such as PHP is that module importing and the PYTHONPATH is a bit of a mystery. I remember understanding PYTHONPATH when I learned Python since I'd done a bit of Java at university (and PYTHONPATH is conceptually the same as Java's CLASSPATH), but several flavours of import confused me. This post covers both; first we'll talk about the import statement, and then we'll cover PYTHONPATH.

Understanding import and from ... import ...

Python has two forms of import statement. They look something like this:

import z3c.form.form
from z3c.form import form

Python is all about binding (or assigning) names to values, and the primary purpose of the import statement is to bind names to modules. The key difference between the two forms above is what names are made available. The first form lets you reference 'z3c.form.form' in your code; the latter lets you reference 'form' directly. Let's examine the first case:

>>> import z3c.form.form
>>> z3c.form.form
<module 'z3c.form.form' from '/eggs/z3c.form-1.9.0-py2.5.egg/z3c/form/form.pyc'>
>>> form
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'form' is not defined

Contrast this with the second case:

>>> from z3c.form import form
>>> z3c.form.form
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'z3c' is not defined
>>> form
<module 'z3c.form.form' from '/eggs/z3c.form-1.9.0-py2.5.egg/z3c/form/form.pyc'>

The only difference between the two cases is the name which is available after the import. (OK, that's a bit of a lie, but we'll gloss over that for now). The rule of thumb is that you will always refer to the bit after the 'import' in following code; so, when you say 'import z3c.form.form' you'll be able to refer to 'z3c.form.form', and when you say 'from z3c.form import form' you'll simply be able to refer to 'form'. In both cases, what you are actually dealing with when you actually use that name (be that 'z3c.form.form' or just 'form') is exactly the same - in this case, the 'form.pyc' module from the z3c.form package.

PYTHONPATH

So how does PYTHONPATH fit into this?

PYTHONPATH is an environment variable, much like PATH. You can get a list of environment variables on UNIX-like operating systems by running the 'env' command. It's available in the Properties of My Computer in Windows. PYTHONPATH is similar to PATH in another way, in that it defines a search path. However, unlike PATH (which tells the operating system which directories to look for executable files in), PYTHONPATH is used by the Python interpreter to find out where to look for modules to import.

This is probably best demonstrated with an example. Let's create a file called hello.py in a directory ~/pymodules:

hornet:~ dan$ mkdir pymodules
hornet:~ dan$ cd pymodules/
hornet:pymodules dan$ emacs hello.py
hornet:pymodules dan$ cat hello.py
def print_hello():
    print 'hello!'

Now, as my current directory is the pymodules directory, I can fire up Python, import my hello module, and run print_hello():

hornet:pymodules dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>> hello.print_hello()
hello!

However, this doesn't work if I'm not in that pymodules directory:

hornet:pymodules dan$ cd
hornet:~ dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named hello

To fix this, we need to tell Python to look in our new pymodules directory for libraries. We do this by setting the PYTHONPATH variable:

hornet:~ dan$ export PYTHONPATH=$PYTHONPATH:/Users/dan/pymodules 
hornet:~ dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>> hello.print_hello()
hello!

The magic is in that 'export' line, which is appending the path '/Users/dan/pymodules' to the environment variable PYTHONPATH. Note how we append, to avoid completely overwriting any existing values.

Special cases and examining the search path

There are a few of 'special cases' to be aware of when thinking about where Python modules may be imported from. The first is that the Python installation's site-packages directory will always be placed on the search path automatically. Secondly, as we saw in the first 'hello' example, the current module's directory is placed on the search path, allowing relative imports (more on this shortly). Finally, the current directory is also placed on the search path.

This begs the obvious question: how can you definitively find out where Python is looking for modules? Well, like this:

hornet:~ dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> from pprint import pprint as pp
>>> pp(sys.path)
['',
 '/Library/Python/2.5/site-packages/virtualenv-1.0-py2.5.egg',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python25.zip',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-darwin',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-mac',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-dynload',
 '/Library/Python/2.5/site-packages',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC']

That's using a my MacBook's system Python in a fresh terminal. As you can see, there's quite a lot of items in there! (Note also the use of pprint, the 'pretty printer' library to format the output nicely). Note that the first item is the empty list (the current directory) - this is what made our very first 'import hello' work, as noted before. Also notice that the site-packages directory has been placed on the search path. Finally, there's a bunch of items on there that Apple set up. You can also see that I have the virtualenv package installed in my system Python.

Contrast this with the list after we've set our PYTHONPATH manually as before:

hornet:~ dan$ export PYTHONPATH=$PYTHONPATH:/Users/dan/pymodules
hornet:~ dan$ cd /tmp
hornet:tmp dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> from pprint import pprint as pp
>>> pp(sys.path)
['',
 '/Library/Python/2.5/site-packages/virtualenv-1.0-py2.5.egg',
 '/private/tmp',
 '/Users/dan/pymodules',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python25.zip',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-darwin',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-mac',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-dynload',
 '/Library/Python/2.5/site-packages',
 '/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC']

The two important things to note are:

  • My changed current working directory (/tmp) has been added to the search path
  • /Users/dan/pymodules has been added to the path, having been read from the PYTHONPATH environment variable.

You can dynamically add and remove paths from sys.path to change where Python looks for modules on-the-fly. It's not really recommended, however, unless you're specifically writing package management software.

Relative Imports

There's one final type of import that we haven't considered yet: the relative import. Consider this:

hornet:~ dan$ cd pymodules/
hornet:pymodules dan$ mkdir p
hornet:pymodules dan$ touch p/__init__.py
hornet:pymodules dan$ touch p/m1.py
hornet:pymodules dan$ touch p/m2.py
hornet:pymodules dan$ emacs p/m1.py
hornet:pymodules dan$ cat p/m1.py
import m2
hornet:pymodules dan$

What's happened here?

Well, we've gone back to our pymodules directory (which is now on the PYTHONPATH, so we can import things directly from it). We've created a package called 'p' by creating a directory of that name and adding an __init__.py, making that directory importable. We've then created two modules in that 'p' package, called m1 and m2. Now - look at the following session:

hornet:pymodules dan$ cd /tmp
hornet:tmp dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import m1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named m1
>>> import m2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named m2
>>> import p.m1
>>>

Firstly, we demonstrate that just typing 'import m1' and 'import m2' don't work. This is expected: they're not on the PYTHONPATH. 'import p.m1' does work, though; again, this is expected, since p's parent directory, pymodules, is on the PYTHONPATH.

But hold on a minute - let's take another look at m1.py:

hornet:pymodules dan$ cat p/m1.py
import m2

What's going on here? We just showed that 'import m2' by itself doesn't work, right?

Well, almost. Python allows relative imports. Basically, you can say 'import m2' from inside m1.py because m1.py and m2.py are in the same package (same directory). This looks like a handy feature - if you use relative imports everywhere, then you can freely move your modules around and their relative imports will keep working. However, there is a danger here: if I were to create a file called 'os.py' in that pymodules directory, it would mask the system Python's 'os' module when I try to import it. The problem is one of transparency: from looking at the import line, you can't tell whether an import is relative or absolute.

It's worth noting that this behaviour has changed in Python 3: you now say 'import .m2' to do a relative import of the m2 module. To put it another way, imports are always absolute unless you specifically make them relative.

Modules are only imported once

The final thing to note is that modules are only imported once, the first time that they're used. You can generally forget about this fact, though remember that some modules (particularly in the Zope 2 world) have code which runs upon import. Let's modify hello.py to demonstrate this:

hornet:pymodules dan$ cat hello.py
print 'Importing hello'

def print_hello():
    print 'hello!'
hornet:pymodules dan$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
Importing hello
>>> import hello
>>>

Even though we imported the hello module twice, the code at the module level was only executed once.

Managing PYTHONPATH

Not too long ago, that was pretty much all there was to know. To use a third-party package, you'd download it and either install it in your Python's site-packages directory (as that's already on the PYTHONPATH), or you'd create a new directory for it to live in, and add that directory to your PYTHONPATH in a wrapper script, or in your ~/.bash_profile. As we've demonstrated, this still works just fine.

The problem with that approach, however, is that it affects every Python program you run. They'll all get the same (probably large) PYTHONPATH, and the chance of them accidentally importing code from an unexpected location rises. Unfun debugging sessions ensue.

A better approach these days is to use virtualenv. As discussed in previous articles, a virtualenv isolates gives you a place to install packages without interfering with the main Python install; refer to those articles for more information on installing and using virtualenv.

That about wraps up this look at imports and PYTHONPATH. As ever, more information on all this can be found in the official Python documentation.


Vista hanging on crcdisk.sys

by Dan Fairs last modified Jan 19, 2009 09:30 PM
Badly seated memory found with smoking gun (or, memtest86 is your friend).

So - after an extended period of it having been sat in our conservatory, with temperatures ranging from -5C to +35C, I booted up my venerable Vista PC. It hung.

Damn.

Tried safe mode, and the boot started - but hung when it tried to load a file called crcdisk.sys. Googling revealed likely hardware problems, but nothing really specific - some people found disabling the built-in wifi helped. This PC doesn't have built-in wifi.

One thing that did crop up was that all the problems relating to this file appeared after the application of a Windows Update patch from Microsoft; and sure enough, pretty much the last thing I did last time the PC was on was run Windows Update. So I did the natural thing, cursed Microsoft again and downloaded the latest Ubuntu.

Except that didn't boot either.

This was getting annoying. I wanted to use the PC as a development server, and there it was, an electricity-sucking paperweight.

However - one thing that the Ubuntu installer does have is a menu option to run memtest86. This is a program that, as the name suggest, tests your memory. I ran that, and the screen immediately turned to a sea of red. My thoughts returned to the somewhat extreme temperature variations, and how likely that repeated expansion and shrinkage was to unseat some memory.

memtest86 is now happily running as we speak with no failures, now I've removed and re-seated all the memory. So I owe Microsoft an apology - this time, I don't think it was Vista.

I might still install Ubuntu on it though.

Stereoplex is sponsored by Fez Consulting Ltd