Michael Merickel
2018-10-15 0c29cf2df41600d3906d521c72991c7686018b71
commit | author | age
0c29cf 1 from zope.interface import implementer, providedBy
57267a 2
0c1c39 3 from pyramid.interfaces import (
CM 4     IDebugLogger,
0bee84 5     IExecutionPolicy,
0c1c39 6     IRequest,
735987 7     IRequestExtensions,
0c1c39 8     IRootFactory,
CM 9     IRouteRequest,
10     IRouter,
11     IRequestFactory,
12     IRoutesMapper,
13     ITraverser,
14     ITweens,
0c29cf 15 )
358dc2 16
0c1c39 17 from pyramid.events import (
CM 18     ContextFound,
19     NewRequest,
20     NewResponse,
d4f5a8 21     BeforeTraversal,
0c29cf 22 )
0c1c39 23
99edc5 24 from pyramid.httpexceptions import HTTPNotFound
b60bdb 25 from pyramid.request import Request
eb3ac8 26 from pyramid.view import _call_view
04cc91 27 from pyramid.request import apply_request_extensions
822653 28 from pyramid.threadlocal import RequestContext
0c1c39 29
0c29cf 30 from pyramid.traversal import DefaultRootFactory, ResourceTreeTraverser
MM 31
0c1c39 32
3b7334 33 @implementer(IRouter)
eba6f8 34 class Router(object):
ab5959 35
CM 36     debug_notfound = False
24bf2a 37     debug_routematch = False
ab5959 38
358dc2 39     def __init__(self, registry):
37369c 40         q = registry.queryUtility
bd39b1 41         self.logger = q(IDebugLogger)
37369c 42         self.root_factory = q(IRootFactory, default=DefaultRootFactory)
cbfafb 43         self.routes_mapper = q(IRoutesMapper)
81a833 44         self.request_factory = q(IRequestFactory, default=Request)
735987 45         self.request_extensions = q(IRequestExtensions)
0bee84 46         self.execution_policy = q(
0c29cf 47             IExecutionPolicy, default=default_execution_policy
MM 48         )
dd9859 49         self.orig_handle_request = self.handle_request
35b0e3 50         tweens = q(ITweens)
MM 51         if tweens is not None:
52             self.handle_request = tweens(self.handle_request, registry)
0c29cf 53         self.root_policy = self.root_factory  # b/w compat
9fcb68 54         self.registry = registry
5a972b 55         settings = registry.settings
ab5959 56         if settings is not None:
37369c 57             self.debug_notfound = settings['debug_notfound']
24bf2a 58             self.debug_routematch = settings['debug_routematch']
af2323 59
CM 60     def handle_request(self, request):
61         attrs = request.__dict__
62         registry = attrs['registry']
73e31c 63
da1381 64         request.request_iface = IRequest
CM 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
af2323 72
da1381 73         has_listeners and notify(NewRequest(request))
CM 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:
0c29cf 81                     msg = 'no route matched for url %s' % request.url
da1381 82                     logger and logger.debug(msg)
CM 83             else:
84                 attrs['matchdict'] = match
85                 attrs['matched_route'] = route
d15920 86
da1381 87                 if debug_routematch:
3c4555 88                     msg = (
da1381 89                         'route matched for url %s; '
CM 90                         'route_name: %r, '
91                         'path_info: %r, '
92                         'pattern: %r, '
93                         'matchdict: %r, '
0c29cf 94                         'predicates: %r'
MM 95                         % (
da1381 96                             request.url,
CM 97                             route.name,
98                             request.path_info,
def68d 99                             route.pattern,
CM 100                             match,
0c29cf 101                             ', '.join([p.text() for p in route.predicates]),
3c4555 102                         )
0c29cf 103                     )
3c4555 104                     logger and logger.debug(msg)
da1381 105
CM 106                 request.request_iface = registry.queryUtility(
0c29cf 107                     IRouteRequest, name=route.name, default=IRequest
MM 108                 )
da1381 109
CM 110                 root_factory = route.factory or self.root_factory
111
20bc06 112         # Notify anyone listening that we are about to start traversal
BJR 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
da1381 121         root = root_factory(request)
CM 122         attrs['root'] = root
123
20bc06 124         # We are about to traverse and find a context
da1381 125         traverser = adapters.queryAdapter(root, ITraverser)
CM 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'],
0c29cf 136             tdict['virtual_root_path'],
MM 137         )
da1381 138
CM 139         attrs.update(tdict)
20bc06 140
BJR 141         # Notify anyone listening that we have a context and traversal is
142         # complete
da1381 143         has_listeners and notify(ContextFound(request))
CM 144
145         # find a view callable
146         context_iface = providedBy(context)
eb3ac8 147         response = _call_view(
0c29cf 148             registry, request, context, context_iface, view_name
MM 149         )
85d801 150
eb3ac8 151         if response is None:
CM 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, '
0c29cf 157                     'vroot_path: %r'
MM 158                     % (
159                         request.url,
160                         request.path_info,
161                         context,
162                         view_name,
163                         subpath,
164                         traversed,
165                         root,
166                         vroot,
167                         vroot_path,
eb3ac8 168                     )
0c29cf 169                 )
eb3ac8 170                 logger and logger.debug(msg)
CM 171             else:
172                 msg = request.path_info
173             raise HTTPNotFound(msg)
da1381 174
eb3ac8 175         return response
c8cf22 176
db2a03 177     def invoke_subrequest(self, request, use_tweens=False):
2033ee 178         """Obtain a response object from the Pyramid application based on
277b2a 179         information in the ``request`` object provided.  The ``request``
CM 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
2033ee 185         router handler, and no tweens will be invoked.
0bee84 186
2033ee 187         See the API for pyramid.request for complete documentation.
37d2c2 188         """
0bee84 189         request.registry = self.registry
MM 190         request.invoke_subrequest = self.invoke_subrequest
72ca13 191         extensions = self.request_extensions
MM 192         if extensions is not None:
193             apply_request_extensions(request, extensions=extensions)
822653 194         with RequestContext(request):
MM 195             return self.invoke_request(request, _use_tweens=use_tweens)
0bee84 196
822653 197     def request_context(self, environ):
72ca13 198         """
822653 199         Create a new request context from a WSGI environ.
72ca13 200
822653 201         The request context is used to push/pop the threadlocals required
MM 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()
72ca13 223
MM 224         """
0bee84 225         request = self.request_factory(environ)
MM 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)
822653 231         return RequestContext(request)
0bee84 232
72ca13 233     def invoke_request(self, request, _use_tweens=True):
MM 234         """
235         Execute a request through the request processing pipeline and
236         return the generated response.
237
238         """
dd9859 239         registry = self.registry
822653 240         has_listeners = registry.has_listeners
MM 241         notify = registry.notify
0bee84 242
MM 243         if _use_tweens:
dd9859 244             handle_request = self.handle_request
CM 245         else:
246             handle_request = self.orig_handle_request
7259e7 247
dd9859 248         try:
822653 249             response = handle_request(request)
dd9859 250
822653 251             if request.response_callbacks:
MM 252                 request._process_response_callbacks(response)
dd9859 253
822653 254             has_listeners and notify(NewResponse(request, response))
dd9859 255
822653 256             return response
dd9859 257
CM 258         finally:
822653 259             if request.finished_callbacks:
MM 260                 request._process_finished_callbacks()
dd9859 261
612d74 262     def __call__(self, environ, start_response):
74f67d 263         """
8b1f6e 264         Accept ``environ`` and ``start_response``; create a
fd5ae9 265         :term:`request` and route the request to a :app:`Pyramid`
8b1f6e 266         view based on introspection of :term:`view configuration`
CM 267         within the application registry; call ``start_response`` and
268         return an iterable.
74f67d 269         """
0bee84 270         response = self.execution_policy(environ, self)
MM 271         return response(environ, start_response)
272
0c29cf 273
0bee84 274 def default_execution_policy(environ, router):
822653 275     with router.request_context(environ) as request:
07e0e1 276         try:
822653 277             return router.invoke_request(request)
MM 278         except Exception:
279             return request.invoke_exception_view(reraise=True)