diff options
| -rw-r--r-- | pyramid/router.py | 24 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 72 | ||||
| -rw-r--r-- | pyramid/view.py | 18 |
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 |
