summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyramid/router.py24
-rw-r--r--pyramid/tests/test_config/test_views.py72
-rw-r--r--pyramid/view.py18
3 files changed, 100 insertions, 14 deletions
diff --git a/pyramid/router.py b/pyramid/router.py
index 0b1ecade7..eac7f7976 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -13,8 +13,6 @@ from pyramid.interfaces import (
IRequestFactory,
IRoutesMapper,
ITraverser,
- IView,
- IViewClassifier,
ITweens,
)
@@ -27,6 +25,7 @@ from pyramid.events import (
from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import HTTPNotFound
from pyramid.request import Request
+from pyramid.view import _find_views
from pyramid.request import apply_request_extensions
from pyramid.threadlocal import manager
@@ -139,9 +138,14 @@ class Router(object):
# find a view callable
context_iface = providedBy(context)
- view_callable = adapters.lookup(
- (IViewClassifier, request.request_iface, context_iface),
- IView, name=view_name, default=None)
+ views_iter = _find_views(
+ registry,
+ request.request_iface,
+ context_iface,
+ view_name,
+ )
+
+ view_callable = next(views_iter, None)
# invoke the view callable
if view_callable is None:
@@ -165,14 +169,7 @@ class Router(object):
except PredicateMismatch:
# look for other views that meet the predicate
# criteria
- for iface in context_iface.__sro__[1:]:
- previous_view_callable = view_callable
- view_callable = adapters.lookup(
- (IViewClassifier, request.request_iface, iface),
- IView, name=view_name, default=None)
- # intermediate bases may lookup same view_callable
- if view_callable is previous_view_callable:
- continue
+ for view_callable in views_iter:
if view_callable is not None:
try:
response = view_callable(context, request)
@@ -242,4 +239,3 @@ class Router(object):
request = self.request_factory(environ)
response = self.invoke_subrequest(request, use_tweens=True)
return response(request.environ, start_response)
-
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index 180050941..706d7ff77 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -1007,6 +1007,78 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request.params = {'param':'1'}
self.assertEqual(wrapper(ctx, request), 'view8')
+ def test_view_with_most_specific_predicate(self):
+ from pyramid.renderers import null_renderer as nr
+ from pyramid.router import Router
+
+ class OtherBase(object): pass
+ class Int1(object): pass
+ class Int2(object): pass
+
+ class Resource(OtherBase, Int1, Int2):
+ def __init__(self, request): pass
+
+ def unknown(context, request): return 'unknown'
+ def view(context, request): return 'hello'
+
+ config = self._makeOne(autocommit=True)
+ config.add_route('root', '/', factory=Resource)
+ config.add_view(unknown, route_name='root', renderer=nr)
+ config.add_view(
+ view, renderer=nr, route_name='root',
+ context=Int1, request_method='GET'
+ )
+ config.add_view(
+ view=view, renderer=nr, route_name='root',
+ context=Int2, request_method='POST'
+ )
+ request = self._makeRequest(config)
+ request.method = 'POST'
+ request.params = {}
+ router = Router(config.registry)
+ response = router.handle_request(request)
+ self.assertEqual(response, 'hello')
+
+ def test_view_with_most_specific_predicate_with_mismatch(self):
+ from pyramid.renderers import null_renderer as nr
+ from pyramid.router import Router
+
+ class OtherBase(object): pass
+ class Int1(object): pass
+ class Int2(object): pass
+
+ class Resource(OtherBase, Int1, Int2):
+ def __init__(self, request): pass
+
+ def unknown(context, request): return 'unknown'
+ def view(context, request): return 'hello'
+
+ config = self._makeOne(autocommit=True)
+ config.add_route('root', '/', factory=Resource)
+
+ config.add_view(
+ unknown,
+ route_name='root',
+ renderer=nr,
+ request_method=('POST',),
+ xhr=True,
+ )
+
+ config.add_view(
+ view, renderer=nr, route_name='root',
+ context=Int1, request_method='GET'
+ )
+ config.add_view(
+ view=view, renderer=nr, route_name='root',
+ context=Int2, request_method='POST'
+ )
+ request = self._makeRequest(config)
+ request.method = 'POST'
+ request.params = {}
+ router = Router(config.registry)
+ response = router.handle_request(request)
+ self.assertEqual(response, 'hello')
+
def test_add_view_multiview___discriminator__(self):
from pyramid.renderers import null_renderer
from zope.interface import Interface
diff --git a/pyramid/view.py b/pyramid/view.py
index 02ac8849f..4bd036428 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -1,9 +1,12 @@
+import itertools
import venusian
from zope.interface import providedBy
from pyramid.interfaces import (
IRoutesMapper,
+ IMultiView,
+ ISecuredView,
IView,
IViewClassifier,
)
@@ -414,3 +417,18 @@ class forbidden_view_config(object):
settings['_info'] = info.codeinfo # fbo "action_method"
return wrapped
+def _find_views(registry, request_iface, context_iface, view_name):
+ registered = registry.adapters.registered
+ view_types = (IView, ISecuredView, IMultiView)
+ for req_type, ctx_type in itertools.product(
+ request_iface.__sro__, context_iface.__sro__
+ ):
+ source_ifaces = (IViewClassifier, req_type, ctx_type)
+ for view_type in view_types:
+ view_callable = registered(
+ source_ifaces,
+ view_type,
+ name=view_name,
+ )
+ if view_callable is not None:
+ yield view_callable