Python DevCenter
oreilly.comSafari Books Online.Conferences.


Getting Started with WSGI

by Jason R. Briggs

What is WSGI, and why should you care? WSGI, or the Web Server Gateway Interface, is a Python Enhancement Proposal (#333) authored by Philip Eby in an attempt to address the lack of standardization among Python web frameworks. Think of it as the servlet spec for the Python world, only simpler. Although the WSGI specification is primarily aimed at framework developers, you can also develop application components to it. One of the aims of PEP 333 is ease of implementation, which consequently carries through to the development of those components. For example, the "Hello world" example given in the WSGI specification could hardly be any simpler to implement:

def simple_app(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type','text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

With the recent release of Python 2.5, the reference implementation of WSGI is available as a standard module (wsgiref), meaning that there is a direct path from developing application components to test and production environments. It's easy to imagine rapid development of components using wsgiref, then using something more scalable for testing and production. That said, WSGI doesn't provide many features you might expect or want for webapp development; for sessions, cookies, and the like, you'd also need a suitable web framework (of which there are many), or perhaps middleware or utilities to provide those services.

As to why you should care: if you're a low-level person who prefers to bolt on utilities and modules to keep your development effort as free of constraint as possible, WSGI will hold considerable attraction. Even if you prefer the benefits offered by higher-level frameworks, chances are they'll be built on top of WSGI, and it's always useful to know what happens behind the scenes.

Installing wsgiref

For those (like myself) running Python 2.4.x, the good news is that the wsgiref module will still function. wsgiref is available from the Python Subversion repository, or you can download it from the command line via:

svn co

Copy the wsgiref directory into the site-packages directory of your Python distribution (in my case, /usr/lib/python2.4/site-packages/) and check whether you can import the module. If so, you should be able to type import wsgiref in the Python console with no errors reported.

Testing the "Hello world" application shown earlier requires a few extra lines of code (see

if __name__ == '__main__':
    from wsgiref import simple_server
    httpd = simple_server.make_server('', 8080, simple_app)
    except KeyboardInterrupt:

This uses simple_server (an implementation of the BaseHttpServer module) to provide basic web server facilities, and passes the name of the simple_app function as an argument to the make_server function. Run this program (python and direct your browser to http://localhost:8080 to see it in action.

And Using an Object...

You don't have to stick to simple functions for your applications--WSGI supports object instantiation for handling requests. To do so, create a class that implements the __init__ and __iter__ methods. For example, I've abstracted out some basic utilities in the following class. The __iter__ method checks for a do_ method matching the type of HTTP request (GET, PUT, etc.) and either calls that method to process, or sends an HTTP 405 in response. In addition, I've added a parse_fields method to parse the x-url-form-encoded parameters in the body of a request using the standard cgi module. Note that, for both object instantiation and simple method calls, the arguments (environ and start_response) are positional--the order is important, not the name.

import cgi

class BaseWSGI:

    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        method = 'do_%s' % self.environ['REQUEST_METHOD']
        if not hasattr(self, method):
            status = '405 Method Not Allowed'
            response_headers = [('Content-type','text/plain')]
            self.start(status, response_headers)
            yield 'Method Not Allowed'
            m = getattr(self, method)
            yield m()

    def parse_fields(self):
        s = self.environ['wsgi.input'].read(int(self.environ['CONTENT_LENGTH']))
        return cgi.parse_qs(s)

I can then subclass BaseWSGI to create a simple number-guessing application (

import random
number = random.randint(1,100)

class Test(BaseWSGI):
    def __init__(self, environ, start_response):
        BaseWSGI.__init__(self, environ, start_response)
        self.message = ''
    def do_GET(self):
        status = '200 OK'
        response_headers = [('Content-type','text/html')]
        self.start(status, response_headers)
        return '''
        <form method="POST">
            <p><input type="text" name="myparam" value="" />
            <p><input type="submit" /></p>
''' % self.message
    def do_POST(self):
        global number
        fields = self.parse_fields()
        if not fields.has_key('myparam'):
            self.message = 'You didn't guess'
            return self.do_GET()
        guess = int(fields['myparam'][0])
        if guess == number:
            self.message = 'You guessed correctly'
            number = random.randint(1,100)
        elif guess < number:
            self.message = 'Try again, the number is higher than your guess'
            self.message = 'Try again, the number is lower than your guess'
        return self.do_GET()

You may be thinking that all of this is somewhat like reinventing the wheel--which is true, to a point. However, the low-level nature of WSGI is designed to make implementing frameworks a straightforward process--and more standardized. If you don't want to reinvent the wheel from an application perspective, look to a higher-level web framework, but do read on for some alternatives.

Pages: 1, 2, 3

Next Pagearrow

Sponsored by: