diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-12-10 15:54:43 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-12-10 15:54:43 +0000 |
| commit | 29f5c1a101802e0ba66d72195fbe4e9d340a96a0 (patch) | |
| tree | 83f40864db4d6a8859e2ea6ef1bb1e8d6b359226 /repoze | |
| parent | 09bbfc5279971f5dfeadb514591b978edeeaa820 (diff) | |
| download | pyramid-29f5c1a101802e0ba66d72195fbe4e9d340a96a0.tar.gz pyramid-29f5c1a101802e0ba66d72195fbe4e9d340a96a0.tar.bz2 pyramid-29f5c1a101802e0ba66d72195fbe4e9d340a96a0.zip | |
- Added a "Special Exceptions" section to the "Views" narrative
documentation chapter explaining the effect of raising
``repoze.bfg.exceptions.NotFound`` and
``repoze.bfg.exceptions.Forbidden`` from within view code.
- When the ``repoze.bfg.exceptions.NotFound`` or
``repoze.bfg.exceptions.Forbidden`` error is raised from within a
custom root factory or the ``factory`` of a route, the appropriate
response is now sent to the requesting user agent (the result of the
notfound view or the forbidden view, respectively). When these
errors are raised from within a root factory, the ``context`` passed
to the notfound or forbidden view will be ``None``. Also, the
request will not be decorated with ``view_name``, ``subpath``,
``context``, etc. as would normally be the case if traversal had
been allowed to take place.
Diffstat (limited to 'repoze')
| -rw-r--r-- | repoze/bfg/router.py | 116 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_router.py | 43 |
2 files changed, 101 insertions, 58 deletions
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py index deffa4e17..630aa201c 100644 --- a/repoze/bfg/router.py +++ b/repoze/bfg/router.py @@ -65,71 +65,73 @@ class Router(object): try: # setup request = Request(environ) + context = None threadlocals['request'] = request attrs = request.__dict__ attrs['registry'] = registry has_listeners and registry.notify(NewRequest(request)) + + try: + # root resolution + root_factory = self.root_factory + if self.routes_mapper is not None: + info = self.routes_mapper(request) + match, route = info['match'], info['route'] + if route is not None: + environ['wsgiorg.routing_args'] = ((), match) + environ['bfg.routes.route'] = route + environ['bfg.routes.matchdict'] = match + request.matchdict = match + iface = registry.queryUtility(IRouteRequest, + name=route.name) + if iface is not None: + alsoProvides(request, iface) + root_factory = route.factory or self.root_factory + + root = root_factory(request) + attrs['root'] = root + + # view lookup + traverser = registry.adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ModelGraphTraverser(root) + tdict = traverser(request) + context, view_name, subpath, traversed, vroot, vroot_path = ( + tdict['context'], tdict['view_name'], tdict['subpath'], + tdict['traversed'], tdict['virtual_root'], + tdict['virtual_root_path']) + attrs.update(tdict) + has_listeners and registry.notify(AfterTraversal(request)) + provides = map(providedBy, (context, request)) + view_callable = registry.adapters.lookup( + provides, IView, name=view_name, default=None) - # root resolution - root_factory = self.root_factory - if self.routes_mapper is not None: - info = self.routes_mapper(request) - match, route = info['match'], info['route'] - if route is not None: - environ['wsgiorg.routing_args'] = ((), match) - environ['bfg.routes.route'] = route - environ['bfg.routes.matchdict'] = match - request.matchdict = match - iface = registry.queryUtility(IRouteRequest, - name=route.name) - if iface is not None: - alsoProvides(request, iface) - root_factory = route.factory or self.root_factory - - root = root_factory(request) - attrs['root'] = root - - # view lookup - traverser = registry.adapters.queryAdapter(root, ITraverser) - if traverser is None: - traverser = ModelGraphTraverser(root) - tdict = traverser(request) - context, view_name, subpath, traversed, vroot, vroot_path = ( - tdict['context'], tdict['view_name'], tdict['subpath'], - tdict['traversed'], tdict['virtual_root'], - tdict['virtual_root_path']) - attrs.update(tdict) - has_listeners and registry.notify(AfterTraversal(request)) - provides = map(providedBy, (context, request)) - view_callable = registry.adapters.lookup( - provides, IView, name=view_name, default=None) - - # view execution - if view_callable is None: - if self.debug_notfound: - msg = ( - 'debug_notfound of url %s; path_info: %r, context: %r, ' - 'view_name: %r, subpath: %r, traversed: %r, ' - 'root: %r, vroot: %r, vroot_path: %r' % ( - request.url, request.path_info, context, view_name, - subpath, traversed, root, vroot, vroot_path) - ) - logger and logger.debug(msg) + # view execution + if view_callable is None: + if self.debug_notfound: + msg = ( + 'debug_notfound of url %s; path_info: %r, context: %r, ' + 'view_name: %r, subpath: %r, traversed: %r, ' + 'root: %r, vroot: %r, vroot_path: %r' % ( + request.url, request.path_info, context, view_name, + subpath, traversed, root, vroot, vroot_path) + ) + logger and logger.debug(msg) + else: + msg = request.path_info + environ['repoze.bfg.message'] = msg + response = self.notfound_view(context, request) else: - msg = request.path_info + response = view_callable(context, request) + + except Forbidden, why: + msg = why[0] + environ['repoze.bfg.message'] = msg + response = self.forbidden_view(context, request) + except NotFound, why: + msg = why[0] environ['repoze.bfg.message'] = msg response = self.notfound_view(context, request) - else: - try: - response = view_callable(context, request) - except Forbidden, why: - msg = why[0] - environ['repoze.bfg.message'] = msg - response = self.forbidden_view(context, request) - except NotFound, why: - msg = why[0] - environ['repoze.bfg.message'] = msg - response = self.notfound_view(context, request) # response handling has_listeners and registry.notify(NewResponse(response)) diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 45e66b8fe..5352c6d79 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -42,7 +42,8 @@ class TestRouter(unittest.TestCase): def _registerTraverserFactory(self, context, view_name='', subpath=None, traversed=None, virtual_root=None, - virtual_root_path=None, **kw): + virtual_root_path=None, raise_error=None, + **kw): from repoze.bfg.interfaces import ITraverser if virtual_root is None: @@ -59,6 +60,8 @@ class TestRouter(unittest.TestCase): self.root = root def __call__(self, request): + if raise_error: + raise raise_error values = {'root':self.root, 'context':context, 'view_name':view_name, @@ -504,6 +507,44 @@ class TestRouter(unittest.TestCase): self.failUnless(req_iface.providedBy(request)) self.failUnless(IFoo.providedBy(request)) + def test_root_factory_raises_notfound(self): + from repoze.bfg.interfaces import IRootFactory + from repoze.bfg.exceptions import NotFound + from zope.interface import Interface + from zope.interface import directlyProvides + def rootfactory(request): + raise NotFound('from root factory') + self.registry.registerUtility(rootfactory, IRootFactory) + class IContext(Interface): + pass + context = DummyContext() + directlyProvides(context, IContext) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(start_response.status, '404 Not Found') + self.failUnless('from root factory' in app_iter[0]) + + def test_root_factory_raises_forbidden(self): + from repoze.bfg.interfaces import IRootFactory + from repoze.bfg.exceptions import Forbidden + from zope.interface import Interface + from zope.interface import directlyProvides + def rootfactory(request): + raise Forbidden('from root factory') + self.registry.registerUtility(rootfactory, IRootFactory) + class IContext(Interface): + pass + context = DummyContext() + directlyProvides(context, IContext) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(start_response.status, '401 Unauthorized') + self.failUnless('from root factory' in app_iter[0]) + class DummyContext: pass |
