summaryrefslogtreecommitdiff
path: root/repoze
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-12-02 21:10:57 +0000
committerChris McDonough <chrism@agendaless.com>2009-12-02 21:10:57 +0000
commit6a6f6cc370c51f28536cbc1751176a6480ed5f68 (patch)
treeb8ff09ae7a9119e2f1198245877545186135ed9b /repoze
parent16db57af0922f93026a5b046897acdaa8dd1e602 (diff)
downloadpyramid-6a6f6cc370c51f28536cbc1751176a6480ed5f68.tar.gz
pyramid-6a6f6cc370c51f28536cbc1751176a6480ed5f68.tar.bz2
pyramid-6a6f6cc370c51f28536cbc1751176a6480ed5f68.zip
- When two views were registered with differering ``for`` interfaces
or classes, and the ``for`` of first view registered was a superclass of the second, the ``repoze.bfg` view machinery would incorrectly associate the two views with the same "multiview". Multiviews are meant to be collections of views that have *exactly* the same for/request/viewname values, without taking inheritance into account. Symptom: wrong view callable found even when you had correctly specified a ``for_`` interface/class during view configuration for one or both view configurations.
Diffstat (limited to 'repoze')
-rw-r--r--repoze/bfg/configuration.py43
-rw-r--r--repoze/bfg/tests/test_configuration.py42
2 files changed, 83 insertions, 2 deletions
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index d474c557f..e6d421c34 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -586,15 +586,44 @@ class Configurator(object):
r_for_ = implementedBy(r_for_)
if not IInterface.providedBy(r_request_type):
r_request_type = implementedBy(r_request_type)
- old_view = self.registry.adapters.lookup((r_for_, r_request_type),
- IView, name=name)
+
+ registered = self.registry.adapters.registered
+
+ # A multiviews is a set of views which are registered for
+ # exactly the same context type/request type/name triad. Each
+ # consituent view in a multiview differs only by the
+ # predicates which it possesses.
+
+ # To find a previously registered view for a context
+ # type/request type/name triad, we need to use the
+ # ``registered`` method of the adapter registry rather than
+ # ``lookup``. ``registered`` ignores interface inheritance
+ # for the required and provided arguments, returning only a
+ # view registered previously with the *exact* triad we pass
+ # in.
+
+ # We need to do this three times, because we use three
+ # different interfaces as the ``provided`` interface while
+ # doing registrations, and ``registered`` performs exact
+ # matches on all the arguments it receives.
+
+ old_view = registered((r_for_, r_request_type), IView, name)
+ if old_view is None:
+ old_view = registered((r_for_, r_request_type), ISecuredView, name)
+ if old_view is None:
+ old_view = registered((r_for_, r_request_type), IMultiView,
+ name)
if old_view is None:
+ # No component was registered for any of our I*View
+ # interfaces exactly; this is the first view for this
+ # triad. We don't need a multiview.
if hasattr(derived_view, '__call_permissive__'):
view_iface = ISecuredView
else:
view_iface = IView
self.registry.registerAdapter(derived_view, (for_, request_type),
view_iface, name, info=_info)
+ final_view = derived_view
else:
# XXX we could try to be more efficient here and register
# a non-secured view for a multiview if none of the
@@ -614,6 +643,16 @@ class Configurator(object):
name=name)
self.registry.registerAdapter(multiview, (for_, request_type),
IMultiView, name, info=_info)
+ final_view = multiview
+
+ if name == 'edit.html':
+ import pprint
+ pprint.pprint ({'for':r_for_,
+ 'request_type':r_request_type,
+ 'old_view':old_view,
+ 'final_view':final_view,
+ 'numpreds':len(predicates)})
+ print
def add_route(self, name, path, view=None, view_for=None,
permission=None, factory=None, for_=None,
diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py
index 43d2cb153..da2f1d2c4 100644
--- a/repoze/bfg/tests/test_configuration.py
+++ b/repoze/bfg/tests/test_configuration.py
@@ -547,6 +547,48 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(wrapper.views, [(view2, None)])
self.assertEqual(wrapper(None, None), 'OK1')
+ def test_add_view_multiview_context_superclass_then_subclass(self):
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IMultiView
+ class ISuper(Interface):
+ pass
+ class ISub(ISuper):
+ pass
+ view = lambda *arg: 'OK'
+ view2 = lambda *arg: 'OK2'
+ config = self._makeOne()
+ config.registry.registerAdapter(
+ view, (ISuper, IRequest), IView, name='')
+ config.add_view(view=view2, for_=ISub)
+ wrapper = self._getViewCallable(config, ISuper, IRequest)
+ self.failIf(IMultiView.providedBy(wrapper))
+ self.assertEqual(wrapper(None, None), 'OK')
+ wrapper = self._getViewCallable(config, ISub, IRequest)
+ self.failIf(IMultiView.providedBy(wrapper))
+ self.assertEqual(wrapper(None, None), 'OK2')
+
+ def test_add_view_multiview_request_superclass_then_subclass(self):
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IMultiView
+ class ISubRequest(IRequest):
+ pass
+ view = lambda *arg: 'OK'
+ view2 = lambda *arg: 'OK2'
+ config = self._makeOne()
+ config.registry.registerAdapter(
+ view, (Interface, IRequest), IView, name='')
+ config.add_view(view=view2, request_type=ISubRequest)
+ wrapper = self._getViewCallable(config, Interface, IRequest)
+ self.failIf(IMultiView.providedBy(wrapper))
+ self.assertEqual(wrapper(None, None), 'OK')
+ wrapper = self._getViewCallable(config, Interface, ISubRequest)
+ self.failIf(IMultiView.providedBy(wrapper))
+ self.assertEqual(wrapper(None, None), 'OK2')
+
def test_add_view_multiview_call_ordering(self):
from zope.interface import directlyProvides
def view1(context, request): return 'view1'