python -m SimpleHTTPServer is a very handy command that sets up a server listening on a port (by default 8080) having as document root the current directory. However it does not count for the purpose of this article: producing our first dynamic page with Python.
We won't care a lot about the performance of various approaches or whether to adopt frameworks like Django: we are just getting started. Even when higher-level abstraction are introduced, we still have to know how the foundation of our house are made.
mod_python is an Apache module that bridges the Apache 2 httpd processes with the Python interpreter. You can install it on Ubuntu with a single command:
sudo apt-get install libapache2-mod-python
See the mod_python website for alternative ways of installation.
In the words of its homepage, mod_python is a mature but not dead project: it requires very little maintenance due to its stability. It is of course built only for Apache, and exposes its API; in any case, it promises to be faster than CGI (who doesn't?)
This configuration lines should made their way into your httpd.conf or in the other configuration files you have on your system:
LoadModule python_module /usr/lib/apache2/modules/mod_python.so <Directory /var/www/python> AddHandler mod_python .py PythonHandler hello PythonDebug On </Directory>
We loaded the .so file of the module: if you installed mod_python in a Linux distribution, this line should already be present (use the a2enmod command if it's not in the enabled/ directory). In Ubuntu, it's written in the /etc/apache2/mods-enabled/python.load file, where you can add the other lines.
The rest of the configuration means that in the directory /var/www/python, we wwant to enable the processing of .py files by mod_python. The hello.py file will be targeted, unless you use a standard handler.
PythonDebug On activates a nice stack trace visualization in the browser, in case of runtime errors. Without it, the production setting is to display a blank, generic 500 Server Error.
Our Hello World it's easy to write:
from mod_python import apache def handler(req): req.content_type = 'text/plain' req.write("Hello, World!") return apache.OK
It's a bit strange that the Content-Type has to be specified on the request object, but there is no response object anyway: all interaction has to be performed on req.
A bit less boilerplate?
You can use a standard handler to simplify even more your .py files, by changing the configuration to this:
Now the Hello World example becomes:
from mod_python import apache def say(req): return "Hello, World!"
and is displayed at http://localhost/python/hello.py/say.
While the parameters are actually kept in the request object, you can specify them as required, named parameters:
def say(req, name): return "Hello, " + name + "!"
http://localhost/python/hello.py/say?name=Giorgio will display:
Including other files
import is the standard Python instruction for including other code into the current source file, usually with the form from module import entity. You can use it in your handlers, but you probably need to specify Python paths specific to each of them, to avoid that multiple application clash with each other:
The string value of this directive should evaluate to a Python list containing all the folders to search for modules, just like sys.path originally does.
To show you how it works, I can put this in /var/python_code/hello_lib.py:
def say_to(name): return "Hello, " + name + "!"
and micro-refactor my handler to:
from mod_python import apache from hello_lib import say_to def say(req, name): return say_to(name)
Of course there's more to mod_python and to build real web applications: they have to manipulate cookies, the session API, and databases. Moreover, their configuration should access Apache internals, with directives for everything from importing automatically modules at the start of a request to setting up HTTP authentication.
However, this article is about the HTTP-facing side of a web application. It's interesting to explore how the architecture on the server side works, so probably my next stop will be to try out a framework like Django to see which standard design choices it mades for us (and which may be questionable). In any case, we already have a Turing-complete entry point in our handlers, capable of linking to any Python source code.
The official mod_python documentation is long-winded but very good.
The Style Guide for Python Code is a more general document that defines the coding standards for the whole Python world, while applying also to our web development scenario.