From b3643d8e587b48711f3224673d6c864d8ce1f93f Mon Sep 17 00:00:00 2001 From: Amos Latteier Date: Thu, 10 Jan 2013 14:55:07 -0500 Subject: Make predicate mismatches not hide other possible valid views. This is mostly an issue for REST style views that use request_method predicates and are registered for context interfaces. See Issue #409 --- pyramid/router.py | 22 ++++++++++++-- pyramid/tests/test_router.py | 72 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/pyramid/router.py b/pyramid/router.py index 9b6138ea9..bb2466ec3 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -1,4 +1,5 @@ from zope.interface import ( + Interface, implementer, providedBy, ) @@ -24,6 +25,7 @@ from pyramid.events import ( NewResponse, ) +from pyramid.exceptions import PredicateMismatch from pyramid.httpexceptions import HTTPNotFound from pyramid.request import Request from pyramid.threadlocal import manager @@ -158,8 +160,24 @@ class Router(object): msg = request.path_info raise HTTPNotFound(msg) else: - response = view_callable(context, request) - + try: + response = view_callable(context, request) + except PredicateMismatch: + # look for other views that meet the predicate + # criteria + for iface in [i for i in context_iface.flattened() + if i != Interface]: + view_callable = adapters.lookup( + (IViewClassifier, request.request_iface, iface), + IView, name=view_name, default=None) + if view_callable is not None: + try: + response = view_callable(context, request) + break + except PredicateMismatch: + pass + else: + raise return response def invoke_subrequest(self, request, use_tweens=False): diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py index 65152ca05..32e74a3b3 100644 --- a/pyramid/tests/test_router.py +++ b/pyramid/tests/test_router.py @@ -1164,6 +1164,78 @@ class TestRouter(unittest.TestCase): start_response = DummyStartResponse() self.assertRaises(RuntimeError, router, environ, start_response) + def test_call_view_raises_predicate_mismatch(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest + view = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view, '', IViewClassifier, IRequest, None) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(PredicateMismatch, router, environ, start_response) + + def test_call_view_predicate_mismatch_doesnt_hide_views(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest, IResponse + from pyramid.response import Response + from zope.interface import Interface, implementer + class IContext(Interface): + pass + @implementer(IContext) + class DummyContext: + pass + context = DummyContext() + self._registerTraverserFactory(context) + view = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view, '', IViewClassifier, IRequest, + DummyContext) + good_view = DummyView('abc') + self._registerView(self.config.derive_view(good_view), + '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + def make_response(s): + return Response(s) + router.registry.registerAdapter(make_response, (str,), IResponse) + environ = self._makeEnviron() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(app_iter, [b'abc']) + + def test_call_view_multiple_predicate_mismatches_dont_hide_views(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest, IResponse + from pyramid.response import Response + from zope.interface import Interface, implementer + class IBaseContext(Interface): + pass + class IContext(IBaseContext): + pass + @implementer(IContext) + class DummyContext: + pass + context = DummyContext() + self._registerTraverserFactory(context) + view1 = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view1, '', IViewClassifier, IRequest, + DummyContext) + view2 = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view2, '', IViewClassifier, IRequest, + IContext) + good_view = DummyView('abc') + self._registerView(self.config.derive_view(good_view), + '', IViewClassifier, IRequest, IBaseContext) + router = self._makeOne() + def make_response(s): + return Response(s) + router.registry.registerAdapter(make_response, (str,), IResponse) + environ = self._makeEnviron() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(app_iter, [b'abc']) + class DummyPredicate(object): def __call__(self, info, request): return True -- cgit v1.2.3