[pypy-commit] pypy identity-dict-strategy: add a way to check whether a type has custom versions of __eq__, __hash__ or __cmp__

antocuni noreply at buildbot.pypy.org
Tue Jul 19 18:44:18 CEST 2011


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: identity-dict-strategy
Changeset: r45740:a2ad83608b4b
Date: 2011-07-19 17:02 +0200
http://bitbucket.org/pypy/pypy/changeset/a2ad83608b4b/

Log:	add a way to check whether a type has custom versions of __eq__,
	__hash__ or __cmp__

diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -327,6 +327,9 @@
         BoolOption("mutable_builtintypes",
                    "Allow the changing of builtin types", default=False,
                    requires=[("objspace.std.builtinshortcut", True)]),
+        BoolOption("trackcomparebyidentity",
+                   "track types that override __hash__, __eq__ or __cmp__",
+                   default=True),
      ]),
 ])
 
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -28,6 +28,13 @@
     return w_delattr
 object_delattr._annspecialcase_ = 'specialize:memo'
 
+def object_hash(space):
+    "Utility that returns the app-level descriptor object.__hash__."
+    w_src, w_hash = space.lookup_in_type_where(space.w_object,
+                                                  '__hash__')
+    return w_hash
+object_hash._annspecialcase_ = 'specialize:memo'
+
 def raiseattrerror(space, w_obj, name, w_descr=None):
     w_type = space.type(w_obj)
     typename = w_type.getname(space)
diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py
--- a/pypy/objspace/std/test/test_typeobject.py
+++ b/pypy/objspace/std/test/test_typeobject.py
@@ -1199,3 +1199,37 @@
                 return x + 1
         a = A()
         assert a.f(1) == 2
+
+
+class AppTestTrackCompareByIdentity:
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(
+                        **{"objspace.std.trackcomparebyidentity": True})
+
+        def compares_by_identity(space, w_cls):
+            return space.wrap(w_cls.compares_by_identity())
+
+        cls.w_compares_by_identity = cls.space.wrap(interp2app(compares_by_identity))
+
+    def test_compares_by_identity(self):
+        class Plain(object):
+            pass
+
+        class CustomEq(object):
+            def __eq__(self, other):
+                return True
+
+        class CustomCmp (object):
+            def __cmp__(self, other):
+                return 0
+
+        class CustomHash(object):
+            def __hash__(self):
+                return 0
+
+
+        assert self.compares_by_identity(Plain)
+        assert not self.compares_by_identity(CustomEq)
+        assert not self.compares_by_identity(CustomCmp)
+        assert not self.compares_by_identity(CustomHash)
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -102,6 +102,10 @@
     # (False is a conservative default, fixed during real usage)
     uses_object_getattribute = False
 
+    # for config.objspace.std.trackcomparebyidentity
+    # (True is a conservative default, fixed during real usage)
+    overrides_hash_eq_or_cmp = True
+
     # used to cache the type __new__ function if it comes from a builtin type
     # != 'type', in that case call__Type will also assumes the result
     # of the __new__ is an instance of the type
@@ -207,6 +211,21 @@
     def has_object_getattribute(w_self):
         return w_self.getattribute_if_not_from_object() is None
 
+    def compares_by_identity(w_self):
+        from pypy.objspace.descroperation import object_hash
+        track = w_self.space.config.objspace.std.trackcomparebyidentity
+        if not track:
+            return False # conservative
+        #
+        if not w_self.overrides_hash_eq_or_cmp:
+            return True # fast path
+        #
+        default_hash = object_hash(w_self.space)
+        w_self.overrides_hash_eq_or_cmp = (w_self.lookup('__eq__') or
+                                           w_self.lookup('__cmp__') or
+                                           w_self.lookup('__hash__') is not default_hash)
+        return not w_self.overrides_hash_eq_or_cmp
+
     def ready(w_self):
         for w_base in w_self.bases_w:
             if not isinstance(w_base, W_TypeObject):


More information about the pypy-commit mailing list