Django, mod_wsgi, Apache and OS X - do it.

by jesse in , ,


whut_2.jpgSo, I'm one of those people where I don't like running things "too far" from what a production setup might look like (I code on OS/X, deploy to Linux). This is why I jump(ed) through various hoops on my OS X system to get Apache/Django/mod_wsgi/etc all up and running and happy (not for serving the site; just developing). Since I like simple/succinct guides, I thought I'd post what I did so others can follow in my stead.

Note: These instructions work with the python 2.5 version which ships with Leopard, or a self-compiled version of 2.6 (which is what I prefer) - see the InstallationOnMacOSX mod_wsgi page. Additionally, see the "Missing Code For Architecture" section for possible work-arounds if you find yourself needing 32 bit execution of Apache; I think the "Forcing 32 Bit Execution" are preferred over the "thinning" of the Apache binary.

First, download and install mod_wsgi on leopard, this is as easy as (on Leopard):

curl -o mod_wsgi.tgz http://modwsgi.googlecode.com/files/mod_wsgi-2.5.tar.gz
tar -xzf mod_wsgi.tgz
cd mod_wsgi-2.5
./configure
make
sudo make install

Now, edit (via sudo) /etc/apache2/httpd.conf and add the line:

LoadModule wsgi_module libexec/apache2/mod_wsgi.so

After the rest of the LoadModule lines. Cool.

Invariably all of my directions play with virtualenv/virtualenvwrapper and pip:

mkvirtualenv django
cdvirtualenv
easy_install pip
pip install http://media.djangoproject.com/releases/1.1/Django-1.1-rc-1.tar.gz
django-admin.py startproject mysite
django-admin.py startapp myapp
cd mysite
mkdir apache
mkdir media

Now, that just sets up the skeleton - the meat of the wsgi configuration goes in apache/ in the mysite/apache directory. The first file is named mysite.wsgi:

import os, sys

#Calculate the path based on the location of the WSGI script.
apache_configuration = os.path.dirname(__file__)
project = os.path.dirname(apache_configuration)
workspace = os.path.dirname(project)
sys.path.append(workspace)

os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

This does the needed wsgi project magic for the Django application - don't worry about the interpreter path; we'll do that next.

Next up is a file named apache_django_wsgi.conf, this looks like this:

# mod_wsgi configuration directives - I like having stdout access, the other two 
# options run mod_wsgi in daemon mode - more on this in a minute.
WSGIPythonHome /<path to virtualenv>
WSGIRestrictStdout Off
WSGIDaemonProcess django
WSGIProcessGroup django

#
# This should be the path of the /mysite/media directory
# for example "/Users/jesse/mysite/media/"
#
Alias /site_media/ "<PATH TO>/mysite/media/"
<Directory "<PATH TO>/mysite/media">
Order allow,deny
Options Indexes
Allow from all
IndexOptions FancyIndexing
</Directory>

#
# Directory path to the admin media, for example:
#

Alias /media/ "<PATH TO>/virtualenv/site-packages/django/contrib/admin/media/"
<Directory "<PATH TO>/virtualenv/site-packages/django/contrib/admin/media">
Order allow,deny
Options Indexes
Allow from all
IndexOptions FancyIndexing
</Directory>

#
# Path to the mysite.wsgi file, for example:
# "/Users/jesse/mysite/apache/mysite.wsgi"
#

WSGIScriptAlias / "<PATH TO>/mysite/apache/mysite.wsgi"

<Directory "<PATH TO>/mysite/apache">
Allow from all
</Directory>

The apache_django_wsgi.conf file is the meat-and-potatoes here. This sets up all the paths/permissions, and is in Apache httpd.conf format. You can pretty much logjam any apache configuration directive here that you like.

Your final step is to once again edit (via sudo) /etc/apache2/httpd.conf and add a line like this at the verrrrry bottom:

Include "/path to/mysite/apache/apache_django_wsgi.conf"

And then run "sudo apachectl restart"

You should now be able to hit http://127.0.0.1/ and see the friendly and inviting django welcome page. Note, that if you are using sqlite as your database, you should chmod a+rw the file, so that processes which are not you can mess with it.

There's a final piece to this though. Normally, if you run mod_wsgi in embedded mode, you're going to need to restart apache every single time you make a change to your django app.

Ah! But we're running in daemon mode. This means all you need to do when you change a file is:

touch mysite/apache/mysite.wsgi

