Getting WSGI Responses

Thanks to the WSGI spec and WebOb we can think about Python web applications as functions that take HTTP request as an argument and return HTTP response.

That’s what happens on the conceptual level, but at the code level WSGI makes it look quite different. Unfortunately the argument to the WSGI callable is not just a representation of HTTP request and the return value isn’t the response either. It’s a shame, but we can work around it. What for? Here are some of the use cases:

  • middleware
  • testing
  • request dispatching

First thing you should know is that every webob.Request instance has a .get_response method. It takes any WSGI app as an argument and returns a Response instance with all the data that WSGI app returned in response to the request.

>>> def some_wsgi_app(env, sr):
...     sr('200 OK', [('Content-Type', 'application/x-foo')])
...     return ['abc'] * 3
...
>>> req = Request.blank('/')
>>> resp = req.get_response(some_wsgi_app)
>>> resp
<Response at 0x1a85ab0 200 OK>
>>> print resp
200 OK
Content-Type: application/x-foo
Content-Length: 9

abcabcabc

After getting the Response object, we can edit the headers, body, set .conditional_response so it can serve ranges, in testing we can make assertions on the parts of the response we are testing etc.

This is great, but if you remember, we want to look at the apps as functions taking a request, so the req.get_response(some_wsgi_app) is a bit backwards. I’ll show two ways to correct that.

wsgi_to_func

The first one is to wrap the wsgi app in a decorator that changes its calling signature to req -> resp.

def wsgi_to_func(wsgi_app):
    def func(req):
        return req.get_response(wsgi_app)
    return func

The problem with this is that now the wsgi app gets two interfaces, the WSGI one and the wrapped one.

resp = app << req

The other solution will feel magic at first, but if you understand what’s happening, you’ll see that it’s not magical or fragile at all. What we do is subclass Request and add __rlshift__ = get_response. That makes the following work:

>>> Request.__rlshift__ = Request.get_response
>>> resp = some_wsgi_app << req
>>> resp
<Response at 0x1a859b0 200 OK>

What happens is that we overload the left-shift operator (<<) of the request object, when the request is the second argument. That makes app << req equivalent to req.get_response(app) but more concise. The order of the app and req in that expression also make much more sense and read out very similar to app(req).

The main benefit of this is that app can indeed be any wsgi app and unlike the wsgi_to_func it doesn’t need wrapping.

Anyway, even you decide not to use it, you’ve got to agree that this is a neat trick.