From 7b67a79a8c2cb3ed4b35590f1d75ed1b97dc353d Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 23 Oct 2018 08:42:47 -0500 Subject: [PATCH 1/2] Fix 'verifyObject' for class objects with staticmethods on Python 3. Fixes #126 --- CHANGES.rst | 3 +++ src/zope/interface/tests/test_verify.py | 21 +++++++++++++++++++++ src/zope/interface/verify.py | 5 ++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 283ca733..6d6deb70 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,9 @@ Changes - Add support for Python 3.7 +- Fix ``verifyObject`` for class objects with staticmethods on + Python 3. See `issue 126 + `_. 4.5.0 (2018-04-19) ------------------ diff --git a/src/zope/interface/tests/test_verify.py b/src/zope/interface/tests/test_verify.py index b9e3d1f9..b7a357af 100644 --- a/src/zope/interface/tests/test_verify.py +++ b/src/zope/interface/tests/test_verify.py @@ -557,5 +557,26 @@ class IDummyModule(Interface): self.assertRaises(DoesNotImplement, self._callFUT, IDummyModule, dummy) + def test_staticmethod_hit_on_class(self): + from zope.interface import Interface + from zope.interface import provider + from zope.interface.verify import verifyObject + + class IFoo(Interface): + + def bar(a, b): + pass + + @provider(IFoo) + class Foo(object): + + @staticmethod + def bar(a, b): + pass + + # Don't use self._callFUT, we don't want to instantiate the + # class. + verifyObject(IFoo, Foo) + class OldSkool: pass diff --git a/src/zope/interface/verify.py b/src/zope/interface/verify.py index 098b484a..62bb64c4 100644 --- a/src/zope/interface/verify.py +++ b/src/zope/interface/verify.py @@ -66,8 +66,11 @@ def _verify(iface, candidate, tentative=0, vtype=None): continue if isinstance(attr, FunctionType): - if sys.version_info[0] >= 3 and isinstance(candidate, type): + if sys.version_info[0] >= 3 and isinstance(candidate, type) and vtype == 'c': # This is an "unbound method" in Python 3. + # Only unwrap this if we're verifying implementedBy; + # otherwise we can unwrap @staticmethod on classes that directly + # provide an interface. meth = fromFunction(attr, iface, name=name, imlevel=1) else: From 705bad3d86a876132578b7ae7c85e94179267061 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 23 Oct 2018 10:45:01 -0500 Subject: [PATCH 2/2] Fix coverage. --- .coveragerc | 1 + src/zope/interface/tests/test_verify.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.coveragerc b/.coveragerc index 29bc9dec..e8d7d252 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,4 +8,5 @@ exclude_lines = pragma: no cover class I[A-Z]\w+\((Interface|I[A-Z].*)\): raise NotImplementedError + raise AssertionError self\.fail diff --git a/src/zope/interface/tests/test_verify.py b/src/zope/interface/tests/test_verify.py index b7a357af..5ad8bff2 100644 --- a/src/zope/interface/tests/test_verify.py +++ b/src/zope/interface/tests/test_verify.py @@ -565,14 +565,14 @@ def test_staticmethod_hit_on_class(self): class IFoo(Interface): def bar(a, b): - pass + "The bar method" @provider(IFoo) class Foo(object): @staticmethod def bar(a, b): - pass + raise AssertionError("We're never actually called") # Don't use self._callFUT, we don't want to instantiate the # class.