Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from zope.interface import implementer, providedBy 

2 

3from pyramid.interfaces import ( 

4 IDebugLogger, 

5 IExecutionPolicy, 

6 IRequest, 

7 IRequestExtensions, 

8 IRootFactory, 

9 IRouteRequest, 

10 IRouter, 

11 IRequestFactory, 

12 IRoutesMapper, 

13 ITraverser, 

14 ITweens, 

15) 

16 

17from pyramid.events import ( 

18 ContextFound, 

19 NewRequest, 

20 NewResponse, 

21 BeforeTraversal, 

22) 

23 

24from pyramid.httpexceptions import HTTPNotFound 

25from pyramid.request import Request 

26from pyramid.view import _call_view 

27from pyramid.request import apply_request_extensions 

28from pyramid.threadlocal import RequestContext 

29 

30from pyramid.traversal import DefaultRootFactory, ResourceTreeTraverser 

31 

32 

33@implementer(IRouter) 

34class Router(object): 

35 

36 debug_notfound = False 

37 debug_routematch = False 

38 

39 def __init__(self, registry): 

40 q = registry.queryUtility 

41 self.logger = q(IDebugLogger) 

42 self.root_factory = q(IRootFactory, default=DefaultRootFactory) 

43 self.routes_mapper = q(IRoutesMapper) 

44 self.request_factory = q(IRequestFactory, default=Request) 

45 self.request_extensions = q(IRequestExtensions) 

46 self.execution_policy = q( 

47 IExecutionPolicy, default=default_execution_policy 

48 ) 

49 self.orig_handle_request = self.handle_request 

50 tweens = q(ITweens) 

51 if tweens is not None: 

52 self.handle_request = tweens(self.handle_request, registry) 

53 self.root_policy = self.root_factory # b/w compat 

54 self.registry = registry 

55 settings = registry.settings 

56 if settings is not None: 

57 self.debug_notfound = settings['debug_notfound'] 

58 self.debug_routematch = settings['debug_routematch'] 

59 

60 def handle_request(self, request): 

61 attrs = request.__dict__ 

62 registry = attrs['registry'] 

63 

64 request.request_iface = IRequest 

65 context = None 

66 routes_mapper = self.routes_mapper 

67 debug_routematch = self.debug_routematch 

68 adapters = registry.adapters 

69 has_listeners = registry.has_listeners 

70 notify = registry.notify 

71 logger = self.logger 

72 

73 has_listeners and notify(NewRequest(request)) 

74 # find the root object 

75 root_factory = self.root_factory 

76 if routes_mapper is not None: 

77 info = routes_mapper(request) 

78 match, route = info['match'], info['route'] 

79 if route is None: 

80 if debug_routematch: 

81 msg = 'no route matched for url %s' % request.url 

82 logger and logger.debug(msg) 

83 else: 

84 attrs['matchdict'] = match 

85 attrs['matched_route'] = route 

86 

87 if debug_routematch: 

88 msg = ( 

89 'route matched for url %s; ' 

90 'route_name: %r, ' 

91 'path_info: %r, ' 

92 'pattern: %r, ' 

93 'matchdict: %r, ' 

94 'predicates: %r' 

95 % ( 

96 request.url, 

97 route.name, 

98 request.path_info, 

99 route.pattern, 

100 match, 

101 ', '.join([p.text() for p in route.predicates]), 

102 ) 

103 ) 

104 logger and logger.debug(msg) 

105 

106 request.request_iface = registry.queryUtility( 

107 IRouteRequest, name=route.name, default=IRequest 

108 ) 

109 

110 root_factory = route.factory or self.root_factory 

111 

112 # Notify anyone listening that we are about to start traversal 

113 # 

114 # Notify before creating root_factory in case we want to do something 

115 # special on a route we may have matched. See 

116 # https://github.com/Pylons/pyramid/pull/1876 for ideas of what is 

117 # possible. 

118 has_listeners and notify(BeforeTraversal(request)) 

119 

120 # Create the root factory 

121 root = root_factory(request) 

122 attrs['root'] = root 

123 

124 # We are about to traverse and find a context 

125 traverser = adapters.queryAdapter(root, ITraverser) 

126 if traverser is None: 

127 traverser = ResourceTreeTraverser(root) 

128 tdict = traverser(request) 

129 

130 context, view_name, subpath, traversed, vroot, vroot_path = ( 

131 tdict['context'], 

132 tdict['view_name'], 

133 tdict['subpath'], 

134 tdict['traversed'], 

135 tdict['virtual_root'], 

136 tdict['virtual_root_path'], 

137 ) 

