[pypy-commit] pypy identity-dict-strategy: implement a global version counter to track changes to classes that changed their compares_by_identity() status
antocuni
noreply at buildbot.pypy.org
Tue Jul 19 18:44:21 CEST 2011
Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: identity-dict-strategy
Changeset: r45743:923f2db3961e
Date: 2011-07-19 18:19 +0200
http://bitbucket.org/pypy/pypy/changeset/923f2db3961e/
Log: implement a global version counter to track changes to classes that
changed their compares_by_identity() status
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -32,18 +32,19 @@
from pypy.objspace.std.smallintobject import W_SmallIntObject
from pypy.objspace.std.stringobject import W_StringObject
from pypy.objspace.std.tupleobject import W_TupleObject
-from pypy.objspace.std.typeobject import W_TypeObject
+from pypy.objspace.std.typeobject import W_TypeObject, VersionTag
# types
from pypy.objspace.std.inttype import wrapint
from pypy.objspace.std.stringtype import wrapstr
from pypy.objspace.std.unicodetype import wrapunicode
-
class StdObjSpace(ObjSpace, DescrOperation):
"""The standard object space, implementing a general-purpose object
library in Restricted Python."""
+ compares_by_identity_version = None
+
def initialize(self):
"NOT_RPYTHON: only for initializing the space."
# setup all the object types and implementations
@@ -80,6 +81,15 @@
# the type of old-style classes
self.w_classobj = self.builtin.get('__metaclass__')
+ # a global version counter to track live instances which "compare by
+ # identity" (i.e., whose __eq__, __cmp__ and __hash__ are the default
+ # ones). The idea is to track only classes for which we checked the
+ # compares_by_identity() status at least once: we increment the
+ # version if its status might change, e.g. because we set one of those
+ # attributes. The actual work is done by W_TypeObject.mutated()
+ if self.config.objspace.std.trackcomparebyidentity:
+ self.compares_by_identity_version = VersionTag()
+
# final setup
self.setup_builtin_modules()
# Adding transparent proxy call
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
@@ -1209,8 +1209,13 @@
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))
- cls.w_compares_by_identity = cls.space.wrap(interp2app(compares_by_identity))
+ versions = {}
+ def get_version(space):
+ v = versions.setdefault(space.compares_by_identity_version, len(versions))
+ return space.wrap(v)
+ cls.w_get_version = cls.space.wrap(interp2app(get_version))
def test_compares_by_identity(self):
class Plain(object):
@@ -1234,14 +1239,43 @@
assert not self.compares_by_identity(CustomHash)
def test_modify_class(self):
- def foo(self, *args):
- pass
-
class X(object):
pass
assert self.compares_by_identity(X)
- X.__eq__ = foo
+ X.__eq__ = lambda x: None
assert not self.compares_by_identity(X)
del X.__eq__
assert self.compares_by_identity(X)
+
+ def test_versioning(self):
+ class X(object):
+ pass
+
+ class Y(object):
+ def __eq__(self, other):
+ pass
+
+ assert self.get_version() == 0
+ X.__eq__ = lambda x: None
+ # modifying a class for which we never checked the
+ # compares_by_identity() status does not increase the version
+ assert self.get_version() == 0
+
+ del X.__eq__
+ assert self.compares_by_identity(X) # now we check it
+ X.__add__ = lambda x: None
+ assert self.get_version() == 0 # innocent change
+ #
+ X.__eq__ = lambda x: None
+ assert self.get_version() == 1 # BUMP!
+
+ del X.__eq__
+ assert self.compares_by_identity(X)
+ X.__bases__ = (object,)
+ assert self.get_version() == 2 # BUMP!
+
+ # modifying a class which is already "bad" does not increase the
+ # version
+ Y.__eq__ = lambda x: None
+ assert self.get_version() == 2
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
@@ -150,7 +150,12 @@
else:
w_self.terminator = NoDictTerminator(space, w_self)
- def mutated(w_self):
+ def mutated(w_self, key):
+ """
+ The type is being mutated. key is either the string containing the
+ specific attribute which is being deleted/set or None to indicate a
+ generic mutation.
+ """
space = w_self.space
assert w_self.is_heaptype() or space.config.objspace.std.mutable_builtintypes
if (not space.config.objspace.std.withtypeversion and
@@ -164,8 +169,13 @@
# ^^^ conservative default, fixed during real usage
if space.config.objspace.std.trackcomparebyidentity:
+ did_compare_by_identity = not w_self.overrides_hash_eq_or_cmp
+ if did_compare_by_identity and (key is None or
+ key == '__eq__' or
+ key == '__cmp__' or
+ key == '__hash__'):
w_self.overrides_hash_eq_or_cmp = True
- # ^^^ conservative default, fixed during real usage
+ w_self.space.compares_by_identity_version = VersionTag()
if space.config.objspace.std.newshortcut:
w_self.w_bltin_new = None
@@ -177,7 +187,7 @@
subclasses_w = w_self.get_subclasses()
for w_subclass in subclasses_w:
assert isinstance(w_subclass, W_TypeObject)
- w_subclass.mutated()
+ w_subclass.mutated(key)
def version_tag(w_self):
if (not we_are_jitted() or w_self.is_heaptype() or
@@ -295,7 +305,7 @@
w_curr.w_value = w_value
return True
w_value = TypeCell(w_value)
- w_self.mutated()
+ w_self.mutated(name)
w_self.dict_w[name] = w_value
return True
@@ -312,7 +322,7 @@
except KeyError:
return False
else:
- w_self.mutated()
+ w_self.mutated(key)
return True
def lookup(w_self, name):
diff --git a/pypy/objspace/std/typetype.py b/pypy/objspace/std/typetype.py
--- a/pypy/objspace/std/typetype.py
+++ b/pypy/objspace/std/typetype.py
@@ -141,7 +141,7 @@
w_oldbestbase.getname(space))
# invalidate the version_tag of all the current subclasses
- w_type.mutated()
+ w_type.mutated(None)
# now we can go ahead and change 'w_type.bases_w'
saved_bases_w = w_type.bases_w
More information about the pypy-commit
mailing list