How I got Routes working with web.py

158 views
Skip to first unread message

Jon Rosebaugh

unread,
Mar 10, 2006, 10:01:28 AM3/10/06
to we...@googlegroups.com
Routes (http://routes.groovie.org/) is a nifty method for converting
URLs to controllers/actions and back. After the nth time I got annoyed
at web.py's regexp url tuple, I decided to see what it would take to
get Routes working in web.py.

I'm certainly not saying this is the only way to do it, or that it's
bug-free. I'm just saying that it worked for me. For a demonstration,
I re-implemented the "complete web.py application" found in the
upper-right corner of http://webpy.org/
It's kinda long, but much of it could be spun off into a
webpyroutes.py module or something.

A couple things to keep in mind with this: first, any class that would
have been mapped to a url in your urls tuple should now subclass from
the controller class. Secondly, variables (which would previously have
been captured by grouping in the url regexp) are now passed into the
GET or POST method as keyword arguments, not positional arguments.

It's a litttle more work up front, and is therefore hardly worthwhile
for the little sample application I used here, but I think it's better
than web.py's urls tuple for larger apps.

import web
from routes import *

ourmapper = Mapper()

# These parts could safely be spun off into their own module.
class controller(object):
registry = {}
class __metaclass__(type):
def __init__(cls, name, bases, dict):
if not ('register' in dict):
cls.register(cls)
return
@classmethod
def register(cls, thecls):
cls.registry[thecls.__name__] = thecls
return
@classmethod
def subclasses(cls):
return cls.registry.keys()

from inspect import getargspec
def handler():
matchdict = ourmapper.match(web.context.path)
if matchdict is None:
return web.notfound()
cls = controller.registry[matchdict['controller']]
web.context.mapper = ourmapper
web.context.mapper_dict = matchdict
web.context.protocol = web.context.method # Routes expects this
under a different name
web.context.redirect = web.redirect # Likewise
meth = web.context.method
if meth == "HEAD":
if not hasattr(cls, meth): meth = "GET"
if not hasattr(cls, meth): return web.nomethod(cls)
tocall = getattr(cls(), meth)
kwargs = matchdict.copy()
del kwargs['controller']
# The match dictionary contains 'action', even if that was never
specified in the route. I'm not sure why this occurs (I think for
backwards compatibility with something), but we'll remove it here if
the function isn't expecting it.
if 'action' in kwargs and kwargs['action'] == 'index' and 'action'
not in getargspec(tocall)[0]:
del kwargs['action']
return tocall(**kwargs)

config = request_config()
if hasattr(config, 'using_request_local'):
config.request_local = lambda: web.context # Routes has its own
threadsafe context tuple, but we can configure it to use web.py's.
config = request_config()
# We've come to the end of the parts to spin off into a separate module.

ourmapper.connect(':name', controller = 'hello')
ourmapper.connect('', controller='hello', name=None) # the empty path
is a special case in Routes

class hello(controller):
def GET(self, name):
i = web.input(times=1)
if not name: name = 'world'
for c in xrange(int(i.times)): print 'Hello,', name+'!'

ourmapper.create_regs(controller.subclasses()) # This should come
after all your controllers, just before the call to web.run.

if __name__ == "__main__": web.run(handler)

Reply all
Reply to author
Forward
0 new messages