138 

139 attrs.update(tdict) 

140 

141 # Notify anyone listening that we have a context and traversal is 

142 # complete 

143 has_listeners and notify(ContextFound(request)) 

144 

145 # find a view callable 

146 context_iface = providedBy(context) 

147 response = _call_view( 

148 registry, request, context, context_iface, view_name 

149 ) 

150 

151 if response is None: 

152 if self.debug_notfound: 

153 msg = ( 

154 'debug_notfound of url %s; path_info: %r, ' 

155 'context: %r, view_name: %r, subpath: %r, ' 

156 'traversed: %r, root: %r, vroot: %r, ' 

157 'vroot_path: %r' 

158 % ( 

159 request.url, 

160 request.path_info, 

161 context, 

162 view_name, 

163 subpath, 

164 traversed, 

165 root, 

166 vroot, 

167 vroot_path, 

168 ) 

169 ) 

170 logger and logger.debug(msg) 

171 else: 

172 msg = request.path_info 

173 raise HTTPNotFound(msg) 

174 

175 return response 

176 

177 def invoke_subrequest(self, request, use_tweens=False): 

178 """Obtain a response object from the Pyramid application based on 

179 information in the ``request`` object provided. The ``request`` 

180 object must be an object that implements the Pyramid request 

181 interface (such as a :class:`pyramid.request.Request` instance). If 

182 ``use_tweens`` is ``True``, the request will be sent to the 

183 :term:`tween` in the tween stack closest to the request ingress. If 

184 ``use_tweens`` is ``False``, the request will be sent to the main 

185 router handler, and no tweens will be invoked. 

186 

187 See the API for pyramid.request for complete documentation. 

188 """ 

189 request.registry = self.registry 

190 request.invoke_subrequest = self.invoke_subrequest 

191 extensions = self.request_extensions 

192 if extensions is not None: 

193 apply_request_extensions(request, extensions=extensions) 

194 with RequestContext(request): 

195 return self.invoke_request(request, _use_tweens=use_tweens) 

196 

197 def request_context(self, environ): 

198 """ 

199 Create a new request context from a WSGI environ. 

200 

201 The request context is used to push/pop the threadlocals required 

202 when processing the request. It also contains an initialized 

203 :class:`pyramid.interfaces.IRequest` instance using the registered 

204 :class:`pyramid.interfaces.IRequestFactory`. The context may be 

205 used as a context manager to control the threadlocal lifecycle: 

206 

207 .. code-block:: python 

208 

209 with router.request_context(environ) as request: 

210 ... 

211 

212 Alternatively, the context may be used without the ``with`` statement 

213 by manually invoking its ``begin()`` and ``end()`` methods. 

214 

215 .. code-block:: python 

216 

217 ctx = router.request_context(environ) 

218 request = ctx.begin() 

219 try: 

220 ... 

221 finally: 

222 ctx.end() 

223 

224 """ 

225 request = self.request_factory(environ) 

226 request.registry = self.registry 

227 request.invoke_subrequest = self.invoke_subrequest 

228 extensions = self.request_extensions 

229 if extensions is not None: 

230 apply_request_extensions(request, extensions=extensions) 

231 return RequestContext(request) 

232 

233 def invoke_request(self, request, _use_tweens=True): 

234 """ 

235 Execute a request through the request processing pipeline and 

236 return the generated response. 

237 

238 """ 

239 registry = self.registry 

240 has_listeners = registry.has_listeners 

241 notify = registry.notify 

242 

243 if _use_tweens: 

244 handle_request = self.handle_request 

245 else: 

246 handle_request = self.orig_handle_request 

247 

248 try: 

249 response = handle_request(request) 

250 

251 if request.response_callbacks: 

252 request._process_response_callbacks(response) 

253 

254 has_listeners and notify(NewResponse(request, response)) 

255 

256 return response 

257 

258 finally: 

259 if request.finished_callbacks: 

260 request._process_finished_callbacks() 

261 

262 def __call__(self, environ, start_response): 

263 """ 

264 Accept ``environ`` and ``start_response``; create a 

265 :term:`request` and route the request to a :app:`Pyramid` 

266 view based on introspection of :term:`view configuration` 

267 within the application registry; call ``start_response`` and 

268 return an iterable. 

269 """ 

270 response = self.execution_policy(environ, self) 

271 return response(environ, start_response) 

272 

273 

274def default_execution_policy(environ, router): 

275 with router.request_context(environ) as request: 

276 try: 

277 return router.invoke_request(request) 

278 except Exception: 

279 return request.invoke_exception_view(reraise=True)