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 From 9e1e6d3f6d8e131afe102f60af3ca8f597b32745 Mon Sep 17 00:00:00 2001 From: Amos Latteier Date: Thu, 10 Jan 2013 15:19:08 -0500 Subject: Change log note. --- CHANGES.txt | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index adf448945..54623ddd2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,40 @@ +Bug Fixes +--------- + +- Now predicate mismatches don't hide valid views. This is mostly an + issue with views that use request_method predicates. Here's an + example that now works:: + + class IResource(Interface): + ... + + @view_config(context=IResource) + def get(context, request): + ... + + @view_config(context=IResource, request_method='POST') + def post(context, request): + ... + + @view_config(context=IResource, request_method='DELETE') + def delete(context, request): + ... + + @implementor(IResource) + class MyResource: + ... + + @view_config(context=MyResource, request_method='POST') + def override_post(context, request): + ... + + Previously the override_post view registration would hide the get + and delete views in the context of MyResource -- leading to a + predicate mismatch error when trying to use GET or DELETE + methods. Now the views are found and no predicate mismatch is + raised. + + 1.4 (2012-12-18) ================ -- cgit v1.2.3