Making your Twisted resource(s) a url sub-tree of your WSGI resource...

For those paddling about in the Twisted + WSGI-hosted app world (here I'm playing with TurboGears), one thing you may wind up wanting to do is to have your WSGI application be the "default" URL-tree, with only certain sub-trees handled by Twisted.  To be more concrete, say you want your URLs to look like this:

/* -> WSGI URL handling for all urls except
/channels -> Twisted long-polling protocol (or Soap, or XMLRPC, or whatever

Normally (i.e. with normal twisted.web resources), you'd do either a putChild or write a getChild handler.  putChild won't work on a WSGI resource, however, nor does it provide a getChild that you could delegate to... so you'll have to do some hacking here.

You create a root resource with a getChild, but you warp the request's paths so that your root controller doesn't "consume" any path fragments.  You can use putChild() on the root resource to put "channels" in (or any other root name, such as "RPC"), and only undefined children will be delegated to the WSGI.

class Root( Resource ):
"""Root resource that combines the two sites/entry points"""
WSGI = None
def getChild( self, child, request ):
request.prepath.pop()
request.postpath.insert(0,child)
return self.WSGI
def render( self, request ):
"""Delegate to the WSGI resource"""
return self.WSGI.render( request )

The WSGI child is "promoted" to the root by being delegated to for both the root render and the lookup of all non-statically-defined root url fragments.  Enjoy.

Comments

  1. Alex Clemesha

    Alex Clemesha on 01/25/2010 3:32 p.m. #

    Did you independently think up this way of doing things, or were you inspired by other code you found on the internet?

    I ask this because I've been using the method you described for a good while now (see here: http://github.com/clemesha/twisted-wsgi-django/blob/master/twresource.py) but I always wondered if there was "a better/cleaner way". Thanks for any opinions you have.

  2. Mike C. Fletcher

    Mike C. Fletcher on 01/25/2010 3:56 p.m. #

    I based the rewriting on the twisted.web rewriting-rule code: http://twistedmatrix.com/trac/browser/tags/releases/twisted-9.0.0/twisted/web/rewrite.py#L7

    I ran into the problem, poked at making the WSGI container host a child resource, decided that was too messy. Googled for how to revert the path on a request (finding the RewriterRule code), then created a root that had its getChild() choose the WSGI or the Twisted resource, rewriting the path if the WSGI was chosen.

    Then realized that you didn't need the getChild for the Twisted resource, then realized that the whole pattern was just a delegate.

    Can't say I think the rewriting of the path is particularly elegant (mucking about with internals), but as I say, its taken from Twisted.web, so I'll take it as idiomatic-enough for now :) .

  3. ssc

    ssc on 09/02/2011 5:17 a.m. #

    just what i was looking for. thanks for posting this! :-)

    why do you have WSGI = None in the code ? is that to confuse newbies ? you do need a wsgi resource after all, don't you ?!?

    WSGI = WSGIResource(reactor, reactor.getThreadPool(), application)

    also, a sample static resource handled by twisted would show how this splits up into twisted / wsgi, e.g.

    def __init__(self):
    Resource.__init__(self)
    self.putChild('tmp', File('/tmp'))

    http://localhost/tmp is then indeed handled by twisted, right ? (just asking to check if i've really understood what's going on there...)

Comments are closed.

Pingbacks

Pingbacks are closed.