1
\chapter{Tutorial\label{tutorial}}
4
\emph{So how can I make this work?}
7
\emph{This is a quick guide to getting started with mod_python
8
programming once you have it installed. This is \textbf{not} an
11
\emph{It is also highly recommended to read (at least the top part of)
12
Section \ref{pythonapi}, \citetitle[pythonapi.html]{Python API} after
13
completing this tutorial.}
15
\section{A Quick Start with the Publisher Handler\label{tut-pub}}
17
This section provides a quick overview of the Publisher handler for
18
those who would like to get started without getting into too much
19
detail. A more thorough explanation of how mod_python handlers work
20
and what a handler actually is follows on in the later sections of the
23
The \code{publisher} handler is provided as one of the standard
24
mod_python handlers. To get the publisher handler working, you will
25
need the following lines in your config:
28
AddHandler mod_python .py
29
PythonHandler mod_python.publisher
33
The following example will demonstrate a simple feedback form. The
34
form will ask for the name, e-mail address and a comment and construct
35
an e-mail to the webmaster using the information submitted by the
36
user. This simple application consists of two files:
37
\filenq{form.html} - the form to collect the data, and
38
\filenq{form.py} - the target of the form's action.
40
Here is the html for the form:
44
Please provide feedback below:
46
<form action="form.py/email" method="POST">
48
Name: <input type="text" name="name"><br>
49
Email: <input type="text" name="email"><br>
50
Comment: <textarea name="comment" rows=4 cols=20></textarea><br>
57
Note the \code{action} element of the \code{<form>} tag points to
58
\code{form.py/email}. We are going to create a file called
59
\filenq{form.py}, like this:
64
WEBMASTER = "webmaster" # webmaster e-mail
65
SMTP_SERVER = "localhost" # your SMTP server
67
def email(req, name, email, comment):
69
# make sure the user provided all the parameters
70
if not (name and email and comment):
71
return "A required parameter is missing, \
72
please go back and correct the error"
74
# create the message text
80
I have the following comment:
88
""" % (email, WEBMASTER, comment, name)
91
conn = smtplib.SMTP(SMTP_SERVER)
92
conn.sendmail(email, [WEBMASTER], msg)
95
# provide feedback to the user
100
Thank You for your kind comments, we
101
will get back to you shortly.
108
When the user clicks the Submit button, the publisher handler will
109
load the \function{email} function in the \module{form} module,
110
passing it the form fields as keyword arguments. It will also pass the
111
request object as \code{req}.
113
Note that you do not have to have \code{req} as one of the arguments
114
if you do not need it. The publisher handler is smart enough to pass
115
your function only those arguments that it will accept.
117
The data is sent back to the browser via the return value of the
120
Even though the Publisher handler simplifies mod_python programming a
121
great deal, all the power of mod_python is still available to this
122
program, since it has access to the request object. You can do all the
123
same things you can do with a ``native'' mod_python handler, e.g. set
124
custom headers via \code{req.headers_out}, return errors by raising
125
\exception{apache.SERVER_ERROR} exceptions, write or read directly to
126
and from the client via \method{req.write()} and \method{req.read()},
129
Read Section \ref{hand-pub} \citetitle[hand-pub.html]{Publisher Handler}
130
for more information on the publisher handler.
132
\section{Quick Overview of how Apache Handles Requests\label{tut-overview}}
134
If you would like delve in deeper into the functionality of
135
mod_python, you need to understand what a handler is.
137
Apache processes requests in \dfn{phases}. For example, the first
138
phase may be to authenticate the user, the next phase to verify
139
whether that user is allowed to see a particular file, then (next
140
phase) read the file and send it to the client. A typical static file
141
request involves three phases: (1) translate the requested URI to a
142
file location (2) read the file and send it to the client, then (3)
143
log the request. Exactly which phases are processed and how varies
144
greatly and depends on the configuration.
146
A \dfn{handler} is a function that processes one phase. There may be
147
more than one handler available to process a particular phase, in
148
which case they are called by Apache in sequence. For each of the
149
phases, there is a default Apache handler (most of which by default
150
perform only very basic functions or do nothing), and then there are
151
additional handlers provided by Apache modules, such as mod_python.
153
Mod_python provides every possible handler to Apache. Mod_python
154
handlers by default do not perform any function, unless specifically
155
told so by a configuration directive. These directives begin with
156
\samp{Python} and end with \samp{Handler}
157
(e.g. \code{PythonAuthenHandler}) and associate a phase with a Python
158
function. So the main function of mod_python is to act as a dispatcher
159
between Apache handlers and Python functions written by a developer
162
The most commonly used handler is \code{PythonHandler}. It handles the
163
phase of the request during which the actual content is
164
provided. Because it has no name, it is sometimes referred to as as
165
\dfn{generic} handler. The default Apache action for this handler is
166
to read the file and send it to the client. Most applications you will
167
write will override this one handler. To see all the possible
168
handlers, refer to Section \ref{directives},
169
\citetitle[directives.html]{Apache Directives}.
171
\section{So what Exactly does Mod-python do?\label{tut-what-it-do}}
173
Let's pretend we have the following configuration:
175
<Directory /mywebdir>
176
AddHandler mod_python .py
177
PythonHandler myscript
182
\strong{NB:} \filenq{/mywebdir} is an absolute physical path.
184
And let's say that we have a python program (Windows users: substitute
185
forward slashes for backslashes) \file{/mywedir/myscript.py} that looks like
189
from mod_python import apache
193
req.content_type = "text/plain"
194
req.write("Hello World!")
199
Here is what's going to happen: The \code{AddHandler} directive tells
200
Apache that any request for any file ending with \file{.py} in the
201
\file{/mywebdir} directory or a subdirectory thereof needs to be
202
processed by mod_python. The \samp{PythonHandler myscript} directive
203
tells mod_python to process the generic handler using the
204
\code{myscript} script. The \samp{PythonDebug On} directive instructs
205
mod_python in case of an Python error to send error output to the
206
client (in addition to the logs), very useful during development.
208
When a request comes in, Apache starts stepping through its request
209
processing phases calling handlers in mod_python. The mod_python
210
handlers check whether a directive for that handler was specified in
211
the configuration. (Remember, it acts as a dispatcher.) In our
212
example, no action will be taken by mod_python for all handlers except
213
for the generic handler. When we get to the generic handler,
214
mod_python will notice \samp{PythonHandler myscript} directive and do
220
If not already done, prepend the directory in which the
221
\code{PythonHandler} directive was found to \code{sys.path}.
224
Attempt to import a module by name \code{myscript}. (Note that if
225
\code{myscript} was in a subdirectory of the directory where
226
\code{PythonHandler} was specified, then the import would not work
227
because said subdirectory would not be in the \code{sys.path}. One
228
way around this is to use package notation, e.g. \samp{PythonHandler
232
Look for a function called \code{handler} in \code{myscript}.
235
Call the function, passing it a request object. (More on what a
236
request object is later)
239
At this point we're inside the script:
245
from mod_python import apache
248
This imports the apache module which provides us the interface to
249
Apache. With a few rare exceptions, every mod_python program will have
257
\index{handler} This is our \dfn{handler} function declaration. It
258
is called \samp{handler} because mod_python takes the name of the
259
directive, converts it to lower case and removes the word
260
\samp{python}. Thus \samp{PythonHandler} becomes
261
\samp{handler}. You could name it something else, and specify it
262
explicitly in the directive using \samp{::}. For example, if the
263
handler function was called \samp{spam}, then the directive would
264
be \samp{PythonHandler myscript::spam}.
266
Note that a handler must take one argument - the request
267
object. The request object is an object that provides all of the
268
information about this particular request - such as the IP of
269
client, the headers, the URI, etc. The communication back to the
270
client is also done via the request object, i.e. there is no
275
req.content_type = "text/plain"
278
This sets the content type to \samp{text/plain}. The default is usually
279
\samp{text/html}, but since our handler doesn't produce any html,
280
\samp{text/plain} is more appropriate.
284
req.write("Hello World!")
287
This writes the \samp{Hello World!} string to the client. (Did I really
288
have to explain this one?)
295
This tells Apache that everything went OK and that the request has
296
been processed. If things did not go OK, that line could be return
297
\constant{apache.HTTP_INTERNAL_SERVER_ERROR} or return
298
\constant{apache.HTTP_FORBIDDEN}. When things do not go OK, Apache
299
will log the error and generate an error message for the client.
303
\strong{Some food for thought:} If you were paying attention, you
304
noticed that the text above didn't specify that in order for the
305
handler code to be executed, the URL needs to refer to
306
\filenq{myscript.py}. The only requirement was that it refers to a
307
\filenq{.py} file. In fact the name of the file doesn't matter, and
308
the file referred to in the URL doesn't have to exist. So, given the
309
above configuration, \samp{http://myserver/mywebdir/myscript.py} and
310
\samp{http://myserver/mywebdir/montypython.py} would give the exact
311
same result. The important thing to understand here is that a handler
312
augments the server behaviour when processing a specific type of file,
313
not an individual file.
315
\emph{At this point, if you didn't understand the above paragraph, go
316
back and read it again, until you do.}
318
\section{Now something More Complicated - Authentication\label{tut-more-complicated}}
320
Now that you know how to write a primitive handler, let's try
321
something more complicated.
323
Let's say we want to password-protect this directory. We want the
324
login to be \samp{spam}, and the password to be \samp{eggs}.
326
First, we need to tell Apache to call our \emph{authentication}
327
handler when authentication is needed. We do this by adding the
328
\code{PythonAuthenHandler}. So now our config looks like this:
331
<Directory /mywebdir>
332
AddHandler mod_python .py
333
PythonHandler myscript
334
PythonAuthenHandler myscript
339
Notice that the same script is specified for two different
340
handlers. This is fine, because if you remember, mod_python will look
341
for different functions within that script for the different handlers.
343
Next, we need to tell Apache that we are using Basic HTTP
344
authentication, and only valid users are allowed (this is fairly basic
345
Apache stuff, so we're not going to go into details here). Our config
349
<Directory /mywebdir>
350
AddHandler mod_python .py
351
PythonHandler myscript
352
PythonAuthenHandler myscript
355
AuthName "Restricted Area"
360
Now we need to write an authentication handler function in
361
\file{myscript.py}. A basic authentication handler would look like
366
from mod_python import apache
368
def authenhandler(req):
370
pw = req.get_basic_auth_pw()
373
if user == "spam" and pw == "eggs":
376
return apache.HTTP_UNAUTHORIZED
379
Let's look at this line by line:
385
def authenhandler(req):
388
This is the handler function declaration. This one is called
389
\code{authenhandler} because, as we already described above,
390
mod_python takes the name of the directive
391
(\code{PythonAuthenHandler}), drops the word \samp{Python} and converts
396
pw = req.get_basic_auth_pw()
399
This is how we obtain the password. The basic HTTP authentication
400
transmits the password in base64 encoded form to make it a little
401
bit less obvious. This function decodes the password and returns it
402
as a string. Note that we have to call this function before obtaining
410
This is how you obtain the username that the user entered.
414
if user == "spam" and pw == "eggs":
418
We compare the values provided by the user, and if they are what we
419
were expecting, we tell Apache to go ahead and proceed by returning
420
\constant{apache.OK}. Apache will then consider this phase of the
421
request complete, and proceed to the next phase. (Which in this case
422
would be \function{handler()} if it's a \code{.py} file).
427
return apache.HTTP_UNAUTHORIZED
430
Else, we tell Apache to return \constant{HTTP_UNAUTHORIZED} to the
431
client, which usually causes the browser to pop a dialog box asking
432
for username and password.