This will trigger a reload and magic happens. Me being as lazy as I am (ask my wife) ended up snagging Bruno Bord's tdaemon script, and hacking it up a bit. The tdaemon script will watch a directory and run tests. Well, I wanted it to watch a directory (and let me filter sub directories) and then run that touch command. So I reused my watcher.py (here) - I used this to monitor my sphinx tree and run builds as well (and other stuff). Here's how I'd use this:

workon django
cdvirtualenv
cd mysite
python ~/.slash/bin/watcher.py --command "touch apache/mysite.wsgi" -f media

This will auto-fire the touch command whenever it detects a file change (including svn updates).

You can also do this another way In my rush to reuse a tool I use a bit (watcher) I skipped past the mod_wsgi document section on code reloading that shows how to setup a monitor which will watch .py file changes and kill the wsgi daemon, here. If you scroll down a bit, you'll see the "Monitoring For Code Changes" section. All you need to do here is copy the code from the wiki into a module on your PYTHONPATH - in my case, I wrote it to mysite/apache/wsgi_monitor.py (just for this example! you should put it someplace else!) and then changed the mysite.wsgi file to import it, and set it up:

import os, sys

#Calculate the path based on the location of the WSGI script.
apache_configuration = os.path.dirname(__file__)
project = os.path.dirname(apache_configuration)
workspace = os.path.dirname(project)
sys.path.append(workspace)

sys.path.append(apache_configuration) # you probably shouldn't do this.
import wsgi_monitor
wsgi_monitor.start(interval=1.0)

os.environ['DJANGO_SETTINGS_MODULE'] = 'ui.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

This method - once you reload apache - will watch the project for changes and then kill the wsgi daemon (forces a reload). So there you go - two ways of doing it.

The nice thing about this setup is that I can make production version of the wsgi scripts and check them in, but keep local "my copies" (ala local_settings.py) additionally, I don't have to jump through hoops to get static media and content served up via the django development server.

Additional reading:


Evernote invites.

by jesse in , ,


Just a quick note - I've got 10 Evernote invites - if you don't know what Evernote is, check out the Ars review here. Post in the comments if you'd like to test drive it. I'm going to need an email address. So far, I'm loving it - but I'm also someone with about 500+ bookmarks in Safari, a ton of "to read" stuff saved on my hard drive, and over 100 RSS feeds in NetNewsWire. So something to help me archive/save/search everything is key.

Now I just want PDF indexing support.

Ok, I have 10 more left!


Per Ars: Sun is talking about porting Java to the iPhone (and an idea for python)

by jesse in , , , ,


coffee poster.pngOk. So Sun has come out stating they're starting work based on the SDK released by Apple for the iPhone earlier this week to port the JDK over to the iPhone. Quoting the sun rep:

"Now, the iPhone is open" as a target platform, Klein said. The free JVM would be made available via Apple's AppStore marketplace for third-party applications.

Now, as comments there and elsewhere have pointed out - there's a clause in the Agreement that comes with the SDK forbidding applications from being placed on the AppStore that can execute/interpret "other applications" - to whit:

3.3.2) An Application may not itself install or launch other executable code by any means, including without limitation through the use of a plug-in architecture, calling other frameworks, other APIs or otherwise. No interpreted code may be downloaded and used in an Application except for code that is interpreted and run by Apple's Published APIs and builtin interpreter(s).

So - unless Sun plans on making a JDK that a) doesn't run anything or b) can be compiled into an application that is to be sold on the AppStore (providing the runtime for the app, like a self-standing .jar/.war) then I can't see this happening, and option a is about as useful as a toilet bowl filled with taco meat.theoffice.jpg

On the other hand, option b: Making a runtime they release outside of the app store for application developers to use to write an application in Java and then have it compile-down to an Objective-C runtime/bytecode binary - then it could work, but those would be some *fat* binaries without a lot of magic.

Now - could the same thing be done with Python? Perhaps. Right now we have the pyObjC bridge that ships with Leopard that allows you mostly unfettered access into the Objective-C/OSX programming environment. This means you can build "native" applications in Python.

I doubt these bindings will work/exist on the iPhone, which means you want some utility to take Python code and "interpret" it down into an Objective-C binary, i.e.: an embed-able environment ala what Sun may end up having to do where you write an app in pyObjC/Python and the app+runtime is compiled down into Objective-C.

Again, without a lot of trickery, these would be fatass binaries - probably fatter than the notion of the universal binaries most people ship nowadays for OS/X.

It's a thought - now I should get back to poking at Objective-C and other pre-pycon hackery. This is something people more versed in compilers, runtimes and with more free time than me will probably explore.