WebOb decorator
Lately I’ve been writing a few applications (e.g., PickyWiki and a revisiting a request-tracking application VaingloriousEye), and I usually use no framework at all. Pylons would be a natural choice, but given that I am comfortable with all the components, I find myself inclined to assemble the pieces myself.
In the process I keep writing bits of code to make WSGI applications from simple WebOb -based request/response cycles. The simplest form looks like this:
from webob import Request, Response, exc
def wsgiwrap(func):
def wsgi_app(environ, start_response):
req = Request(environ)
try:
resp = func(req)
except exc.HTTPException, e:
resp = e
return resp(environ, start_response)
return wsgi_app
@wsgiwrap
def hello_world(req):
return Response('Hi %s!' % (req.POST.get('name', 'You')))
But each time I’d write it, I change things slightly, implementing more or less features. For instance, handling methods, or coercing other responses, or handling middleware.
Having implemented several of these (and reading other people’s implementations) I decided I wanted WebOb to include a kind of reference implementation. But I don’t like to include anything in WebOb unless I’m sure I can get it right, so I’d really like feedback. (There’s been some less than positive feedback, but I trudge on.)
My implementation is in a WebOb branch, primarily in webob.dec (along with some doctests).
The most prominent way this is different from the example I gave is that it doesn’t change the function signature, instead it adds an attribute .wsgi_app which is WSGI application associated with the function. My goal with this is that the decorator isn’t intrusive. Here’s the case where I’ve been bothered:
class MyClass(object):
@wsgiwrap
def form(self, req):
return Response(form_html...)
@wsgiwrap
def form_post(self, req):
handle submission
OK, that’s fine, then I add validation:
@wsgiwrap
def form_post(self, req):
if req not valid:
return self.form
handle submission
This still works, because the decorator allows you to return any WSGI application, not just a WebOb Response object. But that’s not helpful, because I need errors…
@wsgiwrap
def form_post(self, req):
if req not valid:
return self.form(req, errors)
handle submission
That is, I want to have an option argument to the form method that passes in errors. But I can’t do this with the traditional wsgiwrap decorator, instead I have to refactor the code to have a third method that both form and form_post use. Of course, there’s more than one way to address this issue, but this is the technique I like.
The one other notable feature is that you can also make middleware:
@wsgify.middleware
def cap_middleware(req, app):
resp = app(req)
resp.body = resp.body.upper()
return resp
capped_app = cap_middleware(some_wsgi_app)
Otherwise, for some reason I’ve found myself putting an inordinate amount of time into __repr__. Why I’ve done this I cannot say.