[Checkins] SVN: zope.interface/trunk/ Work around buggy behavior in some subclasses of `InterfaceClass``

Tres Seaver tseaver at palladion.com
Thu Aug 11 15:49:19 EDT 2011


Log message for revision 122546:
  Work around buggy behavior in some subclasses of `InterfaceClass``
  
  Some sublcasses invoke '__hash__' before initializing '__module__' and
  '__name__'.  The workaround returns a fixed constant hash in such cases, and
  issues a UserWarning.
  
  Addresses LP #811792.
  

Changed:
  U   zope.interface/trunk/CHANGES.txt
  U   zope.interface/trunk/src/zope/interface/interface.py
  A   zope.interface/trunk/src/zope/interface/tests/ifoo_other.py
  U   zope.interface/trunk/src/zope/interface/tests/test_interface.py

-=-
Modified: zope.interface/trunk/CHANGES.txt
===================================================================
--- zope.interface/trunk/CHANGES.txt	2011-08-11 19:24:13 UTC (rev 122545)
+++ zope.interface/trunk/CHANGES.txt	2011-08-11 19:49:19 UTC (rev 122546)
@@ -4,6 +4,11 @@
 3.6.5 (unreleased)
 ------------------
 
+- LP #811792:  work around buggy behavior in some subclasses of
+  ``zope.interface.interface.InterfaceClass``, which invoke ``__hash__``
+  before initializing ``__module__`` and ``__name__``.  The workaround
+  returns a fixed constant hash in such cases, and issues a ``UserWarning``.
+
 - LP #804832:  Under PyPy, ``zope.interface`` should not build its C
   extension.  Also, prevent attempting to build it under Jython.
 

Modified: zope.interface/trunk/src/zope/interface/interface.py
===================================================================
--- zope.interface/trunk/src/zope/interface/interface.py	2011-08-11 19:24:13 UTC (rev 122545)
+++ zope.interface/trunk/src/zope/interface/interface.py	2011-08-11 19:49:19 UTC (rev 122546)
@@ -17,6 +17,7 @@
 
 import sys
 from types import FunctionType
+import warnings
 import weakref
 
 from zope.interface.exceptions import Invalid
@@ -682,6 +683,10 @@
         return (n1 > n2) - (n1 < n2)
 
     def __hash__(self):
+        d = self.__dict__
+        if '__module__' not in d or '__name__' not in d:
+            warnings.warn('Hashing uninitialized InterfaceClass instance')
+            return 1
         return hash((self.__name__, self.__module__))
 
     def __eq__(self, other):

Copied: zope.interface/trunk/src/zope/interface/tests/ifoo_other.py (from rev 122541, zope.interface/trunk/src/zope/interface/tests/ifoo.py)
===================================================================
--- zope.interface/trunk/src/zope/interface/tests/ifoo_other.py	                        (rev 0)
+++ zope.interface/trunk/src/zope/interface/tests/ifoo_other.py	2011-08-11 19:49:19 UTC (rev 122546)
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""IFoo test module
+"""
+from zope.interface import Interface
+
+class IFoo(Interface):
+    """
+        Dummy interface for unit tests.
+    """
+
+    def bar(baz):
+        """
+            Just a note.
+        """

Modified: zope.interface/trunk/src/zope/interface/tests/test_interface.py
===================================================================
--- zope.interface/trunk/src/zope/interface/tests/test_interface.py	2011-08-11 19:24:13 UTC (rev 122545)
+++ zope.interface/trunk/src/zope/interface/tests/test_interface.py	2011-08-11 19:49:19 UTC (rev 122546)
@@ -387,16 +387,37 @@
         self.failUnless(IEmpty >= IEmpty)
         self.failIf(IEmpty > IEmpty)
 
-    def test_hash(self):
-        from zope.interface import Interface
+    def test_comparison_with_same_named_instance_in_other_module(self):
+        from zope.interface.tests.ifoo import IFoo as IFoo1
+        from zope.interface.tests.ifoo_other import IFoo as IFoo2
 
-        class IEmpty(Interface):
-            pass
+        self.failUnless(IFoo1 < IFoo2)
+        self.failUnless(IFoo1 <= IFoo2)
+        self.failIf(IFoo1 == IFoo2)
+        self.failUnless(IFoo1 != IFoo2)
+        self.failIf(IFoo1 >= IFoo2)
+        self.failIf(IFoo1 > IFoo2)
 
-        self.assertEqual(hash(IEmpty),
-                         hash((IEmpty.__name__, IEmpty.__module__)))
+    def test_hash_normal(self):
+        from zope.interface.tests.ifoo import IFoo
+        self.assertEqual(hash(IFoo),
+                         hash((('IFoo', 'zope.interface.tests.ifoo'))))
 
+    def test_hash_missing_required_attrs(self):
+        from warnings import catch_warnings
+        from zope.interface.interface import InterfaceClass
+        class Derived(InterfaceClass):
+            def __init__(self):
+                pass # Don't call base class.
+        derived = Derived()
+        with catch_warnings(record=True) as warned:
+            self.assertEqual(hash(derived), 1)
+            self.assertEqual(len(warned), 1)
+            self.failUnless(warned[0].category is UserWarning)
+            self.assertEqual(str(warned[0].message),
+                             'Hashing uninitialized InterfaceClass instance')
 
+
 if sys.version_info >= (2, 4):
 
     def test_invariant_as_decorator():



More information about the checkins mailing list