OK, so how can I make this work?
This is a quick guide to getting started with mod_python programming once you have it installed. This is not an installation manual!
It may seem like a little too much for starters, but you
need to understand what a handler
is in order to
use mod_python. And it's really rather simple.
Apache processes requests in steps. For example, the first step may be to authenticate the user, the next step to verify whether that user is allowed to see a particular file, then (next step) read the file and send it to the client. Most requests consist of one step: read the file and send it to the client. Exactly which steps are taken, depends on the configuration.
A handler is a function that processes one step. There may be more than one handler available to process a particular step, in which case they are called in sequence. For each of the steps, there is a default Apache handler (most of which perform only very basic functions or do nothing), and then there are additional handlers provided by Apache modules, such as mod_python.
Mod_python provides nearly every possible handler to
Apache. Mod_python handlers by default do not perform any
function, unless specifically told so by a configuration
directive. These directives begin with Python
and
end with Handler
(e.g.
PythonAuthenHandler
) and associate a handler with a
Python function. So the main function of mod_python is to act as
a dispatcher between Apache handlers and python functions
written by a developer like you.
The most commonly used one is PythonHandler
. It's
for a handler that has no specialized purpose, such as
authentication. For lack of a better term, I will refer to
this handler from here on as generic handler. The
default Apache action for this handler would be to read the file
and send it to the client. Most applications you will write will
use this one handler. If you insist on seeing ALL the possible
handlers, click here.
<Directory /mywebdir> AddHandler python-program .py PythonHandler myscript </Directory>And let's say that we have a python program (windows users: substitute forward slashes for backslashes)
/mywedir/myscript.py
that looks like this:
from mod_python import apache def handler(req): req.conent_type = "text/plain" req.send_http_header() req.write("Hello World!") return apache.OKHere is what's going to happen: The AddHandler directive tells Apache that any request for any file ending with .py in the
/mywebdir
directory or a subdirectory
thereof needs to be processed by mod_python.
When such a request comes in, Apache starts going through it's
regular steps calling handlers in mod_python. The mod_python
handlers check if a directive for that handler was specified in
the configuration. In this particular example, no action will be
taken by mod_python for all handlers except for the generic
handler. When we get to the generic handler, mod_python will
notice that PythonHandler myscript
directive was
specified and do the following:
PythonHandler
directive was found to sys.path
.
myscript
. (Note that
if myscript
was in a subdirectory of the directory where
PythonHandler
was specified, then the import
would not work. The way around this is to use packages, so the directive
would look like PythonHandler subdir.myscript
.)
handler
in myscript
from mod_python import apache
apache
module which
provides us the interface to Apache. With a few rare exceptions,
every mod_python program will have this line.
def handler(req):
req.content_type = "text/plain"
- This sets the content type to "text/plain". The default is usually "text/html", but since our handler doesn't produce any html, "text/plain" is more appropriate.
req.send_http_header()
- This function sends the HTTP headers to the client. You can't really start writing to the client without sending the headers first. Note that one of the headers is "content-type". So if you want to set custom content-types, you better do it before you call
req.send_http_header()
.req.write("Hello Wordl!")
- This writes the "Hello World!" string to the client. (Did I really have to explain this one?)
return apache.OK
- This tells mod_python (who is calling this function) that everything went OK and that the request has been processed. If things did not go OK, that line could be
return apache.HTTP_INTERNAL_SERVER_ERROR
orreturn apache.HTTP_FORBIDDEN
. When things do not go OK, Apache will log the error and generate an error message for the client.
myscript.py
. The only requirement was that it
refers to a .py
file. In fact the name of the
file doesn't matter, and the file referred to in the URL
doesn't have to exist. So, given the above configuration,
http://myserver/mywebdir/myscript.py and
http://myserver/mywebdir/montypython.py would give the exact
same result.
At this point, if you didn't understand the above paragraph, go back and read it again, until you do.
Let's say we want to password-protect this directory. We want the login to be "spam", and the password to be "eggs".
First, we need to tell Apache to call our authentication handler
when authentication is needed. We do this by adding the
PythonAuthenHandler
. So now our config looks like
this:
<Directory /mywebdir> AddHandler python-program .py PythonHandler myscript PythonAuthenHandler myscript </Directory>Notice that the same script is specified for two different handlers. This is fine, because if you remember, mod_python will look for different functions within that script for the different handlers.
Next, we need to tell Apache that we are using basic HTTP authentication, and only valid users are allowed (this is pretty basic Apache stuff, so I'm not going to go into details here). Our config looks like this now:
<Directory /mywebdir> AddHandler python-program .py PythonHandler myscript PythonAuthenHandler myscript AuthType Basic AuthName "Restricted Area" require valid-user </Directory>Now we need to write an authentication handler function in myscript.py. A basic authentication handler would look like this:
def authenhandler(req): user = req.connection.user pw = req.get_basic_auth_pw() if user == "spam" and pw == "eggs": return apache.OK else: return apache.HTTP_UNAUTHORIZEDLet's look at this line by line:
def authenhandler(req)
authenhandler
because, as we already described above,
mod_python takes the name of the directive
(PythonAuthenHandler
), drops the word "Python"
and converts it lower case.
user = req.connection.user
- This is how you obtain the username that the user entered. In case you're wondering, the
connection
object is an object that contains information specific to a connection. With HTTP Keep-Alive, a single connection can serve multiple requests.pw = req.get_basic_auth_pw()
- This is how we obtain the password. The basic HTTP authentication transmits the password in base64 encoded form to make it a little bit less obvious. This function decodes the password and returns it as a string.
if user == "spam" and pw == "eggs":
return apache.OK- We compare the values provided by the user, and if they are what we were expecting, we tell Apache to go ahead and proceed by returning
apache.OK
. Apache will then proceed to the next handler. (which in this case would behandler()
if it's a.py
file).else:
return apache.HTTP_UNAUTHORIZED- Else, we tell Apache to return
HTTP_UNAUTHORIZED
to the client.