转载 2011年10月13日 13:06:51


Getting Started with WSGI

written on Monday, May 21, 2007

I finally finished the written matura and have some more time to work on projects and write articles. One of the things I wanted to write for a long time is a WSGI tutorial that does not require a specific framework or implementation. So here we go.

What's WSGI?

Basically WSGI is lower level than CGI which you probably know. But in difference to CGI, WSGI does scale and can work in both multithreaded and multi process environments because it's a specification that doesn't mind how it's implemented. In fact WSGI is not CGI because it's between your web application and the webserver layer which can be CGI, mod_python, FastCGI or a webserver that implements WSGI in the core like the python stdlib standalone WSGI server called wsgiref.

WSGI is specified in the PEP 333 and adapted by various frameworks including the well known frameworks django and pylons.

If you are too lazy to read the pep 333 here's a short summary:

WSGI application are callable python objects (functions or classes with a __call__ method that are passed two arguments: a WSGI environment as first argument and a function that starts the response.
the application has to start a response using the function provided and return an iterable where each yielded item means writing and flushing.
The WSGI environment is like a CGI environment just with some additional keys that are either provided by the server or a middleware.
you can add middlewares to your application by wrapping it.
Because that's a lot of information let's ignore it for now and have a look at a basic WSGI application:

Extended Hello World

Here a simple, but not too simple example of a WSGI application that says Hello World! where World can be specified via url parameter.

from cgi import parse_qs, escape

def hello_world(environ, start_response):
    parameters = parse_qs(environ.get('QUERY_STRING', ''))
    if 'subject' in parameters:
        subject = escape(parameters['subject'][0])
        subject = 'World'
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ['''Hello %(subject)s
    Hello %(subject)s!

''' % {'subject': subject}]


As you can see the start_response function takes two arguments. A status string and a list of tuples that represent the response headers. What you cannot see because it's not used here and nowhere else is that the start_response function returns something. It returns a write function that directly writes to the webserver output stream. Because it bypasses middlewares (we'll cover that later) it's a terrible bad idea to use that function. For debugging purposes however it can be useful.

But how to start that application now? A webserver doesn't know how to handle that and neither does python because nothing calls that function. Because we're lazy we don't setup a server with WSGI support now but use the wsgiref WSGI standalone server bundled with python2.5 and higher. (You can also download it for python2.3 or 2.4)

Just add this to your file:

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    srv = make_server('localhost', 8080, hello_world)
When you now start the file you should be able to get a Hello John! on http://localhost:8080/?subject=John.

Path Dispatching

You probably worked with CGI or PHP before. If you did so you know that you most of the time have multiple public files (.pl / .php) a user can access and that do something. Not so in WSGI. There you only have one file which consumes all paths. Thus if you have your server from the previous example still running you should get the same content onhttp://localhost:8080/foo?subject=John.

The accessed path is saved in the PATH_INFO variable in the WSGI environment, the real path to the application in SCRIPT_NAME. In case of the development server SCRIPT_NAME will be empty, but if you have a wiki that is mounted on the SCRIPT_NAME variable would be /wiki. This information can now be used to serve multiple indepentent pages with nice URLs.

In this example we have a bunch of regular expressions and match the current request against that:

import re
from cgi import escape

