diff options
| author | Jason Madden <jamadden@gmail.com> | 2020-03-05 09:52:35 -0600 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2020-03-09 12:51:18 -0500 |
| commit | 1e720c3819b8daa07aaad0ee0258d9035597f27d (patch) | |
| tree | 93f0ad828f0d8c8d23670aefeeeccaead854794a /src/zope/interface/tests | |
| parent | c931999371b106ca4638a5cfb40fe0e431840358 (diff) | |
| download | zope-interface-issue11.tar.gz | |
Make provided/implementedBy and adapter registries respect super().issue11
The query functions now start by looking at the next class in the MRO (interfaces directly provided by the underlying object are not found).
Adapter registries automatically pick up providedBy change to start finding the correct implementations of adapters, but to make that really useful they needed to change to unpack super() arguments and pass __self__ to the factory.
Fixes #11
Unfortunately, this makes PyPy unable to build the C extensions.
Additional crash-safety for adapter lookup.
Make the C functions get the cache only after resolving the
``required`` into a tuple, in case of side-effects like...clearing the
cache. This could lead to the ``cache`` object being deallocated
before we used it.
Drop the ``tuplefy`` function in favor of a direct call to
``PySequence_Tuple``. It's what the ``tuple`` constructor would do
anyway and saves a few steps.
Make sure that getting ``providedBy(super())`` and
``implementedBy(super())`` have no side effects.
Diffstat (limited to 'src/zope/interface/tests')
| -rw-r--r-- | src/zope/interface/tests/test_declarations.py | 320 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_registry.py | 155 |
2 files changed, 473 insertions, 2 deletions
diff --git a/src/zope/interface/tests/test_declarations.py b/src/zope/interface/tests/test_declarations.py index 5256b07..ccf4dde 100644 --- a/src/zope/interface/tests/test_declarations.py +++ b/src/zope/interface/tests/test_declarations.py @@ -529,6 +529,177 @@ class Test_implementedByFallback(unittest.TestCase): __implemented__ = impl self.assertTrue(self._callFUT(Foo) is impl) + def test_super_when_base_implements_interface(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + @implementer(IBase) + class Base(object): + pass + + @implementer(IDerived) + class Derived(Base): + pass + + self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) + sup = super(Derived, Derived) + self.assertEqual(list(self._callFUT(sup)), [IBase]) + + def test_super_when_base_implements_interface_diamond(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + @implementer(IBase) + class Base(object): + pass + + class Child1(Base): + pass + + class Child2(Base): + pass + + @implementer(IDerived) + class Derived(Child1, Child2): + pass + + self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) + sup = super(Derived, Derived) + self.assertEqual(list(self._callFUT(sup)), [IBase]) + + def test_super_when_parent_implements_interface_diamond(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + + class Base(object): + pass + + class Child1(Base): + pass + + @implementer(IBase) + class Child2(Base): + pass + + @implementer(IDerived) + class Derived(Child1, Child2): + pass + + self.assertEqual(Derived.__mro__, (Derived, Child1, Child2, Base, object)) + self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) + sup = super(Derived, Derived) + fut = self._callFUT(sup) + self.assertEqual(list(fut), [IBase]) + self.assertIsNone(fut._dependents) + + def test_super_when_base_doesnt_implement_interface(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + class Base(object): + pass + + @implementer(IDerived) + class Derived(Base): + pass + + self.assertEqual(list(self._callFUT(Derived)), [IDerived]) + + sup = super(Derived, Derived) + self.assertEqual(list(self._callFUT(sup)), []) + + def test_super_when_base_is_object(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + @implementer(IDerived) + class Derived(object): + pass + + self.assertEqual(list(self._callFUT(Derived)), [IDerived]) + + sup = super(Derived, Derived) + self.assertEqual(list(self._callFUT(sup)), []) + def test_super_multi_level_multi_inheritance(self): + from zope.interface.declarations import implementer + from zope.interface import Interface + + class IBase(Interface): + pass + + class IM1(Interface): + pass + + class IM2(Interface): + pass + + class IDerived(IBase): + pass + + class IUnrelated(Interface): + pass + + @implementer(IBase) + class Base(object): + pass + + @implementer(IM1) + class M1(Base): + pass + + @implementer(IM2) + class M2(Base): + pass + + @implementer(IDerived, IUnrelated) + class Derived(M1, M2): + pass + + d = Derived + sd = super(Derived, Derived) + sm1 = super(M1, Derived) + sm2 = super(M2, Derived) + + self.assertEqual(list(self._callFUT(d)), + [IDerived, IUnrelated, IM1, IBase, IM2]) + self.assertEqual(list(self._callFUT(sd)), + [IM1, IBase, IM2]) + self.assertEqual(list(self._callFUT(sm1)), + [IM2, IBase]) + self.assertEqual(list(self._callFUT(sm2)), + [IBase]) + class Test_implementedBy(Test_implementedByFallback, OptimizationTestMixin): @@ -1575,6 +1746,155 @@ class Test_providedByFallback(unittest.TestCase): spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) + def test_super_when_base_implements_interface(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + @implementer(IBase) + class Base(object): + pass + + @implementer(IDerived) + class Derived(Base): + pass + + derived = Derived() + self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) + + sup = super(Derived, derived) + fut = self._callFUT(sup) + self.assertIsNone(fut._dependents) + self.assertEqual(list(fut), [IBase]) + + def test_super_when_base_doesnt_implement_interface(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + class Base(object): + pass + + @implementer(IDerived) + class Derived(Base): + pass + + derived = Derived() + self.assertEqual(list(self._callFUT(derived)), [IDerived]) + + sup = super(Derived, derived) + self.assertEqual(list(self._callFUT(sup)), []) + + def test_super_when_base_is_object(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + @implementer(IDerived) + class Derived(object): + pass + + derived = Derived() + self.assertEqual(list(self._callFUT(derived)), [IDerived]) + + sup = super(Derived, derived) + fut = self._callFUT(sup) + self.assertIsNone(fut._dependents) + self.assertEqual(list(fut), []) + + def test_super_when_object_directly_provides(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + from zope.interface.declarations import directlyProvides + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + @implementer(IBase) + class Base(object): + pass + + class Derived(Base): + pass + + derived = Derived() + self.assertEqual(list(self._callFUT(derived)), [IBase]) + + directlyProvides(derived, IDerived) + self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) + + sup = super(Derived, derived) + fut = self._callFUT(sup) + self.assertIsNone(fut._dependents) + self.assertEqual(list(fut), [IBase]) + + def test_super_multi_level_multi_inheritance(self): + from zope.interface.declarations import implementer + from zope.interface import Interface + + class IBase(Interface): + pass + + class IM1(Interface): + pass + + class IM2(Interface): + pass + + class IDerived(IBase): + pass + + class IUnrelated(Interface): + pass + + @implementer(IBase) + class Base(object): + pass + + @implementer(IM1) + class M1(Base): + pass + + @implementer(IM2) + class M2(Base): + pass + + @implementer(IDerived, IUnrelated) + class Derived(M1, M2): + pass + + d = Derived() + sd = super(Derived, d) + sm1 = super(M1, d) + sm2 = super(M2, d) + + self.assertEqual(list(self._callFUT(d)), + [IDerived, IUnrelated, IM1, IBase, IM2]) + self.assertEqual(list(self._callFUT(sd)), + [IM1, IBase, IM2]) + self.assertEqual(list(self._callFUT(sm1)), + [IM2, IBase]) + self.assertEqual(list(self._callFUT(sm2)), + [IBase]) + class Test_providedBy(Test_providedByFallback, OptimizationTestMixin): diff --git a/src/zope/interface/tests/test_registry.py b/src/zope/interface/tests/test_registry.py index e5a8eb0..db91c52 100644 --- a/src/zope/interface/tests/test_registry.py +++ b/src/zope/interface/tests/test_registry.py @@ -1332,8 +1332,102 @@ class ComponentsTests(unittest.TestCase): comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo) adapter = comp.getAdapter(_context, ifoo) - self.assertTrue(isinstance(adapter, _Factory)) - self.assertTrue(adapter.context is _context) + self.assertIsInstance(adapter, _Factory) + self.assertIs(adapter.context, _context) + + def test_getAdapter_hit_super(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + class IFoo(Interface): + pass + + @implementer(IBase) + class Base(object): + pass + + @implementer(IDerived) + class Derived(Base): + pass + + class AdapterBase(object): + def __init__(self, context): + self.context = context + + class AdapterDerived(object): + def __init__(self, context): + self.context = context + + comp = self._makeOne() + comp.registerAdapter(AdapterDerived, (IDerived,), IFoo) + comp.registerAdapter(AdapterBase, (IBase,), IFoo) + self._should_not_change(comp) + + derived = Derived() + adapter = comp.getAdapter(derived, IFoo) + self.assertIsInstance(adapter, AdapterDerived) + self.assertIs(adapter.context, derived) + + supe = super(Derived, derived) + adapter = comp.getAdapter(supe, IFoo) + self.assertIsInstance(adapter, AdapterBase) + self.assertIs(adapter.context, derived) + + def test_getAdapter_hit_super_when_parent_implements_interface_diamond(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + class IFoo(Interface): + pass + + class Base(object): + pass + + class Child1(Base): + pass + + @implementer(IBase) + class Child2(Base): + pass + + @implementer(IDerived) + class Derived(Child1, Child2): + pass + + class AdapterBase(object): + def __init__(self, context): + self.context = context + + class AdapterDerived(object): + def __init__(self, context): + self.context = context + + comp = self._makeOne() + comp.registerAdapter(AdapterDerived, (IDerived,), IFoo) + comp.registerAdapter(AdapterBase, (IBase,), IFoo) + self._should_not_change(comp) + + derived = Derived() + adapter = comp.getAdapter(derived, IFoo) + self.assertIsInstance(adapter, AdapterDerived) + self.assertIs(adapter.context, derived) + + supe = super(Derived, derived) + adapter = comp.getAdapter(supe, IFoo) + self.assertIsInstance(adapter, AdapterBase) + self.assertIs(adapter.context, derived) def test_queryMultiAdapter_miss(self): from zope.interface.declarations import InterfaceClass @@ -1448,6 +1542,63 @@ class ComponentsTests(unittest.TestCase): self.assertTrue(isinstance(adapter, _Factory)) self.assertEqual(adapter.context, (_context1, _context2)) + def _should_not_change(self, comp): + # Be sure that none of the underlying structures + # get told that they have changed during this process + # because that invalidates caches. + def no_changes(*args): + self.fail("Nothing should get changed") + comp.changed = no_changes + comp.adapters.changed = no_changes + comp.adapters._v_lookup.changed = no_changes + + def test_getMultiAdapter_hit_super(self): + from zope.interface import Interface + from zope.interface.declarations import implementer + + class IBase(Interface): + pass + + class IDerived(IBase): + pass + + class IFoo(Interface): + pass + + @implementer(IBase) + class Base(object): + pass + + @implementer(IDerived) + class Derived(Base): + pass + + class AdapterBase(object): + def __init__(self, context1, context2): + self.context1 = context1 + self.context2 = context2 + + class AdapterDerived(AdapterBase): + pass + + comp = self._makeOne() + comp.registerAdapter(AdapterDerived, (IDerived, IDerived), IFoo) + comp.registerAdapter(AdapterBase, (IBase, IDerived), IFoo) + self._should_not_change(comp) + + derived = Derived() + adapter = comp.getMultiAdapter((derived, derived), IFoo) + self.assertIsInstance(adapter, AdapterDerived) + self.assertIs(adapter.context1, derived) + self.assertIs(adapter.context2, derived) + + supe = super(Derived, derived) + adapter = comp.getMultiAdapter((supe, derived), IFoo) + self.assertIsInstance(adapter, AdapterBase) + self.assertNotIsInstance(adapter, AdapterDerived) + self.assertIs(adapter.context1, derived) + self.assertIs(adapter.context2, derived) + def test_getAdapters_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer |
