chester's blog

technology, travel, comics, books, math, web, software and random thoughts

HOWTO: Python 2.6.5 + Django (via Passenger WSGI) at DreamHost

| Comments

djangoDreamhost is a pretty decent provider for people with lots of small websites. I didn’t expect them to have, say, Python 3 (although I’d love to), but I was surprised to find their official support is just for 2.4!

There are instructions for custom builds, but they are not much supportive (“If you are positive that you need to install Python, reconsider”), and a few unofficial ones. Here are the steps I used – try them at your own risk, since I can’t give any guarantees other than the fact that they worked for me (hint: create a new subdomain with its own user and try that there first.)

(These instructions might also work for Python 3.0 – just replace the version numbers accordingly. I did not test that (yet), but if you do, please let me know.)

The first step is to download, build and install the desired Python version (2.6.5). To do that, enable ssh access to that domain (via Dreamhost Panel), ssh to it, then issue the commands below:

mkdir work
cd work
wget http://python.org/ftp/python/2.6.5/Python-2.6.5.tgz
tar -xzvf Python-2.6.5.tgz
cd Python-2.6.5
./configure --prefix=$HOME/local
make
make install

You can do all this because the --prefix ensures files will be inside your home dir (but outside the yourdomain.com dir, so they will be private), under ~/local/bin, ~/local/lib, and so on.

Now prepend the ~/local/bin directory to your path (to force your shell to “see” this Python install before Dreamhost’s) by appending the following lines to the .bash_profile file located on your home directory (using vi, pico or other editor):

# Added for custom-built python 2.6
PATH=$HOME/local/bin:$PATH
export PATH

Logoff and logon, and voila! You have a decent Python:

$ python
Python 2.6.5 (r265:79063, Apr  8 2010, 02:30:51)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

Most add-ons will find your site-packages and related directories automatically. Some configure scripts for makefiles may not, but adding a --prefix=${HOME}/.local parameter (or something like that, check the script’s help if that happens) should solve the issue.

Django can be installed onto this Python setup by following the standard instructions. In my case:

cd ~/work
wget http://www.djangoproject.com/download/1.1.1/tarball/
tar xvzf Django-1.1.1.tar.gz
cd Django-1.1.1
python setup.py install

I also had to add MySQL for Python:

cd ~/work
wget http://downloads.sourceforge.net/project/mysql-python/mysql-python-test/1.2.3c1/MySQL-python-1.2.3c1.tar.gz?use_mirror=ufpr
tar -xvzf MySQL-python-1.2.3c1.tar.gz
cd MySQL-python-1.2.3c1
python setup.py build
python setup.py install

For most libraries, however, you will prefer easy_install, which is part of setuptools. However, if you type easy_install now, it will use the Python 2.4 version (to which you can’t add libraries anyway). Let’s fix it with:

cd ~/work
wget http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086
bash setuptools-0.6c11-py2.6.egg

After that, easy_install should work – if not, try easy_install-2.6. One caveat is that libraries that build against native binaries must have access to them on the correct places. My issue was with lxml, but I solved it with these instructions (only changing .local to local).

That should allow you to add everything you need until your app works fine – that is, if you can enter python manage.py shell under the Django project dir, import some stuff, run your unit tests (you have coded tests, right?), etc.

Unfortunately, Dreamhost’s web server (Apache) will still use the old Python – and you need to change that to put your app online. I guess there is some FastCGI trickery for that, but a cleaner way (in my humble opinion) is to use Passenger (aka “mod_rails”, which Dreamhost supports due to the popularity of Ruby on Rails) with Python Web Server Gateway Interface (WSGI).

Dreamhost’s documentation os Passenger WSGI has a useful bootstrap script (at least if you did not start your Django project already), but let’s assume you already have one (if not, you should really play a little bit beforehand.)

In this process, we’ll separate “static” content (in fact, all non-Python stuff, including PHP/Perl pages) from the Python/Django files. Return to your domain’s configuration on Dreamhost Panel and change the “Web Directory” from "<home dir>/yourdomain.com" to "<home dir>/yourdomain.com/public" (i.e., append a “public” sub-directory), then tick the “Passenger (Ruby/Python apps only)” checkbox.

After that, wait until the public directory shows up and move your existing static files (if any) to it. If you haven’t already uploaded your Django project dir, put it under yourdomain.com, that is, side-by-side with public.

You should now have a directory structure like this: * * /home/yourusername/

*   local/
    *   bin/
    *   include/
    *   lib/
    *   share/
*   yourdomain.com/
    *   public/
    *   yourDjangoProjectDirectory/
*   logs/
*   work/

Create a passenger_wsgi.py file on the “yourdomain.com” directory, with the lines below (replace /home/XXXXXX with your real home and YYYYYY with your Django project directory):

import sys, os
INTERP = "/home/XXXXXX/local/bin/python"
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)
sys.path.append(os.getcwd())
os.environ['DJANGO_SETTINGS_MODULE'] = "YYYYYY.settings"
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

The magic here is that this file will be detected by Passenger and will be used by it whenever an URL that is not an static file under public is called. It will first switch the environment from the default Python (which we can’t change anyway) to our custom-built one, then build a Django environment (the application variable) which will handle valid URLs.

The beauty of this setup is that non-Python files (static content and stuff such as PHP) located inside public will be served directly by Apache, and your Python stuff will reside outside – Django’s URL dispatcher will control access, avoiding nasty accidents with leftover files.

After everything is set up, you can remove the ~/work directory, and have fun with your (cheaply) hosted Python application!

Comments