[Zope-dev] [Checkins] SVN: zope.interface/trunk/ Correct comparison of interfaces from different modules but with the same name.

Gediminas Paulauskas menesis at pov.lt
Tue Jun 14 11:40:04 EDT 2011


Hello,

I ran tests in some zope modules with python3.2, and got an exception:

$ cd zope.proxy
$ python3.2 setup.py test
...
  File "/home/menesis/src/zope.proxy/build/lib.linux-i686-3.2/zope/proxy/__init__.py", line 16, in <module>
    from zope.interface import moduleProvides
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/__init__.py", line 53, in <module>
    from zope.interface.interface import Interface, _wire
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/interface.py", line 710, in <module>
    Interface = InterfaceClass("Interface", __module__ = 'zope.interface')
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/interface.py", line 479, in __init__
    Specification.__init__(self, bases)
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/interface.py", line 266, in __init__
    self.__bases__ = tuple(bases)
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/interface.py", line 290, in __setBases
    self.changed(self)
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/interface.py", line 309, in changed
    ancestors = ro(self)
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/ro.py", line 22, in ro
    return mergeOrderings([_flatten(object)])
  File "/home/menesis/src/zope.proxy/zope.interface-3.6.3-py3.2-linux-i686.egg/zope/interface/ro.py", line 51, in mergeOrderings
    if o not in seen:
TypeError: unhashable type: 'InterfaceClass'

I found the following to explain what happens:
http://stackoverflow.com/questions/1608842/types-that-define-eq-are-unhashable-in-python-3-x

And a commit which does exactly that: define an __eq__ but not __hash__.

Adding a simple

    def __hash__(self):
        return id(self)

fixes the problem.

This change was released as zope.interface 3.6.3. Earlier version does not have this problem.

