XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Introducing WSGI: Python's Secret Web Weapon
by James Gardner | Pages: 1, 2

Hello World!

Here is a simple CGI application that would produce a Hello World! message in the browser:

    print "Content-Type: text/html\n\n"
    print "<html><body>Hello World!</body></html>"

and here is the same application as a WSGI application:

    def application(environ, start_response):
        start_response('200 OK',[('Content-type','text/html')])
        return ['<html><body>Hello World!</body></html>']

At first glance, this looks a little complicated, so let's think about what information a server needs from an application and vice versa. First of all, an application needs to know about the environment in which it is running. In a CGI application, you can obtain this information with this code:

    import os
    environ = os.environ

In a WSGI application, this information is supplied directly by the WSGI server as the first parameter to the application callable; in our example above, this parameter is named environ.

The server also needs to know the status and headers to set before any content is returned to the browser, so it supplies a second argument to the application -- a callback function taking the status and a list of tuple pairs of headers as arguments. In our example, it is named start_response. Our application calls start_response() to set a status of 200 OK, which means everything went fine, and to set the Content-type header to text/html.

Finally, the server needs to know the content to return to the browser. Rather than requiring an application to return all the content in one go, the server iterates over the application, returning data to the browser as the application returns it. In our example, this result is achieved by returning a simple list of strings containing the HTML to display the Hello World! message.

In summary then, a WSGI application is any callable (in our case, a simple function) taking an environment dictionary and start_response callable as positional parameters. The application should call start_response() with a status code and a list of tuple pairs of headers before it returns an iterable response (in our case, a list with just one string). In normal circumstances, applications can only call start_response() once; after all, it wouldn't make a lot of sense to start the response twice.

These requirements for applications can also be met by generators, classes that override the __call__() method or the __iter__() method. It is possible to use these as WSGI applications instead of following the example above and using a simple function.

The WSGI specification also discusses a third parameter to start_response() named exc_info, used in error handling, and a writable object returned by start_response(), used for backward compatibility -- not for use in new applications or frameworks. (We do not need to worry about these details, but they are mentioned for completeness).

Testing the Application

We will start by running our test application as a CGI script. Create a new file named test.py and add the following to it:

    def application(environ, start_response):
        start_response('200 OK',[('Content-type','text/html')])
        return ['<html><body>Hello World!</body></html>']

    from wsgiref.handlers import CGIHandler
    CGIHandler().run(application)

If you are running a version of Python prior to 2.5, you will need to download and install the wsgiref package. You can do this by extracting the wsgiref-0.1.2.zip file (or most recent update) and executing the command:

    > python setup.py install

You can then run the test application from the command line to see what information it outputs:

    > python test.py

You will see the following:

Figure 3
Figure 3. Test output (Click for full-size image).

Notice that the CGIHandler has acted the way any other CGI server would, and added the Status and Content-Length to the output.

Of course, one of the original motivations for the WSGI was to run the same application on multiple WSGI servers without modification. As an example, this is how you would serve the same application using FastCGI instead of CGI, using the flup package:

    from flup.server.fcgi import WSGIServer
    WSGIServer(application).run()

You can also run this same, unmodified application on all other WSGI-compliant servers.

The environ Dictionary

We've seen how to write a simple WSGI application and deploy it as a CGI script or use it with FastCGI, but to write any real application, we'll need access to the environment. This is provided by the first parameter passed to the application by the server, the environ dictionary.

The environ dictionary contains all the information about the environment and the request that the application needs. Below is an application named show_environ that displays a list of all the keys in the environ dictionary:

    def show_environ(environ, start_response):
        start_response('200 OK',[('Content-type','text/html')])
        sorted_keys = environ.keys()
        sorted_keys.sort()
        return [
            '<html><body><h1>Keys in <tt>environ</tt></h1><p>',
            '<br />'.join(sorted_keys),
            '</p></body></html>',
        ]
        
    from wsgiref import simple_server
    httpd = simple_server.WSGIServer(
        ('',8000),
        simple_server.WSGIRequestHandler,
    )
    httpd.set_app(show_environ)
    httpd.serve_forever()

This example serves the application using a standalone server. If you run this example, you will be able to see the output from the application by visiting http://localhost:8000. You will notice that environ contains all the usual keys you would expect to see in a CGI environment, such as PATH_INFO, REMOTE_ADDR, etc. It also contains the Web Server Gateway Interface keys wsgi.errors, wsgi.file_wrapper, wsgi.input, wsgi.multiprocess, wsgi.multithread, wsgi.run_once, wsgi.url_scheme, and wsgi.version, which provide information about the WSGI environment. The use of these variables is described in the environ variables section of the specification.

The wsgi.errors key contains an output stream (filelike object). Any information written to the output stream is sent by the server to its error log. This can be useful for debugging purposes, as well as issuing errors or warnings, and it can be used like this:

    environ['wsgi.errors'].write('This will be sent to the error log')

Adding this line to the example above, restarting the server, and refreshing the page outputs the message to the console as expected:

Figure 4
Figure 4. Console (Click for full-size image).

From the information in environ, you can build any web application, and tools already exist to simplify common tasks, such as extracting the variables submitted in a form from the QUERY_STRING or rebuilding the original URL. Have a look at Paste for the lower-level tools or Pylons for a full WSGI framework.

The environ dictionary can actually contain one other type of key: a key defined by the server or a middleware component. Such keys are named using only lowercase letters, numbers, dots, and underscores, and are prefixed with a name that is unique to the defining server or middleware. In Part II of this article, we will look at WSGI middleware and how to add it to existing applications. We will also see an example of a middleware component that adds a key to the environ dictionary to provide session functionality to an application.



1 to 2 of 2
  1. Notes
    2006-09-28 11:05:00 Uche Ogbuji
  2. Cue the Clancy Brothers:
    2006-09-28 04:42:14 smitty_one_each
1 to 2 of 2