Commits

Antonio Cuni committed a2ad836

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

  • Participants
  • Parent commits 4de5eef
  • Branches identity-dict-strategy

Comments (0)

Files changed (4)

File pypy/config/pypyoption.py

         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),
      ]),
 ])
 

File pypy/objspace/descroperation.py

     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)

File pypy/objspace/std/test/test_typeobject.py

                 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)

File pypy/objspace/std/typeobject.py

     # (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
     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):