On Thu, 2011-05-26 12:10-0400, Tres Seaver wrote:
> Log message for revision 121819:
>   Correct comparison of interfaces from different modules but with the same name.
>   
>   Fixes LP #570942.
>   
> 
> Changed:
>   U   zope.interface/trunk/CHANGES.txt
>   U   zope.interface/trunk/src/zope/interface/interface.py
>   U   zope.interface/trunk/src/zope/interface/tests/test_interface.py
>   U   zope.interface/trunk/src/zope/interface/tests/test_sorting.py
> 
> -=-
> Modified: zope.interface/trunk/CHANGES.txt
> ===================================================================
> --- zope.interface/trunk/CHANGES.txt	2011-05-26 11:27:21 UTC (rev 121818)
> +++ zope.interface/trunk/CHANGES.txt	2011-05-26 16:10:57 UTC (rev 121819)
> @@ -1,7 +1,12 @@
>  ``zope.interface Changelog``
>  ============================
>  
> +3.6.3 (unreleased)
> +------------------
>  
> +- LP #570942:  Now correctly compare interfaces  from different modules but
> +  with the same names.
> +
>  3.6.2 (2011-05-17)
>  ------------------
>  
> 
> Modified: zope.interface/trunk/src/zope/interface/interface.py
> ===================================================================
> --- zope.interface/trunk/src/zope/interface/interface.py	2011-05-26 11:27:21 UTC (rev 121818)
> +++ zope.interface/trunk/src/zope/interface/interface.py	2011-05-26 16:10:57 UTC (rev 121819)
> @@ -670,32 +670,42 @@
>          sort before None.
>  
>          """
> -        if o1 == o2:
> -            return 0
> -
>          if o1 is None:
>              return 1
>          if o2 is None:
>              return -1
>  
> -        n1 = (getattr(o1, '__name__', ''),
> -              getattr(getattr(o1,  '__module__', None), '__name__', ''))
> -        n2 = (getattr(o2, '__name__', ''),
> -              getattr(getattr(o2,  '__module__', None), '__name__', ''))
> +        n1 = (getattr(o1, '__name__', ''), getattr(o1,  '__module__', ''))
> +        n2 = (getattr(o2, '__name__', ''), getattr(o2,  '__module__', ''))
>  
> +        # This spelling works under Python3, which doesn't have cmp().
>          return (n1 > n2) - (n1 < n2)
>  
> +    def __eq__(self, other):
> +        c = self.__cmp(self, other)
> +        return c == 0
> +
> +    def __ne__(self, other):
> +        c = self.__cmp(self, other)
> +        return c != 0
> +
>      def __lt__(self, other):
>          c = self.__cmp(self, other)
> -        #print '<', self, other, c < 0, c
>          return c < 0
>  
> +    def __le__(self, other):
> +        c = self.__cmp(self, other)
> +        return c <= 0
> +
>      def __gt__(self, other):
>          c = self.__cmp(self, other)
> -        #print '>', self, other, c > 0, c
>          return c > 0
>  
> +    def __ge__(self, other):
> +        c = self.__cmp(self, other)
> +        return c >= 0
>  
> +
>  Interface = InterfaceClass("Interface", __module__ = 'zope.interface')
>  
>  class Attribute(Element):
> 
> Modified: zope.interface/trunk/src/zope/interface/tests/test_interface.py
> ===================================================================
> --- zope.interface/trunk/src/zope/interface/tests/test_interface.py	2011-05-26 11:27:21 UTC (rev 121818)
> +++ zope.interface/trunk/src/zope/interface/tests/test_interface.py	2011-05-26 16:10:57 UTC (rev 121819)
> @@ -348,8 +348,40 @@
>          # Old style classes don't have a '__class__' attribute
>          self.failUnlessRaises(AttributeError, I.providedBy, Bad)
>  
> +    def test_comparison_with_None(self):
> +        from zope.interface import Interface
>  
> +        class IEmpty(Interface):
> +            pass
>  
> +        self.failUnless(IEmpty < None)
> +        self.failUnless(IEmpty <= None)
> +        self.failIf(IEmpty == None)
> +        self.failUnless(IEmpty != None)
> +        self.failIf(IEmpty >= None)
> +        self.failIf(IEmpty > None)
> +
> +        self.failIf(None < IEmpty)
> +        self.failIf(None <= IEmpty)
> +        self.failIf(None == IEmpty)
> +        self.failUnless(None != IEmpty)
> +        self.failUnless(None >= IEmpty)
> +        self.failUnless(None > IEmpty)
> +
> +    def test_comparison_with_same_instance(self):
> +        from zope.interface import Interface
> +
> +        class IEmpty(Interface):
> +            pass
> +
> +        self.failIf(IEmpty < IEmpty)
> +        self.failUnless(IEmpty <= IEmpty)
> +        self.failUnless(IEmpty == IEmpty)
> +        self.failIf(IEmpty != IEmpty)
> +        self.failUnless(IEmpty >= IEmpty)
> +        self.failIf(IEmpty > IEmpty)
> +
> +
>  if sys.version_info >= (2, 4):
>  
>      def test_invariant_as_decorator():
> 
> Modified: zope.interface/trunk/src/zope/interface/tests/test_sorting.py
> ===================================================================
> --- zope.interface/trunk/src/zope/interface/tests/test_sorting.py	2011-05-26 11:27:21 UTC (rev 121818)
> +++ zope.interface/trunk/src/zope/interface/tests/test_sorting.py	2011-05-26 16:10:57 UTC (rev 121819)
> @@ -37,6 +37,14 @@
>          l = [I1, None, I3, I5, I6, I4, I2]
>          l.sort()
>          self.assertEqual(l, [I1, I2, I3, I4, I5, I6, None])
> +    
> +    def test_w_equal_names(self):
> +        # interfaces with equal names but different modules should sort by
> +        # module name
> +        from zope.interface.tests.m1 import I1 as m1_I1
> +        l = [I1, m1_I1]
> +        l.sort()
> +        self.assertEqual(l, [m1_I1, I1])
>  
>  def test_suite():
>      return TestSuite((


More information about the Zope-Dev mailing list