From b1807049d47afef711c467b785fae6aab7be851a Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Tue, 7 Apr 2020 07:04:44 -0500 Subject: Feedback from review: whitespace, doc clarification, and a unit test showing the precedence of __conform__ vs __adapt__. --- src/zope/interface/common/__init__.py | 2 +- src/zope/interface/interface.py | 5 +++- src/zope/interface/interfaces.py | 30 +++++++++++------------ src/zope/interface/tests/test_interface.py | 39 +++++++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 18 deletions(-) (limited to 'src/zope/interface') diff --git a/src/zope/interface/common/__init__.py b/src/zope/interface/common/__init__.py index b40c317..01f0bd3 100644 --- a/src/zope/interface/common/__init__.py +++ b/src/zope/interface/common/__init__.py @@ -259,5 +259,5 @@ class ABCInterfaceClass(InterfaceClass): return set(itertools.chain(registered, self.__extra_classes)) -ABCInterface = ABCInterfaceClass.__new__(ABCInterfaceClass, 'ABCInterfaceClass', (), {}) +ABCInterface = ABCInterfaceClass.__new__(ABCInterfaceClass, 'ABCInterface', (), {}) InterfaceClass.__init__(ABCInterface, 'ABCInterface', (Interface,), {}) diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py index ff26d33..f819441 100644 --- a/src/zope/interface/interface.py +++ b/src/zope/interface/interface.py @@ -662,7 +662,10 @@ def interfacemethod(func): This is a decorator that functions like `staticmethod` et al. The primary use of this decorator is to allow interface definitions to - define the ``__adapt__`` method. + define the ``__adapt__`` method, but other interface methods can be + overridden this way too. + + .. seealso:: `zope.interface.interfaces.IInterfaceDeclaration.interfacemethod` """ f_locals = sys._getframe(1).f_locals methods = f_locals.setdefault(INTERFACE_METHODS, {}) diff --git a/src/zope/interface/interfaces.py b/src/zope/interface/interfaces.py index 6321d0c..9334374 100644 --- a/src/zope/interface/interfaces.py +++ b/src/zope/interface/interfaces.py @@ -490,7 +490,7 @@ class IInterfaceDeclaration(Interface): This is a way of executing :meth:`IElement.setTaggedValue` from the definition of the interface. For example:: - class IFoo(Interface): + class IFoo(Interface): taggedValue('key', 'value') .. seealso:: `zope.interface.taggedValue` @@ -505,15 +505,15 @@ class IInterfaceDeclaration(Interface): For example:: - def check_range(ob): - if ob.max < ob.min: - range ValueError + def check_range(ob): + if ob.max < ob.min: + raise ValueError("max value is less than min value") - class IRange(Interface): - min = Attribute("The min value") - max = Attribute("The max value") + class IRange(Interface): + min = Attribute("The min value") + max = Attribute("The max value") - invariant(check_range) + invariant(check_range) .. seealso:: `zope.interface.invariant` """ @@ -530,13 +530,13 @@ class IInterfaceDeclaration(Interface): For example:: - class IRange(Interface): - @interfacemethod - def __adapt__(self, obj): - if isinstance(obj, range): - # Return the builtin ``range`` as-is - return obj - return super(type(IRange), self).__adapt__(obj) + class IRange(Interface): + @interfacemethod + def __adapt__(self, obj): + if isinstance(obj, range): + # Return the builtin ``range`` as-is + return obj + return super(type(IRange), self).__adapt__(obj) You can use ``super`` to call the parent class functionality. Note that the zero-argument version (``super().__adapt__``) works on Python 3.6 and above, but diff --git a/src/zope/interface/tests/test_interface.py b/src/zope/interface/tests/test_interface.py index 4bbed1a..036e858 100644 --- a/src/zope/interface/tests/test_interface.py +++ b/src/zope/interface/tests/test_interface.py @@ -2177,9 +2177,46 @@ class InterfaceTests(unittest.TestCase): pass self.assertEqual(42, I(object())) - # __adapt__ supercedes providedBy() if defined. + # __adapt__ can ignore the fact that the object provides + # the interface if it chooses. self.assertEqual(42, I(O())) + def test___call___w_overridden_adapt_and_conform(self): + # Conform is first, taking precedence over __adapt__, + # *if* it returns non-None + from zope.interface import Interface + from zope.interface import interfacemethod + from zope.interface import implementer + + class IAdapt(Interface): + @interfacemethod + def __adapt__(self, obj): + return 42 + + class ISimple(Interface): + """Nothing special.""" + + @implementer(IAdapt) + class Conform24(object): + def __conform__(self, iface): + return 24 + + @implementer(IAdapt) + class ConformNone(object): + def __conform__(self, iface): + return None + + self.assertEqual(42, IAdapt(object())) + + self.assertEqual(24, ISimple(Conform24())) + self.assertEqual(24, IAdapt(Conform24())) + + with self.assertRaises(TypeError): + ISimple(ConformNone()) + + self.assertEqual(42, IAdapt(ConformNone())) + + def test___call___w_overridden_adapt_call_super(self): import sys from zope.interface import Interface -- cgit v1.2.1