Switched over to Rick Copeland's iterative object-dispatch. Proved

everything still works with tests.
This commit is contained in:
Jonathan LaCour
2010-09-29 00:01:01 -04:00
parent 4061fa2f93
commit 972579b687
4 changed files with 69 additions and 64 deletions

8
README
View File

@@ -3,14 +3,6 @@ much much smaller, with many fewer dependancies.
TODO
====
* Change to alternate routing code lifted from Rick Copeland's
clutch:
http://code.google.com/p/pyclutch/source/browse/trunk/clutch/controller_lookup.py
This gives me support for _lookup controllers, which I don't
currently have.
* Switch "hooks" over to Context Managers.

View File

@@ -1,6 +1,7 @@
from templating import renderers
from webob import Request, Response, exc
from threading import local
from routing import lookup_controller
import string
@@ -45,59 +46,12 @@ class Pecan(object):
'xhtml' : 'text/html',
'json' : 'application/json'
}.get(format, 'text/html')
def route(self, node, path):
curpath = ""
nodeconf = {}
object_trail = [['root', self.root, nodeconf, curpath]]
names = [x for x in path.strip('/').split('/') if x] + ['index']
iternames = names[:]
while iternames:
name = iternames[0]
objname = name.translate(self.translate)
nodeconf = {}
subnode = getattr(node, objname, None)
name = iternames.pop(0)
node = subnode
curpath = "/".join((curpath, name))
object_trail.append([name, node, nodeconf, curpath])
# try successive objects (reverse order)
num_candidates = len(object_trail) - 1
for i in range(num_candidates, -1, -1):
name, candidate, nodeconf, curpath = object_trail[i]
if candidate is None:
continue
# try a "_route" method on the current leaf.
if hasattr(candidate, "_route"):
processed_path = object_trail[i][-1]
unprocessed_path = object_trail[-1][-1].replace(processed_path, '')
return candidate._route(unprocessed_path)
# try a "_lookup" method on the current leaf.
if hasattr(candidate, "_lookup"):
lookup = candidate._lookup(object_trail[i+1][0])
processed_path = object_trail[i+1][-1]
unprocessed_path = object_trail[-2][-1].replace(processed_path, '')
return self.route(lookup, unprocessed_path)
# try a "_default" method on the current leaf.
if hasattr(candidate, "_default"):
defhandler = candidate._default
if getattr(defhandler, 'exposed', False):
object_trail.insert(i+1, ["_default", defhandler, {}, curpath])
return defhandler
# try the current leaf.
if getattr(candidate, 'exposed', False):
return candidate
# we didn't find anything
return None
def route(self, node, path):
path = path.split('/')[1:]
node, remainder = lookup_controller(node, path)
return node
def __call__(self, environ, start_response):
# create the request object
state.request = Request(environ)

60
pecan/routing.py Normal file
View File

@@ -0,0 +1,60 @@
from webob import exc
def lookup_controller(obj, url_path):
remainder = url_path
notfound_handlers = []
while True:
try:
obj, remainder = find_object(obj, remainder, notfound_handlers)
return obj, remainder
except exc.HTTPNotFound:
while notfound_handlers:
name, obj, remainder = notfound_handlers.pop()
if name == '_default':
# Notfound handler is, in fact, a controller, so stop
# traversal
return obj, remainder
else:
# Notfound handler is an internal redirect, so continue
# traversal
try:
result = obj(*remainder)
if result:
obj, remainder = result
break
except TypeError, te:
print 'Got exception calling lookup(): %s (%s)' % (te, te.args)
else:
raise exc.HTTPNotFound
def find_object(obj, remainder, notfound_handlers):
while True:
if obj is None: raise exc.HTTPNotFound
if iscontroller(obj): return obj, remainder
if remainder and remainder[0] == '':
index = getattr(obj, 'index', None)
if iscontroller(index): return index, remainder[1:]
elif not remainder:
index = getattr(obj, 'index', None)
if iscontroller(index):
return index, remainder[1:] # TODO: why did I have to do this instead?
#raise exc.HTTPFound(add_slash=True)
default = getattr(obj, '_default', None)
if iscontroller(default):
notfound_handlers.append(('_default', default, remainder))
lookup = getattr(obj, '_lookup', None)
if iscontroller(lookup):
notfound_handlers.append(('_lookup', lookup, remainder))
if not remainder: raise exc.HTTPNotFound
next, remainder = remainder[0], remainder[1:]
obj = getattr(obj, next, None)
def iscontroller(obj):
return getattr(obj, 'exposed', False)

View File

@@ -78,8 +78,9 @@ class TestBase(object):
def index(self):
return '/'
def _lookup(self, someID):
return LookupController(someID)
@expose()
def _lookup(self, someID, *remainder):
return LookupController(someID), remainder
app = TestApp(Pecan(RootController()))
response = app.get('/')
@@ -93,10 +94,8 @@ class TestBase(object):
response = app.get('/100/name')
assert response.status_int == 200
assert response.body == '/100/name'
class TestEngines(object):
def test_genshi(self):