This repository has been archived by the owner on Oct 12, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
NevowDispatcher
155 lines (127 loc) · 6.75 KB
/
NevowDispatcher
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
There was a post about a new url traversal implemented in turbogears
on top of cherrypy (with ideas being borrowed from Nevow):
https://projects.isotoma.com/tgnewtraversal
Here's how to do it in CherryPy 3 (and without requiring mixin classes!):
{{{
class NevowStyleDispatcher(cherrypy.dispatch.Dispatcher):
"""Dispatcher which walks a tree of objects to find a handler.
The tree is rooted at cherrypy.request.app.root, and each hierarchical
component in the path_info argument is matched to a corresponding nested
attribute of the root object. Matching handlers must have an 'exposed'
attribute which evaluates to True. The special method name "index"
matches a URI which ends in a slash ("/"). The special method name
"default" may match a portion of the path_info (but only when no longer
substring of the path_info matches some other object).
In addition, this class extends the builtin CherryPy Dispatcher by
allowing any node in the tree to define a "locateChild" method.
This will be passed a "segments" tuple of the remaining path
components (starting with the name of the desired child),
and should return (child node, remaining segments tuple).
If a locateChild method does not exist or returns None,
then the normal CP lookup will occur.
"""
def find_handler(self, path):
"""Return the appropriate page handler, plus any virtual path.
This will return two objects. The first will be a callable,
which can be used to generate page output. Any parameters from
the query string or request body will be sent to that callable
as keyword arguments.
The callable is found by traversing the application's tree,
starting from cherrypy.request.app.root, and matching path
components to successive objects in the tree. For example, the
URL "/path/to/handler" might return root.path.to.handler.
The second object returned will be a list of names which are
'virtual path' components: parts of the URL which are dynamic,
and were not used when looking up the handler.
These virtual path components are passed to the handler as
positional arguments.
"""
request = cherrypy.request
app = request.app
root = app.root
# Get config for the root object/path.
curpath = ""
nodeconf = {}
if hasattr(root, "_cp_config"):
nodeconf.update(root._cp_config)
if "/" in app.config:
nodeconf.update(app.config["/"])
object_trail = [['root', root, nodeconf, curpath]]
node = root
names = [x for x in path.strip('/').split('/') if x] + ['index']
traversal_names = names[:]
while True:
name = traversal_names.pop(0)
# map to legal Python identifiers (replace '.' with '_')
objname = name.replace('.', '_')
nodeconf = {}
nextnode = None
# Here's where we differ from the superclass. We allow
# each node to define a "locateChild" method, which can
# return any object as the next child in the chain.
if hasattr(node, "locateChild"):
segments = curpath.split("/")
nextnode, traversal_names = node.locateChild(segments)
# If locateChild did not exist or did not locate a child,
# default back to the normal CP attribute lookup.
if nextnode is None:
nextnode = getattr(node, objname, None)
node = nextnode
if node is not None:
# Get _cp_config attached to this node.
if hasattr(node, "_cp_config"):
nodeconf.update(node._cp_config)
# Mix in values from app.config for this path.
curpath = "/".join((curpath, name))
if curpath in app.config:
nodeconf.update(app.config[curpath])
object_trail.append([name, node, nodeconf, curpath])
if not traversal_names:
break
def set_conf():
"""Collapse all object_trail config into cherrypy.request.config."""
base = cherrypy.config.copy()
# Note that we merge the config from each node
# even if that node was None.
for name, obj, conf, curpath in object_trail:
base.update(conf)
if 'tools.staticdir.dir' in conf:
base['tools.staticdir.section'] = curpath
return base
# Try successive objects (reverse order)
num_candidates = len(object_trail) - 1
for i in xrange(num_candidates, -1, -1):
name, candidate, nodeconf, curpath = object_trail[i]
if candidate is None:
continue
# Try a "default" method on the current leaf.
if hasattr(candidate, "default"):
defhandler = candidate.default
if getattr(defhandler, 'exposed', False):
# Insert any extra _cp_config from the default handler.
conf = getattr(defhandler, "_cp_config", {})
object_trail.insert(i+1, ["default", defhandler, conf, curpath])
request.config = set_conf()
# See http://www.cherrypy.org/ticket/613
request.is_index = path.endswith("/")
return defhandler, names[i:-1]
# Uncomment the next line to restrict positional params to "default".
# if i < num_candidates - 2: continue
# Try the current leaf.
if getattr(candidate, 'exposed', False):
request.config = set_conf()
if i == num_candidates:
# We found the extra ".index". Mark request so tools
# can redirect if path_info has no trailing slash.
request.is_index = True
else:
# We're not at an 'index' handler. Mark request so tools
# can redirect if path_info has NO trailing slash.
# Note that this also includes handlers which take
# positional parameters (virtual paths).
request.is_index = False
return candidate, names[i:-1]
# We didn't find anything
request.config = set_conf()
return None, []
}}}