def index(environ, start_response):
    """This function will be mounted on "/" and display a link
    to the hello world page."""
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ['''Hello World Application
               This is the Hello World application:

`continue <hello/>`_


def hello(environ, start_response):
    """Like the example above, but it uses the name specified in the
    # get the name from the url if it was specified there.
    args = environ['myapp.url_args']
    if args:
        subject = escape(args[0])
        subject = 'World'
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ['''Hello %(subject)s
            Hello %(subject)s!

''' % {'subject': subject}]

def not_found(environ, start_response):
    """Called if no URL matches."""
    start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
    return ['Not Found']

# map urls to functions
urls = [
    (r'^$', index),
    (r'hello/?$', hello),
    (r'hello/(.+)$', hello)

def application(environ, start_response):
    The main WSGI application. Dispatch the current request to
    the functions from above and store the regular expression
    captures in the WSGI environment as  `myapp.url_args` so that
    the functions from above can access the url placeholders.

    If nothing matches call the `not_found` function.
    path = environ.get('PATH_INFO', '').lstrip('/')
    for regex, callback in urls:
        match =, path)
        if match is not None:
            environ['myapp.url_args'] = match.groups()
            return callback(environ, start_response)
    return not_found(environ, start_response)
Now that's a bunch of code. But you should get the idea how URL dispatching works. Basically if you now visithttp://localhost:8080/hello/John you should get the same as above but with a nicer URL and a error 404 page if you enter the wrong url. Now you could improve that further by encapsulating environ in a request object and replacing the start_response call and the return iterator with a response objects. This is also what WSGI libraries like Werkzeug and Paste do.

By adding something to the environment we did something normally middlewares do. So let's try to write one that catches exceptions and renders them in the browser:

# import the helper functions we need to get and render tracebacks
from sys import exc_info
from traceback import format_tb

class ExceptionMiddleware(object):
    """The middleware we use."""

    def __init__(self, app): = app

    def __call__(self, environ, start_response):
        """Call the application can catch exceptions."""
        appiter = None
        # just call the application and send the output back
        # unchanged but catch exceptions
            appiter =, start_response)
            for item in appiter:
                yield item
        # if an exception occours we get the exception information
        # and prepare a traceback we can render
            e_type, e_value, tb = exc_info()
            traceback = ['Traceback (most recent call last):']
            traceback += format_tb(tb)
            traceback.append('%s: %s' % (e_type.__name__, e_value))
            # we might have not a stated response by now. try
            # to start one with the status code 500 or ignore an
            # raised exception if the application already started one.
                start_response('500 INTERNAL SERVER ERROR', [
                               ('Content-Type', 'text/plain')])
            yield '\n'.join(traceback)

        # wsgi applications might have a close function. If it exists
        # it *must* be called.
        if hasattr(appiter, 'close'):
So how can we use that middleware now? If our WSGI application is called application like in the previous example all we have to do is to wrap it:

application = ExceptionMiddleware(application)
Now all occouring exceptions will be catched and displayed in the browser. Of course you don't have to do that because there are many libraries that do exactly that and with more features.


Now where the application is "finished" it must be installed on the production server somehow. You can of course use wsgiref behind mod_proxy but there are also more sophisticated solutions available. Many people for example prefer using WSGI applications on top of FastCGI. If you have flup installed all you have to do is to defined a myapplication.fcgi with this code in:

from flup.server.fcgi import WSGIServer
from myapplication import application
The apache config then could look like this:

    Alias /public /path/to/the/static/files
    ScriptAlias / /path/to/myapplication.fcgi/
As you can see there is also a clause for static files. If you are in development mode and want to serve static files in your WSGI application there are a couple of middlewares (werkzeug and paste as well as "static" from Luke Arno's tools provide that) available.


Avoid the "Not Invented Here" problem and don't repeat yourself. Use the libraries that exist and their utilities! But there are so many! Which one to use? Here my suggestions:


Since Ruby on Rails appeared on the web everybody is talking about frameworks. Python has two major ones too. One that abstracts stuff very much and is called Django and the other that is much nearer to WSGI and called pylons. Django is an awesome framework but only as long as you don't want to distribute your application. It's if you have to create a webpage in no time. Pylons on the other hand requires more developer interaction and your applications are a lot easier to deploy.

There are other frameworks too but my experiences with them are quite bad or the community is too small.

Utility Libraries

For many situations you don't want a full blown framework. Either because it's too big for your application or your application is too complex that you can solve it with a framework. (You can solve any application with a framework but it could be that the way you have to solve it is a lot more complex than without the "help" of the framework)

For that some utility libraries exist:

Paste — used by pylons behind the scenes. Implements request and response objects. Ships many middlewares.
Werkzeug — minimal WSGI library we wrote for pocoo. Ships unicode away request and response objects as well as an advanced URL mapper and a interactive debugger.
Luke Arno's WSGI helpers — various WSGI helpers in independent modules by Luke Arno.
There are also many middlewares out there. Just look for them at the Cheeseshop.

Template Engines

Here a list of template engines I often use and recommend:

Genshi — the world's best XML template engine. But quite slow, so if you need a really good performance you have to go with something else.
Mako — stupidely fast text based template engine. It's a mix of ERB, Mason and django templates.
Jinja2 — sandboxed, designer friendly and quite fast, text based template engine. Of course my personal choice :D

WSGI rocks. You can simply create your own personal stack. If you think it's too complicated have a look at werkzeug and paste, they make things a lot easier without limiting you.

I hope this article was useful.


11.2.5、搭建RESTful API 之 实现WSGI服务的URL映射

问题引出:对于一个稍具规模的网站来说,实现的功能不可能通过一条URL来完成。如何定义多条URL,也是RESTful API考虑的问题。...
  • li_101357
  • li_101357
  • 2016年10月12日 22:43
  • 697


概述 WSGI接口包含两方面:server/gateway 及 application/framework。 server调用由application提供的可调用对象。 另外在server和...
  • frank_good
  • frank_good
  • 2016年06月08日 10:46
  • 1043


Python的WSGI不是框架不是模块,仅仅是一个规范协议,定义了一些接口,却影响着Python网络开发的方方面面。对于WSGI有这么一段定义:WSGI is the Web Server Gat...
  • WitsMakeMen
  • WitsMakeMen
  • 2014年02月20日 17:18
  • 7442


1. 什么是 urls.py本质上就是一个标准的Python文件,这个python文件的作用就是在URL请求和处理该请求的视图函数之间建立一个对应关系,换句话说,它就是一个url请求...
  • qq_26775359
  • qq_26775359
  • 2017年09月05日 10:02
  • 241


本例环境: 操作系统:CentOS6.5 1,安装这些环境 yum install mod_ssl 在目录/etc/httpd/conf.d下回生成一个新的conf文件ssl.conf 2,...
  • u013485123
  • u013485123
  • 2015年12月25日 14:03
  • 1131


看wsgi协议的定义,感觉都没整明白是什么意思。后来用了之后大致有了自己的理解,这里写一下自己的理解吧: wsgi有两方,服务器方 和 应用程序         ①服务器方:其调用应用程序,给...
  • NRC_DouNingBo
  • NRC_DouNingBo
  • 2012年12月19日 17:25
  • 5057

python web py入门-3-URL映射

本文介绍URL映射,在介绍映射之前,我们先了解下什么是URL? URL(Uniform/Universal Resource Locator的缩写,统一资源定位符)是对可以从互联网上得到的资源的位置和...
  • u011541946
  • u011541946
  • 2017年09月05日 21:28
  • 880

Python WSGI 介绍

一、写在前面         这篇文章主要介绍一下python WSGI, 学习python wsgi规范的时候读到了几篇介绍的很好的入门教程,内容不长,整理了一下。由于能力和时间有限,错...
  • u011521019
  • u011521019
  • 2015年05月10日 15:50
  • 3582


  • tantexian
  • tantexian
  • 2014年11月27日 15:57
  • 3050

web python -- WSGI接口GET请求

执行之前的程序,然后在浏览器中打开 http://localhost:8051/?a=10&b=w&b=r 这样的url。 环境变量字典中保存了请求信息REQUEST_METHOD和QUER...
  • shanzhizi
  • shanzhizi
  • 2015年06月22日 22:40
  • 1874