diff options
author | Jason Madden <jamadden@gmail.com> | 2020-04-06 11:53:21 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2020-04-07 07:32:06 -0500 |
commit | 97c0fbc4a3a532d1b2ec044994422d1a4ada4754 (patch) | |
tree | a1e05da51efc52435efbe4bee8b3f619a2f4ff80 | |
parent | 4cb3e63d3f45f0d4337bf890ec8508c3072be772 (diff) | |
download | zope-interface-issue199.tar.gz |
Fix implementerOnly when duplicating something inherited from the parent.issue199
Seen in plone.app.testing as a problem in Products.GenericSetup.
Added tests for that.
-rw-r--r-- | src/zope/interface/declarations.py | 5 | ||||
-rw-r--r-- | src/zope/interface/tests/test_declarations.py | 163 |
2 files changed, 89 insertions, 79 deletions
diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py index 1c343a2..b8af418 100644 --- a/src/zope/interface/declarations.py +++ b/src/zope/interface/declarations.py @@ -431,8 +431,13 @@ def classImplementsOnly(cls, *interfaces): in *interfaces*. This can be used to alter the interface resolution order. """ spec = implementedBy(cls) + # Clear out everything inherited. It's important to + # also clear the bases right now so that we don't improperly discard + # interfaces that are already implemented by *old* bases that we're + # about to get rid of. spec.declared = () spec.inherit = None + spec.__bases__ = () _classImplements_ordered(spec, interfaces, ()) diff --git a/src/zope/interface/tests/test_declarations.py b/src/zope/interface/tests/test_declarations.py index c69b645..a730fdd 100644 --- a/src/zope/interface/tests/test_declarations.py +++ b/src/zope/interface/tests/test_declarations.py @@ -784,27 +784,58 @@ class Test_implementedBy(Test_implementedByFallback, return implementedBy -class Test_classImplementsOnly(unittest.TestCase): +class _ImplementsTestMixin(object): + FUT_SETS_PROVIDED_BY = True - def _callFUT(self, *args, **kw): - from zope.interface.declarations import classImplementsOnly - return classImplementsOnly(*args, **kw) + def _callFUT(self, cls, iface): + # Declare that *cls* implements *iface*; return *cls* + raise NotImplementedError - def test_no_existing(self): + def _check_implementer(self, Foo, + orig_spec=None, + spec_name=__name__ + '.Foo', + inherit="not given"): from zope.interface.declarations import ClassProvides from zope.interface.interface import InterfaceClass + IFoo = InterfaceClass('IFoo') + + returned = self._callFUT(Foo, IFoo) + + self.assertIs(returned, Foo) + spec = Foo.__implemented__ + if orig_spec is not None: + self.assertIs(spec, orig_spec) + + self.assertEqual(spec.__name__, + spec_name) + inherit = Foo if inherit == "not given" else inherit + self.assertIs(spec.inherit, inherit) + self.assertIs(Foo.__implemented__, spec) + if self.FUT_SETS_PROVIDED_BY: + self.assertIsInstance(Foo.__providedBy__, ClassProvides) + self.assertIsInstance(Foo.__provides__, ClassProvides) + self.assertEqual(Foo.__provides__, Foo.__providedBy__) + + return Foo, IFoo + + def test_oldstyle_class(self): + # This only matters on Python 2 + class Foo: + pass + self._check_implementer(Foo) + + def test_newstyle_class(self): class Foo(object): pass - ifoo = InterfaceClass('IFoo') - self._callFUT(Foo, ifoo) - spec = Foo.__implemented__ # pylint:disable=no-member - self.assertEqual(spec.__name__, - 'zope.interface.tests.test_declarations.Foo') - self.assertIsNone(spec.inherit) - self.assertIs(Foo.__implemented__, spec) # pylint:disable=no-member - self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member - self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member - self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member + self._check_implementer(Foo) + +class Test_classImplementsOnly(_ImplementsTestMixin, unittest.TestCase): + FUT_SETS_PROVIDED_BY = False + + def _callFUT(self, cls, iface): + from zope.interface.declarations import classImplementsOnly + classImplementsOnly(cls, iface) + return cls def test_w_existing_Implements(self): from zope.interface.declarations import Implements @@ -822,50 +853,52 @@ class Test_classImplementsOnly(unittest.TestCase): self.assertEqual(impl.inherit, None) self.assertEqual(impl.declared, (IBar,)) + def test_oldstyle_class(self): + from zope.interface.declarations import Implements + from zope.interface.interface import InterfaceClass + IBar = InterfaceClass('IBar') + old_spec = Implements(IBar) -class Test_classImplements(unittest.TestCase): - - def _callFUT(self, cls, iface): - from zope.interface.declarations import classImplements - result = classImplements(cls, iface) # pylint:disable=assignment-from-no-return - self.assertIsNone(result) - return cls + class Foo: + __implemented__ = old_spec + self._check_implementer(Foo, old_spec, '?', inherit=None) - def __check_implementer(self, Foo): - from zope.interface.declarations import ClassProvides + def test_newstyle_class(self): + from zope.interface.declarations import Implements from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - - returned = self._callFUT(Foo, IFoo) + IBar = InterfaceClass('IBar') + old_spec = Implements(IBar) - self.assertIs(returned, Foo) - spec = Foo.__implemented__ + class Foo(object): + __implemented__ = old_spec + self._check_implementer(Foo, old_spec, '?', inherit=None) - self.assertEqual(spec.__name__, - 'zope.interface.tests.test_declarations.Foo') - self.assertIs(spec.inherit, Foo) - self.assertIs(Foo.__implemented__, spec) - self.assertIsInstance(Foo.__providedBy__, ClassProvides) - self.assertIsInstance(Foo.__provides__, ClassProvides) - self.assertEqual(Foo.__provides__, Foo.__providedBy__) - return Foo, IFoo + def test_redundant_with_super_still_implements(self): + Base, IBase = self._check_implementer( + type('Foo', (object,), {}), + inherit=None, + ) - def test_oldstyle_class(self): - # This only matters on Python 2 - class Foo: + class Child(Base): pass - self.__check_implementer(Foo) - def test_newstyle_class(self): - class Foo(object): - pass - self.__check_implementer(Foo) + self._callFUT(Child, IBase) + self.assertTrue(IBase.implementedBy(Child)) + + +class Test_classImplements(_ImplementsTestMixin, unittest.TestCase): + + def _callFUT(self, cls, iface): + from zope.interface.declarations import classImplements + result = classImplements(cls, iface) # pylint:disable=assignment-from-no-return + self.assertIsNone(result) + return cls def __check_implementer_redundant(self, Base): # If we @implementer exactly what was already present, we write # no declared attributes on the parent (we still set everything, though) - Base, IBase = self.__check_implementer(Base) + Base, IBase = self._check_implementer(Base) class Child(Base): pass @@ -1026,7 +1059,7 @@ class Test_implementer(Test_classImplements): -class Test_implementer_only(unittest.TestCase): +class Test_implementer_only(Test_classImplementsOnly): def _getTargetClass(self): from zope.interface.declarations import implementer_only @@ -1035,6 +1068,10 @@ class Test_implementer_only(unittest.TestCase): def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) + def _callFUT(self, cls, iface): + decorator = self._makeOne(iface) + return decorator(cls) + def test_function(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') @@ -1052,38 +1089,6 @@ class Test_implementer_only(unittest.TestCase): raise NotImplementedError() self.assertRaises(ValueError, decorator, Bar._method) - def test_oldstyle_class(self): - # TODO Py3 story - from zope.interface.declarations import Implements - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - old_spec = Implements(IBar) - class Foo: - __implemented__ = old_spec - decorator = self._makeOne(IFoo) - returned = decorator(Foo) - self.assertTrue(returned is Foo) - spec = Foo.__implemented__ - self.assertEqual(spec.__name__, '?') - self.assertTrue(spec.inherit is None) - self.assertTrue(Foo.__implemented__ is spec) - - def test_newstyle_class(self): - from zope.interface.declarations import Implements - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - old_spec = Implements(IBar) - class Foo(object): - __implemented__ = old_spec - decorator = self._makeOne(IFoo) - returned = decorator(Foo) - self.assertTrue(returned is Foo) - spec = Foo.__implemented__ - self.assertEqual(spec.__name__, '?') - self.assertTrue(spec.inherit is None) - self.assertTrue(Foo.__implemented__ is spec) # Test '_implements' by way of 'implements{,Only}', its only callers. |