summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-04-06 11:53:21 -0500
committerJason Madden <jamadden@gmail.com>2020-04-07 07:32:06 -0500
commit97c0fbc4a3a532d1b2ec044994422d1a4ada4754 (patch)
treea1e05da51efc52435efbe4bee8b3f619a2f4ff80
parent4cb3e63d3f45f0d4337bf890ec8508c3072be772 (diff)
downloadzope-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.py5
-rw-r--r--src/zope/interface/tests/test_declarations.py163
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.