summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-03-30 09:41:32 -0500
committerGitHub <noreply@github.com>2020-03-30 09:41:32 -0500
commitf20604d5ca34cff2fc56cf4333ec6a4439201745 (patch)
tree677dd6679d3a79cd6bb5cada20235bd6d0224ed3
parent1b83ad47e400358dc27e28d1dea013d44b394bde (diff)
parentbfedd6f58706810ee5bac3c0d08650dcac30c215 (diff)
downloadzope-interface-f20604d5ca34cff2fc56cf4333ec6a4439201745.tar.gz
Merge pull request #198 from zopefoundation/issue197
Ensure that objects that implement no interfaces still have Interface in iro and sro
-rw-r--r--CHANGES.rst7
-rw-r--r--src/zope/interface/declarations.py2
-rw-r--r--src/zope/interface/interface.py6
-rw-r--r--src/zope/interface/tests/test_declarations.py58
4 files changed, 60 insertions, 13 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 961bf72..d6965cb 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,7 +5,12 @@
5.0.2 (unreleased)
==================
-- Nothing changed yet.
+- Ensure that objects that implement no interfaces (such as direct
+ subclasses of ``object``) still include ``Interface`` itself in
+ their ``__iro___`` and ``__sro___``. This fixes adapter registry
+ lookups for such objects when the adapter is registered for
+ ``Interface``. See `issue 197
+ <https://github.com/zopefoundation/zope.interface/issues/197>`_.
5.0.1 (2020-03-21)
diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py
index d85dbf4..e84a728 100644
--- a/src/zope/interface/declarations.py
+++ b/src/zope/interface/declarations.py
@@ -180,7 +180,7 @@ class _ImmutableDeclaration(Declaration):
return iter(())
def extends(self, interface, strict=True):
- return False
+ return interface is self._ROOT
def get(self, name, default=None):
return default
diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py
index 6a1ca2a..ac55633 100644
--- a/src/zope/interface/interface.py
+++ b/src/zope/interface/interface.py
@@ -1064,3 +1064,9 @@ from zope.interface.declarations import implementedBy
from zope.interface.declarations import providedBy
from zope.interface.exceptions import InvalidInterface
from zope.interface.exceptions import BrokenImplementation
+
+# This ensures that ``Interface`` winds up in the flattened()
+# list of the immutable declaration. It correctly overrides changed()
+# as a no-op, so we bypass that.
+from zope.interface.declarations import _empty
+Specification.changed(_empty, _empty)
diff --git a/src/zope/interface/tests/test_declarations.py b/src/zope/interface/tests/test_declarations.py
index b0875c4..9344968 100644
--- a/src/zope/interface/tests/test_declarations.py
+++ b/src/zope/interface/tests/test_declarations.py
@@ -74,7 +74,51 @@ class NamedTests(unittest.TestCase):
self.assertEqual(foo.__component_name__, u'foo')
-class DeclarationTests(unittest.TestCase):
+class EmptyDeclarationTests(unittest.TestCase):
+ # Tests that should pass for all objects that are empty
+ # declarations. This includes a Declaration explicitly created
+ # that way, and the empty ImmutableDeclaration.
+ def _getEmpty(self):
+ from zope.interface.declarations import Declaration
+ return Declaration()
+
+ def test___iter___empty(self):
+ decl = self._getEmpty()
+ self.assertEqual(list(decl), [])
+
+ def test_flattened_empty(self):
+ from zope.interface.interface import Interface
+ decl = self._getEmpty()
+ self.assertEqual(list(decl.flattened()), [Interface])
+
+ def test___contains___empty(self):
+ from zope.interface.interface import Interface
+ decl = self._getEmpty()
+ self.assertNotIn(Interface, decl)
+
+ def test_extends_empty(self):
+ from zope.interface.interface import Interface
+ decl = self._getEmpty()
+ self.assertTrue(decl.extends(Interface))
+ self.assertTrue(decl.extends(Interface, strict=True))
+
+ def test_interfaces_empty(self):
+ decl = self._getEmpty()
+ l = list(decl.interfaces())
+ self.assertEqual(l, [])
+
+ def test___sro___(self):
+ from zope.interface.interface import Interface
+ decl = self._getEmpty()
+ self.assertEqual(decl.__sro__, (decl, Interface,))
+
+ def test___iro___(self):
+ from zope.interface.interface import Interface
+ decl = self._getEmpty()
+ self.assertEqual(decl.__iro__, (Interface,))
+
+
+class DeclarationTests(EmptyDeclarationTests):
def _getTargetClass(self):
from zope.interface.declarations import Declaration
@@ -128,10 +172,6 @@ class DeclarationTests(unittest.TestCase):
decl = self._makeOne(IFoo)
self.assertIn(IFoo, decl)
- def test___iter___empty(self):
- decl = self._makeOne()
- self.assertEqual(list(decl), [])
-
def test___iter___single_base(self):
from zope.interface.interface import InterfaceClass
IFoo = InterfaceClass('IFoo')
@@ -159,11 +199,6 @@ class DeclarationTests(unittest.TestCase):
decl = self._makeOne(IBar, (IFoo, IBar))
self.assertEqual(list(decl), [IBar, IFoo])
- def test_flattened_empty(self):
- from zope.interface.interface import Interface
- decl = self._makeOne()
- self.assertEqual(list(decl.flattened()), [Interface])
-
def test_flattened_single_base(self):
from zope.interface.interface import Interface
from zope.interface.interface import InterfaceClass
@@ -244,7 +279,7 @@ class DeclarationTests(unittest.TestCase):
self.assertEqual(list(after), [IFoo, IBar, IBaz])
-class TestImmutableDeclaration(unittest.TestCase):
+class TestImmutableDeclaration(EmptyDeclarationTests):
def _getTargetClass(self):
from zope.interface.declarations import _ImmutableDeclaration
@@ -297,6 +332,7 @@ class TestImmutableDeclaration(unittest.TestCase):
self.assertIsNone(self._getEmpty().get('name'))
self.assertEqual(self._getEmpty().get('name', 42), 42)
+
class TestImplements(NameAndModuleComparisonTestsMixin,
unittest.TestCase):