Categories

Running Python Scripts Under Apache

Apache can be used to serve dynamic pages written in any programming language. The most popular language must be PHP which can be tightly coupled with Apache. We will try here to serve pages generated from a Python script.

Python with Apache

To understand the “tightly” word, you need to understand the way you can use dynamic pages with Apache.

The common and the easiest way to use dynamic pages is the Common Gateway Interface (CGI). It’s a little standard allowing the HTTP server to call an external program. This program can found all information about the request in its environment and must return the response through the standard output. It’s really useful because you can write your program using any language. And, you can also call a scripting engine that support CGI to run your favorite scripts. But this is the “slow way”: for each request the HTTP server launches one instance of your program.

The “fast way” is FastCGI. It uses a different approach to achieve the same goal. An instance of the program is launched to serve a request. But at the end, the program stay alive and can be reused by the HTTP server to serve another request. The second request is faster because the program is already loaded into memory, had already read its configuration file and initialized any static data. It could also kept some information from the previous request and reuse it. To server many simultaneous requests, the HTTP server can launch many programs. Commonly, a FastCGI program will stop after a number of request and the server will launch another. Implementing a FastCGI program is more difficult because it uses more complex mechanism than just environment and standard output.

The “tightly coupled way” is to use a plug-in, a module of the HTTP server. The module is load at server start and keep running until the server stops. It can directly process the request but it can also use some threads or run an external program. It’s like a FastCGI program but can be faster in some situation.

The mod_python project brings to use an Apache module useful to run Python scripts from Apache. It’s the best way to serve Python-generated pages.

Here, I will present you how to be ready with mod_python.

First steps

Installing

To serve Python-generated pages, you need:

  • An Apache server
  • A Python interpreter
  • mod_python which can be download from Apache

When you download mod_python, you need to choose the right version that match you Apache version and your Python version. For example, to install mod_python 3.3.1 under Windows 32 bits with Apache 2.2 and Python 2.5 installed, you need to download mod_python-3.3.1.win32-py2.5-Apache2.2.exe.

If you use Debian, you just have to install libapache2-mod-python.

1
apt-get install libapache2-mod-python

Configuring Apache

To enable mod_python, you need to load its module in your Apache configuration.

1
LoadModule python_module modules/mod_python.so

Debian offers you a easiest command:

1
a2enmod python

Now, you can enable Python for one virtual host using:

1
2
3
4
5
6
7
8
9
10
<VirtualHost *:80>
    ServerName python
   
    DocumentRoot "/var/www/python"
    SetHandler mod_python
    PythonHandler mod_python.testhandler
    <Directory "/var/www/python">
        Allow from all
    </Directory>
</VirtualHost>

This is a “test” configuration which allow you to verify your installation. The SetHandler directive instructs Apache to use mod_python to process all requests (even non Python scripts). The PythonHandler directive instructs mod_python to use the testhandler.py script (included with mod_python) to process all requests. If you open a browser to your virtual host, you will see a page with information about you installation (environment, configuration…). Now, mod_python is ready.

How it works

To run your script, you must configure Apache to use mod_python. Then you must configure mod_python to run an handler. But what is an handler?

Python-handler

A Python-handler is a Python script that process the request. It must have a function named handler that takes one argument, the request. This object allows the script to read data and information from the client and to send data to it.

For example, the following script answerHello world:

1
2
3
4
5
from mod_python import apache

def handler(req)
    req.write("Hello world")
    return apache.DONE

In this script, the apache import gives access to the Apache internals. You can find more information in the documentation.

A Python-handler can be activated using the PythonHandler directive. It takes the name of the script (without extension). mod_python will search it through the Python-path. This path can be changed using the PythonPath directive. But configuring a Python handler is not enough. Apache must call mod_python.

Calling mod_python

Apache use also handlers to process request. When a request arrives, Apache choose one handler depending on its configuration and gives the control to it. Apache includes some handler like the default handler which serves the files as-is (useful for static pages…).

An handler can be associated with a file extension using the AddHandler directive. It can be associated with a MIME type using Action. Finally, an handler can be associated with all requests using SetHandler.

What can I do

Okay, now we know how it works. But what can I exactly do?

One script, one URL

If you have many script and you want to be able to call all of them you can:

  • Associate one script to one URL using a Location block,
  • Create an handler which run the script denoted by the URL.

Using the first approach, your Apache configuration file will become difficult to maintain. And you will have to edit the configuration for each new script. If you really want to try, here is an example of configuration:

1
2
3
4
5
6
7
8
AddHandler mod_python .py
PythonPath "[sys, 'D:/WWW/python']"
<Location "/a.py">
    PythonHandler a
</Location>
<Location "/b.py">
    PythonHandler b
</Location>

The second way is easy to do: mod_python includes a Python-handler for that. It is called publisher. This handler exposes each function of your Python script as a sub-URL.

For example, if you have a script named world.py with a hello function, you can call /world.py/hello to call it. The function can directly return the page content. The world.py script can be:

1
2
def hello(req):
    return "Hello"

If your script contains an index function, it will be called when the script is directly called (eg. /world.py). You can also activate the MultViews option (and MultiviewsMatch Handlers) to forget the .py and have more friendly URL.

The resulting Apache configuration is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DocumentRoot "/var/www/python"
AddHandler mod_python .py
PythonHandler mod_python.publisher
<Directory "/var/www/python">  
    Allow from all
    Options MultiViews
    MultiviewsMatch Handlers
</Directory>
[cc]

<h3>One script, all URL</h3>

You can also process all requests with one Python script. In my company, we use it to forward the requests to another server, processing them before. Your Python scripts need to be an handler as defined early. For this section, our handler will be <em>myapp.py</em>:
[cc lang="python"]
from mod_python import apache

def handler(req):
    req.content_type = "text/plain"
    req.write("Called page is %s" % req.unparsed_uri)
    return apache.DONE

This handler will show the URL used to call the handler: the URL http://localhost/some/path will return Called page is /some/path.

In the Apache configuration, you must define mod_python as the handler for all requests using SetHandler. Then, you set handler.py as the Python handler and add the path of this script to the Python-path (using PythonPath). Your configure will look like:

1
2
3
4
5
6
7
DocumentRoot "/var/www/python"
SetHandler mod_python
PythonPath "[sys, '/var/www/python']"
PythonHandler handler
<Directory "D:/WWW/python">
    Allow from all
</Directory>

Mixed configuration

The Python directives can be located in Directory blocks or Location. So you can define one Python handler per URL or per folder. You can also mix the previous examples to create a more complex architecture.

Compiling your scripts

Sometimes you need to avoid changes in your script. For example, if you sell your script or want to offer support to them but to be sure nobody changed it. But scripts can be easy modified by design.

One way, but not the best, is to compile your Python script using the py_compile library. If you want to compile the handler.py script, you just have to call:

1
2
import py_compile
py_compile.compile('handler.py')

This script must be run from the same folder of the handler.py script. It will produce a handler.pyc. The interpreter can be called with the -OO option to optimize your code (the output file will end with .pyo). mod_python supports these two type of files so you can replace your original Python script by one of them. But, be sure to use exactly the same version of Python to produce these files and to run them.

Conclusion

mod_python offers many ways to integrate Python to Apache. It supports other options like associating handler to a particular file extension or authenticating requests. Using this module, you can have the same power of many application server like the Java-one. But you will have a really good solution: a robust and secure server linked to a simple and flexible language.

Comments are closed.