diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-12-02 21:10:57 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-12-02 21:10:57 +0000 |
| commit | 6a6f6cc370c51f28536cbc1751176a6480ed5f68 (patch) | |
| tree | b8ff09ae7a9119e2f1198245877545186135ed9b /repoze | |
| parent | 16db57af0922f93026a5b046897acdaa8dd1e602 (diff) | |
| download | pyramid-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.py | 43 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_configuration.py | 42 |
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' |
