summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmos Latteier <amos@latteier.com>2013-01-10 14:55:07 -0500
committerAmos Latteier <amos@latteier.com>2013-01-10 14:55:07 -0500
commitb3643d8e587b48711f3224673d6c864d8ce1f93f (patch)
treeeba1d5b58bce00e67ae336bf65f8c2a4665b8fe2
parent09f43d3ba25ba2189a1b9abf6978e14690034d16 (diff)
downloadpyramid-b3643d8e587b48711f3224673d6c864d8ce1f93f.tar.gz
pyramid-b3643d8e587b48711f3224673d6c864d8ce1f93f.tar.bz2
pyramid-b3643d8e587b48711f3224673d6c864d8ce1f93f.zip
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
-rw-r--r--pyramid/router.py22
-rw-r--r--pyramid/tests/test_router.py72
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