Flangy > Software Development > Python CGI Development
Updated September 10, 2003
Send comments to
Contents:
I have a PowerBook running OS X that I use as a web development machine. I can run the same servers that our Linux production machine runs (Apache, MySQL) and I can run Photoshop for graphic editing, which is a pretty good deal.
In order to set up a stock OS X install to run Python CGIs you need to do some extra steps, which is what this document is all about. Note that much of this is applicable to getting Python CGIs running on other UNIX-style environments too.
As of Sept 2003 I'm running 10.2.6 with various security updates.
The easiest way to get Apache running is to open System Preferences and go to Sharing. From there start Personal Web Sharing. This UI sets up Apache to launch when you boot your machine.
You have a few options for getting Python on your machine.
Pre-installed
If you're running 10.2 then you're in luck: it comes with Python 2.2 installed. I'm not sure how common 10.1 is anymore. At least, most of the interesting OS X software I see around requires 10.2.x now. OS X 10.3 will ship with Python 2.3 pre-installed.
Compile it yourself
If you're still on 10.1, or you want a newer version of Python than 2.2, you can compile Python from source. See the Python site for the version of your choice. All recent UNIX source versions of Python compile and install great on OS X.
To build Python you need Apple's OS X devtools. Note that the devtools that shipped with 10.2 are out of date. You really should get the Dec 2002 update if you haven't already.
Precompiled binaries
MacPython has pre-compiled Python packages for OS X 10.2 (and OS 9 too.) The OS X packages are the UNIX command-line Python (the one you want) with additional OS X support. I installed the 2.3 package and it's living side-by-side with the 2.2 that came with the OS. Note that the OS supplied python lives in /usr/bin and will be found first in the search path. You can rename /usr/bin/python to /usr/bin/python2.2 and make a symlink to the 2.3 interpreter if you want.
I use name-based virtual hosts to run multiple domain names off my laptop. For each real domain I add a .dev domain to OS X's NetInfo database.
In NetInfo Manager, click on the lock at the bottom. Go to /machines/localhost and select Edit | Duplicate from the menu (command-D.) In the copy, double-click the value of the 'name' property and change it to your dev domain. Duplicate a new entry for each domain you want to set up.
Alternatively, you can get some command line scripts that modify the NetInfo database.
Files have to go somewhere, of course. I made a top-level /www folder, with a directory for each domain. This lets me take some apache setup shortcuts on the /www directory.
Load up /etc/httpd/httpd.conf in your favorite text editor.
Look for the section:
# To use CGI scripts: # #AddHandler cgi-script .cgi
Uncomment and add whatever extensions you plan to use for your scripts:
AddHandler cgi-script py
I prefer to keep the .py extension for Python CGI files, and then use mod_rewrite to hide script extensions [see].
# # Use name-based virtual hosting. # NameVirtualHost *
Set up a VirtualHost for each domain you added to NetInfo:
<VirtualHost _default_>
ServerName conversatron.dev
DocumentRoot "/www/conversatron"
<Directory "/www/conversatron">
Options Indexes FollowSymLinks ExecCGI Includes
AllowOverride All
</Directory>
</VirtualHost>
Make sure that Options includes ExecCGI.
If all of your domains have a common parent and you want to shortcut the setup a bit, you can replace the VirtualHosts' Directory blocks with one on the parent folder.
This Directory block should go above the VirtualHost blocks.
<Directory "/www"> Options Indexes FollowSymLinks ExecCGI Includes AllowOverride All </Directory>
Then your VirtualHost blocks can look like:
<VirtualHost _default_> ServerName adamv.dev DocumentRoot /www/adamv </VirtualHost>
I prefer to put my virtual host information in a separate file vhosts.conf and put this at the end of httpd.conf:
NameVirtualHost * Include /etc/httpd/vhosts.conf
I also set up a domain www.dev that just has links to the roots of all the development sites on my laptop. I make a domain for this instead of putting it at localhost so I can have a different page come up for people who Rendezvous into my box via Safari.
The Rendezvous module seems to attach itself to the first virtual host specified if virtual hosts are turned on. (Virtual hosts are off by default on a new OS X 10.2.x install.)
To set up a link page at www.dev and a Rendezvous page at localhost, after the <Directory "/www"> block in your vhosts setup add these hosts:
<VirtualHost _default_> ServerName * DocumentRoot /www/localhost </VirtualHost> <VirtualHost _default_> ServerName www.dev DocumentRoot /www </VirtualHost>
Change /www/localhost to wherever you want your default webpage to live. The stock default webpage is the "welcome to Apache" page at /Library/Webserver/Documents/.
Restart Apache from Terminal: sudo apachectl restart
If there was an error you can use apachectl configtest to test the config files.
To debug a script error you need to look at your httpd error log. The default place for this is /var/log/httpd/error_log. At the command line you can use the tail command to view the end of the log. In the OS X GUI you can use the Console app to open the error log. Problems below will be given in terms of the errors in the log file.
Make sure your scripts have the execute bit set.
You can't change execute bits from Finder, you'll have to do it from the Terminal (maybe your editor lets you set them.) From the command line it's:
chmod a+x my_file.py
Make sure the DocumentRoot in your VirtualHost has a Directory block with Options ExecCGI
Check that your script has the magic #! line at the top, otherwise Apache doesn't know what to use to execute it. I usually use /usr/bin/python as that's where it is our production server.
#!/usr/bin/python
It's perhaps more correct to use /usr/bin/env to find the Python interpreter. This lets your script work between machines with different setups.
#!/usr/bin/env python
Your script is running, but there was a coding error or it didn't output proper HTTP headers. At minimum your script should output a content-type line with two new-lines. (Note that print adds a newline to the output.)
#!/usr/bin/python print "Content-type: text/plain\n" print "Hello World"
#!/usr/bin/python print "Content-type: text/html\n" print """ <html> <head> <title>CGI Script Test</title> </head> <body> Hello World Wide Web </body> </html> """
This is a (partial) list of additional software I install on OS X to really get up and running. Lots of them are useful for web and other development.
Using file extensions in URLs is rather gauche (though I can't think of any reason to worry about it for images.) Keith presents some mod_rewrite rules for automatically redirecting URLs to corresponding PHP files if they exist. Here's a version that checks for Python scripts, then regular HTML files.
You can see the per-extension conditions and rule, and copy those for any other filetypes you want to use automatically. If you know a nicer way to handle multiple file types let me know.
RewriteEngine On
# Rewrite all URLs requested from the server
RewriteBase /
# If the request doesn't include a dot and doesn't have a slash at the end,
# add a slash and redirect
# Ex: /a/b/someresource --> /a/b/someresource/
# Ex: /a/b/someresource.ext --> no change, there's an extension
RewriteRule ^([^.]+[^/])$ $1/ [R=permanent,L]
# If the request isn't a folder, and there's a .py that matches
# the last part of the path, redirect to that.
# Ex: /a/b/something/ --> a/b/something.py
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.py -f
RewriteRule ^(.+[^/]) $1.py [L]
# If the request isn't a folder, and there's an .html that matches
# the last part of the path, redirect to that.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.+[^/]) $1.html [L]
These rules can go inside a <Directory> block, not in a <VirtualHost> block.
This is a drop-in module for Python CGI scripts that adds gzip and caching header support.
This page gives an overview of how HTTP compression works. It includes sample Python code to detect and support compression, in a few web programming frameworks.
This page gives a brief example of supporting ETag and If-Modified-Since headers in Python.
The End.