Understanding Python WSGI with Examples

Coming from a strong PHP background, initially, exploring the web development landscape whilst delving into Python seemed rather confusing. As Python was not originally developed for the web, a specification called PEP 333 was accepted, which standardised the required interface between web servers and Python web frameworks/applications. Despite the additional complexity, the manner in which middleware applications can be integrated, along with the server choices, offers possibilities that I find hard to locate a comparable alternative for in PHP.

Basic Example

Simply put, a WSGI (Web Server Gateway Interface) compliant application must supply a callable (function or class) which accepts an ’environ’ dictionary and a start_response function. For a familiar PHP comparison, you can think of the ’environ’ dictionary as a combination of $_SERVER, $_GET and $_POST, albeit with extra processing required. This callable is expected to invoke the start_response function with the desired response code and header data, and then return an iterable of bytes containing the response body.

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'Hello, world!']

if __name__ == '__main__':
    try:
        from wsgiref.simple_server import make_server
        httpd = make_server('', 8080, app)
        print('Serving on port 8080...')
        httpd.serve_forever()
    except KeyboardInterrupt:
        print('Goodbye.')

Using the single-threaded WSGI reference implementation provided with Python is a great choice for experimenting with these lower-level concepts. You will notice that as the example is written for Python 3, we must return an iterable (in this case a list) with declared ‘byte’ content inside.

Post Example

Now that we are familiar with the basic structure of a WSGI compliant application, we can experiment with a more practical example. Below, we provide the client with a simple form which posts a supplied ’name’ for the application to greet accordingly.

import cgi

form = b'''
<html>
    <head>
        <title>Hello User!</title>
    </head>
    <body>
        <form method="post">
            <label>Hello</label>
            <input type="text" name="name">
            <input type="submit" value="Go">
        </form>
    </body>
</html>
'''

def app(environ, start_response):
    html = form

    if environ['REQUEST_METHOD'] == 'POST':
        post_env = environ.copy()
        post_env['QUERY_STRING'] = ''
        post = cgi.FieldStorage(
            fp=environ['wsgi.input'],
            environ=post_env,
            keep_blank_values=True
        )
        html = b'Hello, ' + post['name'].value + '!'

    start_response('200 OK', [('Content-Type', 'text/html')])
    return [html]

if __name__ == '__main__':
    try:
        from wsgiref.simple_server import make_server
        httpd = make_server('', 8080, app)
        print('Serving on port 8080...')
        httpd.serve_forever()
    except KeyboardInterrupt:
        print('Goodbye.')

Although somewhat verbose, we have been able to create a simple web application which handles supplied POST data using the CGI module’s FieldStorage class. These are the very simplified building blocks used in popular frameworks such as Flask and Django.

Compiled Conversations podcast album art

Compiled Conversations

Podcast I host, featuring conversations with the people shaping software and technology.

Listen to Compiled Conversations