Steve Piercy
2018-09-22 e22970cd21eb36c2a658c843bb5cb4f59d77fd19
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,
8     IRequest,
735987 9     IRequestExtensions,
0c1c39 10     IRootFactory,
CM 11     IRouteRequest,
12     IRouter,
13     IRequestFactory,
14     IRoutesMapper,
15     ITraverser,
16     ITweens,
17     )
358dc2 18
0c1c39 19 from pyramid.events import (
CM 20     ContextFound,
21     NewRequest,
22     NewResponse,
d4f5a8 23     BeforeTraversal,
0c1c39 24     )
CM 25
99edc5 26 from pyramid.httpexceptions import HTTPNotFound
b60bdb 27 from pyramid.request import Request
eb3ac8 28 from pyramid.view import _call_view
04cc91 29 from pyramid.request import apply_request_extensions
b60bdb 30 from pyramid.threadlocal import manager
0c1c39 31
CM 32 from pyramid.traversal import (
33     DefaultRootFactory,
34     ResourceTreeTraverser,
35     )
36
3b7334 37 @implementer(IRouter)
eba6f8 38 class Router(object):
ab5959 39
CM 40     debug_notfound = False
24bf2a 41     debug_routematch = False
831e70 42
a1a9fb 43     threadlocal_manager = manager
ab5959 44
358dc2 45     def __init__(self, registry):
37369c 46         q = registry.queryUtility
bd39b1 47         self.logger = q(IDebugLogger)
37369c 48         self.root_factory = q(IRootFactory, default=DefaultRootFactory)
cbfafb 49         self.routes_mapper = q(IRoutesMapper)
81a833 50         self.request_factory = q(IRequestFactory, default=Request)
735987 51         self.request_extensions = q(IRequestExtensions)
dd9859 52         self.orig_handle_request = self.handle_request
35b0e3 53         tweens = q(ITweens)
MM 54         if tweens is not None:
55             self.handle_request = tweens(self.handle_request, registry)
37369c 56         self.root_policy = self.root_factory # b/w compat
9fcb68 57         self.registry = registry
5a972b 58         settings = registry.settings
ab5959 59         if settings is not None:
37369c 60             self.debug_notfound = settings['debug_notfound']
24bf2a 61             self.debug_routematch = settings['debug_routematch']
af2323 62
CM 63     def handle_request(self, request):
64         attrs = request.__dict__
65         registry = attrs['registry']
73e31c 66
da1381 67         request.request_iface = IRequest
CM 68         context = None
69         routes_mapper = self.routes_mapper
70         debug_routematch = self.debug_routematch
71         adapters = registry.adapters
72         has_listeners = registry.has_listeners
73         notify = registry.notify
74         logger = self.logger
af2323 75
da1381 76         has_listeners and notify(NewRequest(request))
CM 77         # find the root object
78         root_factory = self.root_factory
79         if routes_mapper is not None:
80             info = routes_mapper(request)
81             match, route = info['match'], info['route']
82             if route is None:
83                 if debug_routematch:
84                     msg = ('no route matched for url %s' %
85                            request.url)
86                     logger and logger.debug(msg)
87             else:
88                 attrs['matchdict'] = match
89                 attrs['matched_route'] = route
d15920 90
da1381 91                 if debug_routematch:
3c4555 92                     msg = (
da1381 93                         'route matched for url %s; '
CM 94                         'route_name: %r, '
95                         'path_info: %r, '
96                         'pattern: %r, '
97                         'matchdict: %r, '
98                         'predicates: %r' % (
99                             request.url,
100                             route.name,
101                             request.path_info,
def68d 102                             route.pattern,
CM 103                             match,
0ccdc2 104                             ', '.join([p.text() for p in route.predicates]))
3c4555 105                         )
CM 106                     logger and logger.debug(msg)
da1381 107
CM 108                 request.request_iface = registry.queryUtility(
109                     IRouteRequest,
110                     name=route.name,
111                     default=IRequest)
112
113                 root_factory = route.factory or self.root_factory
114
20bc06 115         # Notify anyone listening that we are about to start traversal
BJR 116         #
117         # Notify before creating root_factory in case we want to do something
118         # special on a route we may have matched. See
119         # https://github.com/Pylons/pyramid/pull/1876 for ideas of what is
120         # possible.
121         has_listeners and notify(BeforeTraversal(request))
122
123         # Create the root factory
da1381 124         root = root_factory(request)
CM 125         attrs['root'] = root
126
20bc06 127         # We are about to traverse and find a context
da1381 128         traverser = adapters.queryAdapter(root, ITraverser)
CM 129         if traverser is None:
130             traverser = ResourceTreeTraverser(root)
131         tdict = traverser(request)
132
133         context, view_name, subpath, traversed, vroot, vroot_path = (
134             tdict['context'],
135             tdict['view_name'],
136             tdict['subpath'],
137             tdict['traversed'],
138             tdict['virtual_root'],
139             tdict['virtual_root_path']
140             )
141
142         attrs.update(tdict)
20bc06 143
BJR 144         # Notify anyone listening that we have a context and traversal is
145         # complete
da1381 146         has_listeners and notify(ContextFound(request))
CM 147
148         # find a view callable
149         context_iface = providedBy(context)
eb3ac8 150         response = _call_view(
85d801 151             registry,
eb3ac8 152             request,
CM 153             context,
85d801 154             context_iface,
eb3ac8 155             view_name
85d801 156             )
MM 157
eb3ac8 158         if response is None:
CM 159             if self.debug_notfound:
160                 msg = (
161                     'debug_notfound of url %s; path_info: %r, '
162                     'context: %r, view_name: %r, subpath: %r, '
163                     'traversed: %r, root: %r, vroot: %r, '
164                     'vroot_path: %r' % (
165                         request.url, request.path_info, context,
166                         view_name, subpath, traversed, root, vroot,
167                         vroot_path)
168                     )
169                 logger and logger.debug(msg)
170             else:
171                 msg = request.path_info
172             raise HTTPNotFound(msg)
da1381 173
eb3ac8 174         return response
c8cf22 175
db2a03 176     def invoke_subrequest(self, request, use_tweens=False):
2033ee 177         """Obtain a response object from the Pyramid application based on
277b2a 178         information in the ``request`` object provided.  The ``request``
CM 179         object must be an object that implements the Pyramid request
180         interface (such as a :class:`pyramid.request.Request` instance).  If
181         ``use_tweens`` is ``True``, the request will be sent to the
182         :term:`tween` in the tween stack closest to the request ingress.  If
183         ``use_tweens`` is ``False``, the request will be sent to the main
2033ee 184         router handler, and no tweens will be invoked.
277b2a 185         
2033ee 186         See the API for pyramid.request for complete documentation.
37d2c2 187         """
dd9859 188         registry = self.registry
CM 189         has_listeners = self.registry.has_listeners
190         notify = self.registry.notify
191         threadlocals = {'registry':registry, 'request':request}
192         manager = self.threadlocal_manager
193         manager.push(threadlocals)
194         request.registry = registry
64452e 195         request.invoke_subrequest = self.invoke_subrequest
7259e7 196         
dd9859 197         if use_tweens:
CM 198             handle_request = self.handle_request
199         else:
200             handle_request = self.orig_handle_request
7259e7 201
dd9859 202         try:
CM 203
204             try:
205                 extensions = self.request_extensions
206                 if extensions is not None:
04cc91 207                     apply_request_extensions(request, extensions=extensions)
dd9859 208                 response = handle_request(request)
CM 209
210                 if request.response_callbacks:
211                     request._process_response_callbacks(response)
212
fc477b 213                 has_listeners and notify(NewResponse(request, response))
CM 214                 
dd9859 215                 return response
CM 216
217             finally:
218                 if request.finished_callbacks:
219                     request._process_finished_callbacks()
220
221         finally:
222             manager.pop()
223
612d74 224     def __call__(self, environ, start_response):
74f67d 225         """
8b1f6e 226         Accept ``environ`` and ``start_response``; create a
fd5ae9 227         :term:`request` and route the request to a :app:`Pyramid`
8b1f6e 228         view based on introspection of :term:`view configuration`
CM 229         within the application registry; call ``start_response`` and
230         return an iterable.
74f67d 231         """
d15920 232         request = self.request_factory(environ)
64452e 233         response = self.invoke_subrequest(request, use_tweens=True)
dd9859 234         return response(request.environ, start_response)