From PHP to Python : Getting started with mod_wsgi

This article explores the process of transitioning from a PHP environment to a python one.

In a recent project we started at 6PM Infrastructure, we have a very interesting and complex problem which requires an interesting mix of cross-platform standalone applications (programs which are run as standalone applications, called from the command line that run in both Linux and Windows environments, for example “python exampleProgram.py –username=dvassallo“), as well as web applications (that is, an application which is called via normal HTTP GET and POST requests, for example “http://myserver.com/exampleProgram.py“). This mix of requirements made us favor python, even though initial PoCs were all written in PHP. The reason being that, especially in standalone applications, python far outshone PHP, since it was extremely easy to quickly write multi-proccessing and mulit-threaded applications. At the same time, python has several frameworks (which we are just beginning to explore) that can quickly and easily build the web applications that we need.

Coming fresh out of a PHP development exersize, it was a bit of a culture shift to get my head around Python. Thankfully, with the help of a very talented and patient developer, Alfred, we quickly got up and running. This blog post records my process of getting the web application half of Python up and running, more for my future reference than anything, but hopefully will help another dev… The following was performed on a plain vanilla Ubuntu 12.10 LTS server installation. The below was actually well documented in their own respective sites, the below also serves as a single repository for that documentation

– Installing apache, and mod_wsgi

The first thing we need is an environment within which to run python scripts and return the results to a client web browsing. My server of choice is still apache at the moment, and to get this running we do a usual:

apt-get install apache2 libapache2-mod-wsgi python2.7

this sets up the apache webserver (http://httpd.apache.org/), the python environment, and mod_wsgi (https://code.google.com/p/modwsgi/), which bridges the apache server to the python environment.

– Configuring apache to server python pages

I favor writing separate config files for apache. So, make sure you have the following line in your /etc/apache2/apache2.conf file:

Include conf.d/*.conf

We then place the following into /etc/apache2/conf.d/mod_wsgi.conf:

LoadModule wsgi_module modules/mod_wsgi.so

<Directory /var/www/wsgi>
Options ExecCGI
AddHandler wsgi-script .py
</Directory>

The above simply loads the wsgi module, and defines a folder (/var/www/wsgi), within which any file ending in .py will be executed by the wsgi module.

– Appeasing the dev gods: our first wsgi ‘Hello World’

To get a taste of python environment, we build our first hello world program, and we place the following into /var/www/wsgi/hello.py:

#!/usr/bin/env python

def application(environ, start_response):

status = ‘200 OK’

output = ‘<html><body>Hello World!</body></html>’

response_headers = [(‘Content-type’, ‘text/html’),
(‘Content-Length’, str(len(output)))]
start_response(status, response_headers)

return [output]

By default, as soon as you call a wsgi script, automatically the “application” function gets called, and gets passed two arguments, the more important of which is the environ argument, which contains the environment of the HTTP call (for example whether the call was a GET or POST, any cookies, and so on). I found the following blog post extremely useful in my first steps into the python web world:

http://webpython.codepoint.net/wsgi_tutorial

– Introducing Flask

Once you start developing in the above environment, you quickly start noticing a major deficiency between PHP any Python…. Python has no built in replacement for PHP’s sessions, which was a kind of deal breaker for us since we did not want to build an entire web enviornment from scratch. Enter Flask (http://flask.pocoo.org/docs/) a lightweight python framework targeted specifically to build web applications in python, including support for cookies, sessions, and so on…

So how do we get flask within the wsgi environment we built in our previous step? This is how I went about it…First we install flask on our ubuntu server, with a quick:

apt-get install python-flask

Next, we setup our python file that hosts our second “Hello World” code, run from within mod_wsgi on apache, built on the Flask framework…. we add the following code to myApp.py, the full path being: /var/www/wsgi/myApp.py:

from flask import Flask
app = Flask(__name__)

@app.route(‘/’)
def hello_world():
return ‘Hello World!’

@app.route(‘/dave’)
def myDave():
return ‘Hello World – From Dave’

if __name__ == ‘__main__’:
app.run()

In the above, we first import the flask module we’ve just installed, and define two “routers”. The first routes “/” to the hello_world function, which just returns the text “Hello World!”. The second route servers any requests to /dave to be served by the myDave function, which prints “Hello World – From Dave”. The last couple of lines run the main flask app. By default, Flask expects the main application to be called “app”. In fact the Flask documentation explicitly states that you shouldnt mess around with this and keep the main function called “app”

A keen reader will notice that mod_wsgi expects the main function called to be named application, while Flask expects it to be called app. How do we account for this difference? The Flask official documentation has a nice and easy way of doing this. We write a “wrapper file” which imports the above flask application into a mod_wsgi environment. We place the following into /var/www/wsgi/flask.py:

import sys
sys.path.append(‘/var/www/wsgi’)

from myApp import app as application

The first two lines import the sys module, and add the /var/www/wsgi directory to the python PATH environment. If this is omitted, upon attempting to run our script, apache will spit out error messages in the logs that basically tell you that the “myApp” module cannot be found.

The third line in our script tried to import out Flask application myApp with a special twist… we import the Flask “app” function but under a new alias called “application“. This is how we bridge Flask’s “app” to mod_wsgi’s “application”

Now, when we visit:

http://myserver.com/wsgi/flask.py, will result in “Hello World!”, while visiting http://myserver.com/wsgi/flask.py/dave will result in “Hello World – From Dave”, as expected

– Some quick tips

Unlike PHP, mod_wsgi caches python files so that subsequent requests to these file are served faster…. so do yourself a favour, and when you change any code in your python applications, make sure to restart the apache server to force it to reload the code with a normal /etc/init.d/apache restart