Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of latteier/pyramid into pull.786

  • Loading branch information...
commit b6a4d40d8fa14915413ac337f4b71e203257ca85 2 parents aae62a0 + 9e1e6d3
@mmerickel mmerickel authored
Showing with 134 additions and 2 deletions.
  1. +43 −0 CHANGES.txt
  2. +19 −2 pyramid/router.py
  3. +72 −0 pyramid/tests/test_router.py
View
43 CHANGES.txt
@@ -11,6 +11,49 @@ Features
a dynamic property. It is recommended to define a dynamic ACL as a callable
to avoid this ambiguity. See https://github.com/Pylons/pyramid/issues/735.
+Bug Fixes
+---------
+
+- View lookup will now search for valid views based on the inheritance
+ hierarchy of the context. It tries to find views based on the most
+ specific context first, and upon predicate failure, will move up the
+ inheritance chain to test views found by the super-type of the context.
+ In the past, only the most specific type containing views would be checked
+ and if no matching view could be found then a PredicateMismatch would be
+ raised. Now predicate mismatches don't hide valid views registered on
+ super-types. Here's an example that now works::
+
+ .. code-block:: python
+
+ 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)
================
View
21 pyramid/router.py
@@ -1,4 +1,5 @@
from zope.interface import (
+ Interface,
implementer,
providedBy,
)
@@ -24,6 +25,7 @@
NewResponse,
)
+from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import HTTPNotFound
from pyramid.request import Request
from pyramid.threadlocal import manager
@@ -158,8 +160,23 @@ def handle_request(self, request):
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 context_iface.flattened():
+ 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):
View
72 pyramid/tests/test_router.py
@@ -1164,6 +1164,78 @@ def test_call_view_raises_exception_view_route(self):
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
Please sign in to comment.
Something went wrong with that request. Please try again.