Tutorial: Building Middleware in Python with WebOb and Paste

Post to Twitter

Roaming about the Internet looking at OpenStack information I stumbled upon a slideshow that has some good starter code for a simple Python WSGI application using WebOb and Paste. I decided to take what the author started there and turn it into a simple tutorial for those new to WebOb and Paste.

In today’s article we will walk through how to setup and configure your WSGI application to use these technologies to build a very simple Middleware filter.


To quote Wikipedia: “WSGI Middleware is Python code that receives a WSGI request and then performs logic based upon this request, before passing the request on to a WSGI application or more WSGI middleware”

For this article I’m using Ubuntu 13.04, Python 2.7.x and PyCharm (any editor you prefer will work).

The first step is to create a new project. I’m using Virtualenv to keep my projects organized as well as to keep any installed packages in their own project rather than polluting the global site-packages.

Using virtualenv from the terminal I created my project like this:

$ mkdir PythonProjects && cd PythonProjects
$ virtualenv MiddlewareExample
$ cd MiddlewareExample
$ source bin/activate
$ pip install Paste WebOb PasteDeploy
$ mkdir src && cd src
$ touch app.py
$ touch paste.ini

I added two files to the project: app.py (where the WSGI and middleware code will live) and a configuration file I named paste.ini

Project Structure

To begin with let’s just get a simple WSGI application running and from there we can add the middleware piece.

Inside the app.py:

from webob import Response
from webob.dec import wsgify
from paste import httpserver
from paste.deploy import loadapp


@wsgify
def application(req):
    return Response('Hello World')


def app_factory(global_config, **local_config):
    return application


wsgi_app = loadapp('config:/home/chadlung/PythonProjects/MiddlewareExample/src/paste.ini')
httpserver.serve(wsgi_app, host='127.0.0.1', port=8080)

On the application function you can see the @wsgify decorator. All this function does is return the text Hello World. The app_factory function simply returns the application. All that’s left is to populate the paste.ini file and point it to our entry point in the app.py file.

Here is the paste.ini file contents:

[app:main]
paste.app_factory = app:app_factory

Go ahead and run the code:

$ python app.py

Check out the results in a web browser: http://127.0.0.1:8080

Okay, now we will add in some middleware. It won’t do anything cool, but it will show you how you easily you can add in middleware to your web projects.

Using the @wsgify.middleware decorator we can add another function:

@wsgify.middleware()
def my_filter(req, app):
    # just print a message to the console
    print('my_filter was called')
    return app(req)

We then create another factory function to return the new filter:

def filter_factory(global_config, **local_config):
    return my_filter

The entire app.py code will look like this:

from webob import Response
from webob.dec import wsgify
from paste import httpserver
from paste.deploy import loadapp


@wsgify
def application(req):
    return Response('Hello World')


@wsgify.middleware()
def my_filter(req, app):
    # just print a message to the console
    print('my_filter was called')
    return app(req)


def app_factory(global_config, **local_config):
    return application


def filter_factory(global_config, **local_config):
    return my_filter


wsgi_app = loadapp('config:/home/chadlung/PythonProjects/MiddlewareExample/src/paste.ini')
httpserver.serve(wsgi_app, host='127.0.0.1', port=8080)

The only thing left to deal with is to modify the paste.ini to use the filter and to ensure it is used on incoming requests (ordering in the pipeline:main is important!):

[pipeline:main]
pipeline = myfilter myapp

[app:myapp]
paste.app_factory = app:app_factory

[filter:myfilter]
paste.filter_factory = app:filter_factory

Notice how we are now using pipeline:main versus app:main from before. The names that pipeline point to are then configured below and in turn those point to their appropriate factories.

Kill any previous instance if its still running. Try the new code out:

$ python app.py

When you hit http://127.0.0.1:8080 you will see in the console the message: my_filter was called

Obviously, your own middleware will do more important things like rate limiting, authentication, etc. just to name a few but this serves as a good start to understand the basics.

Post to Twitter

This entry was posted in Open Source, Python. Bookmark the permalink.

One Response to Tutorial: Building Middleware in Python with WebOb and Paste

  1. Yue says:

    Nice example!
    It is helpful to me, thanks!
    The paste.ini lost code format at the end of this article:
    filter_factory seems lost the underline.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>