Steve Piercy
2018-09-22 2a45fe74f9598b4e726ab17ce17948d4e709894b
commit | author | age
0c1c39 1 from zope.interface import (
CM 2     implementer,
3     providedBy,
4     )
57267a 5
0c1c39 6 from pyramid.interfaces import (
CM 7     IDebugLogger,
0bee84 8     IExecutionPolicy,
0c1c39 9     IRequest,
735987 10     IRequestExtensions,
0c1c39 11     IRootFactory,
CM 12     IRouteRequest,
13     IRouter,
14     IRequestFactory,
15     IRoutesMapper,
16     ITraverser,
17     ITweens,
18     )
358dc2 19
0c1c39 20 from pyramid.events import (
CM 21     ContextFound,
22     NewRequest,
23     NewResponse,
d4f5a8 24     BeforeTraversal,
0c1c39 25     )
CM 26
99edc5 27 from pyramid.httpexceptions import HTTPNotFound
b60bdb 28 from pyramid.request import Request
eb3ac8 29 from pyramid.view import _call_view
04cc91 30 from pyramid.request import apply_request_extensions
822653 31 from pyramid.threadlocal import RequestContext
0c1c39 32
CM 33 from pyramid.traversal import (
34     DefaultRootFactory,
35     ResourceTreeTraverser,
36     )
37
3b7334 38 @implementer(IRouter)
eba6f8 39 class Router(object):
ab5959 40
CM 41     debug_notfound = False
24bf2a 42     debug_routematch = False
ab5959 43
358dc2 44     def __init__(self, registry):
37369c 45         q = registry.queryUtility
bd39b1 46         self.logger = q(IDebugLogger)
37369c 47         self.root_factory = q(IRootFactory, default=DefaultRootFactory)
cbfafb 48         self.routes_mapper = q(IRoutesMapper)
81a833 49         self.request_factory = q(IRequestFactory, default=Request)
735987 50         self.request_extensions = q(IRequestExtensions)
0bee84 51         self.execution_policy = q(
MM 52             IExecutionPolicy, default=default_execution_policy)
dd9859 53         self.orig_handle_request = self.handle_request
35b0e3 54         tweens = q(ITweens)
MM 55         if tweens is not None:
56             self.handle_request = tweens(self.handle_request, registry)
37369c 57         self.root_policy = self.root_factory # b/w compat
9fcb68 58         self.registry = registry
5a972b 59         settings = registry.settings
ab5959 60         if settings is not None:
37369c 61             self.debug_notfound = settings['debug_notfound']
24bf2a 62             self.debug_routematch = settings['debug_routematch']
af2323 63
CM 64     def handle_request(self, request):
65         attrs = request.__dict__
66         registry = attrs['registry']
73e31c 67
da1381 68         request.request_iface = IRequest
CM 69         context = None
70         routes_mapper = self.routes_mapper
71         debug_routematch = self.debug_routematch
72         adapters = registry.adapters
73         has_listeners = registry.has_listeners
74         notify = registry.notify
75         logger = self.logger
af2323 76
da1381 77         has_listeners and notify(NewRequest(request))
CM 78         # find the root object
79         root_factory = self.root_factory
80         if routes_mapper is not None:
81             info = routes_mapper(request)
82             match, route = info['match'], info['route']
83             if route is None:
84                 if debug_routematch:
85                     msg = ('no route matched for url %s' %
86                            request.url)
87                     logger and logger.debug(msg)
88             else:
89                 attrs['matchdict'] = match
90                 attrs['matched_route'] = route
d15920 91
da1381 92                 if debug_routematch:
3c4555 93                     msg = (
da1381 94                         'route matched for url %s; '
CM 95                         'route_name: %r, '
96                         'path_info: %r, '
97                         'pattern: %r, '
98                         'matchdict: %r, '
99                         'predicates: %r' % (
100                             request.url,
101                             route.name,
102                             request.path_info,
def68d 103                             route.pattern,
CM 104                             match,
0ccdc2 105                             ', '.join([p.text() for p in route.predicates]))
3c4555 106                         )
CM 107                     logger and logger.debug(msg)
da1381 108
CM 109                 request.request_iface = registry.queryUtility(
110                     IRouteRequest,
111                     name=route.name,
112                     default=IRequest)
113
114                 root_factory = route.factory or self.root_factory
115
20bc06 116         # Notify anyone listening that we are about to start traversal
BJR 117         #
118         # Notify before creating root_factory in case we want to do something
119         # special on a route we may have matched. See
120         # https://github.com/Pylons/pyramid/pull/1876 for ideas of what is
121         # possible.
122         has_listeners and notify(BeforeTraversal(request))
123
124         # Create the root factory
da1381 125         root = root_factory(request)
CM 126         attrs['root'] = root
127
20bc06 128         # We are about to traverse and find a context
da1381 129         traverser = adapters.queryAdapter(root, ITraverser)
CM 130         if traverser is None:
131             traverser = ResourceTreeTraverser(root)
132         tdict = traverser(request)
133
134         context, view_name, subpath, traversed, vroot, vroot_path = (
135             tdict['context'],
136             tdict['view_name'],
137             tdict['subpath'],
138             tdict['traversed'],
139             tdict['virtual_root'],
140             tdict['virtual_root_path']
141             )
142
143         attrs.update(tdict)
20bc06 144
BJR 145         # Notify anyone listening that we have a context and traversal is
146         # complete
da1381 147         has_listeners and notify(ContextFound(request))
CM 148
149         # find a view callable
150         context_iface = providedBy(context)
eb3ac8 151         response = _call_view(
85d801 152             registry,
eb3ac8 153             request,
CM 154             context,
85d801 155             context_iface,
eb3ac8 156             view_name
85d801 157             )
MM 158
eb3ac8 159         if response is None:
CM 160             if self.debug_notfound:
161                 msg = (
162                     'debug_notfound of url %s; path_info: %r, '
163                     'context: %r, view_name: %r, subpath: %r, '
164                     'traversed: %r, root: %r, vroot: %r, '
165                     'vroot_path: %r' % (
166                         request.url, request.path_info, context,
167                         view_name, subpath, traversed, root, vroot,
168                         vroot_path)
169                     )
170                 logger and logger.debug(msg)
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
273 def default_execution_policy(environ, router):
822653 274     with router.request_context(environ) as request:
07e0e1 275         try:
822653 276             return router.invoke_request(request)
MM 277         except Exception:
278             return request.invoke_exception_view(reraise=True)