Alexander Guz

Introduction to WSGI

The Web Server Gateway Interface (WSGI) is a universal interface between web servers and web applications for Python. Universal means there is no more need to design your application for a specific API interface of a web server like CGI, FastCGI, mod_python, etc. - implementing WSGI support in your application gives you opportunity to use it with any web server which in it’s turn has WSGI support.

I highly recommend reading PEP333 with full specification, here is just a bare minimum:

  • the WSGI interface has two sides: the server/gateway and the application side; beside this strict distinction there are so called “middleware” components which implement server and application sides at the same time;
  • WSGI application is a callable object (a function, method, class, or an instance with a __call__ method) that accepts two positional arguments: WSGI environment variables and a callable with two required positional arguments which starts the response;
  • the server side invokes the callable object which returns response.

Basic example

Here is a simple example.

from urlparse import parse_qs

class Greetings:
    def __call__(self, environ, start_response):
        params = parse_qs(environ.get('QUERY_STRING'))
        name = 'Alex'
        if 'name' in params:
            name = params.get('name')[0]
        start_response('200 Ok', [('Content-type', 'text/plain')])
        return ['My name is {0}'.format(name)]

This application greets someone called Alex (me actually) or a person whose name is specified via url. The Greetings class is our callable object with two required positional arguments which is invoked by a server side. Pay attention to the fact that there are other variants of implementing a callable object: we could create a class with __iter__ method which would yield the result, or, simply, create a function without any classes. Regardless of your choice of a callable object it’s first parameter is an environment dictionary object. As the second parameter it accepts a callable which starts response and is invoked with two parameters: status string and a list of tuples with headers information.

In order to test this application we can use any web server with WSGI support. For tests purposes there is a wsgiref module from standard Python library. At the end of our file:

if __name__ == "__main__":
	from wsgiref.simple_server import make_server
	httpd = make_server('', 8005, Greetings())
	httpd.serve_forever()

After starting the app and pointing your browser to http://localhost:8005/?name=John you will see a request log in the console.

$ python wsgi.py 
1.0.0.127.in-addr.arpa - - [01/Mar/2013 21:36:40] "GET /?name=Jonh HTTP/1.1" 200 15

What is a middleware?

Middleware applications play the role of a server for their contained applications and, at the same time, look like an application to their containing server. They can be used to:

  • routing to a different URL based on environ parameters;
  • logging;
  • handling exceptions;
  • perform any kind of preprocess/postprocess operations.

Middleware application acts like a wrapper around another application, which in it’s turn can could be a middleware for another application and so on. In this case you create a so called “middleware stack”. Let’s have a look at the example - our application will add to the result of it’s underlying application a string with information about the size of the result.

class ResultMiddlware:
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        # the size of the result returned by self.app
        size = 0

        # call the application
        appiter = self.app(environ, start_response)
        for r in appiter:
            size += len(r)
            yield r

        yield '\n\nResult length: ' + str(size)

        # the application might define a close method
        # which must be called
        if hasattr(appiter, 'close'):
            appiter.close()

And change the invoking part:

if __name__ == "__main__":
	from wsgiref.simple_server import make_server
    application = ResultMiddlware(Greetings())
    httpd = make_server('', 8005, application)
    httpd.serve_forever()

Conclusion

WSGI is no doubt a great piece of technology which brings some standardization to a zoo of existing technologies in Python web area. But as it is rightly mentioned in PEP333 WSGI is a tool for frameworks and server developers, and is not intended to directly support application developers. Thus consider using existing libraries and frameworks with WSGI support for your applications. Have a look at Werkzeug and Flask, it’s a good point to start from (or maybe stop at).