[pypy-commit] pypy stmgc-c8: Add _weakref.weakkeyiddict(), a generally useful weak-keyed-dict with
arigo
noreply at buildbot.pypy.org
Tue Jun 23 12:02:17 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch: stmgc-c8
Changeset: r78252:f5af62298413
Date: 2015-06-23 11:41 +0200
http://bitbucket.org/pypy/pypy/changeset/f5af62298413/
Log: Add _weakref.weakkeyiddict(), a generally useful weak-keyed-dict
with identity instead of equality. It is useful to add more
attributes to existing objects, for example, without attaching them
to the objects themselves.
diff --git a/pypy/module/_weakref/__init__.py b/pypy/module/_weakref/__init__.py
--- a/pypy/module/_weakref/__init__.py
+++ b/pypy/module/_weakref/__init__.py
@@ -10,5 +10,8 @@
'ReferenceType': 'interp__weakref.W_Weakref',
'ProxyType': 'interp__weakref.W_Proxy',
'CallableProxyType': 'interp__weakref.W_CallableProxy',
- 'proxy': 'interp__weakref.proxy'
+ 'proxy': 'interp__weakref.proxy',
+
+ # PyPy extension
+ 'weakkeyiddict': 'weakkeyiddict.W_WeakKeyIdDict',
}
diff --git a/pypy/module/_weakref/test/test_weakkeyiddict.py b/pypy/module/_weakref/test/test_weakkeyiddict.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_weakref/test/test_weakkeyiddict.py
@@ -0,0 +1,59 @@
+class AppTestWeakKeyIdDict(object):
+ spaceconfig = dict(usemodules=('_weakref',))
+
+ def test_simple(self):
+ import _weakref
+ class A(object):
+ pass
+ d = _weakref.weakkeyiddict()
+ a1 = A()
+ a2 = A()
+ d[a1] = 11
+ d[a2] = 22.5
+ assert d[a1] == 11
+ assert d[a2] == 22.5
+ assert d.get(a2, 5) == 22.5
+ del d[a2]
+ raises(KeyError, "d[a2]")
+ assert d.get(a2, 5) == 5
+ assert a1 in d
+ assert a2 not in d
+ assert d.setdefault(a1, 82) == 11
+ assert d[a1] == 11
+ assert d.setdefault(a2, 83) == 83
+ assert d[a2] == 83
+
+ def test_nonhashable_key(self):
+ import _weakref
+ d = _weakref.weakkeyiddict()
+ lst = []
+ lst2 = []
+ d[lst] = 84
+ assert lst in d
+ assert lst2 not in d
+ assert d.pop(lst) == 84
+ assert lst not in d
+ assert d.pop(lst, 85) == 85
+
+ def test_collect(self):
+ import _weakref
+ gone = []
+ class A(object):
+ def __del__(self):
+ gone.append(True)
+ d = _weakref.weakkeyiddict()
+ a1 = A()
+ a2 = A()
+ d[a1] = -42
+ d[a2] = 83
+ assert gone == []
+ #
+ del a1
+ tries = 0
+ while not gone:
+ tries += 1
+ if tries > 5:
+ raise AssertionError("a1 doesn't disappear")
+ gc.collect()
+ assert gone == [True]
+ assert d[a2] == 83
diff --git a/pypy/module/_weakref/weakkeyiddict.py b/pypy/module/_weakref/weakkeyiddict.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_weakref/weakkeyiddict.py
@@ -0,0 +1,82 @@
+#
+# This is _weakref.weakkeyiddict(), a generally useful weak-keyed-dict
+# with identity instead of equality. It is useful to add more
+# attributes to existing objects, for example, without attaching
+# them to the objects themselves. It can be emulated in pure Python,
+# of course, but given that we already have a class in rlib.rweakref
+# that is doing exactly that in a cheap way, it is far more efficient
+# this way.
+#
+
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
+
+from rpython.rlib.rweakref import RWeakKeyDictionary
+
+
+class W_WeakKeyIdDict(W_Root):
+ def __init__(self):
+ self.data = RWeakKeyDictionary(W_Root, W_Root)
+
+ def getitem_w(self, space, w_key):
+ w_value = self.data.get(w_key)
+ if w_value is None:
+ space.raise_key_error(w_key)
+ return w_value
+
+ def setitem_w(self, space, w_key, w_value):
+ self.data.set(w_key, w_value)
+
+ def delitem_w(self, space, w_key):
+ if self.data.get(w_key) is None:
+ space.raise_key_error(w_key)
+ self.data.set(w_key, None)
+
+ def contains_w(self, space, w_key):
+ return space.wrap(self.data.get(w_key) is not None)
+
+ @unwrap_spec(w_default=WrappedDefault(None))
+ def get_w(self, space, w_key, w_default):
+ w_value = self.data.get(w_key)
+ if w_value is not None:
+ return w_value
+ else:
+ return w_default
+
+ def pop_w(self, space, w_key, w_default=None):
+ w_value = self.data.get(w_key)
+ if w_value is not None:
+ self.data.set(w_key, None)
+ return w_value
+ elif w_default is not None:
+ return w_default
+ else:
+ space.raise_key_error(w_key)
+
+ @unwrap_spec(w_default=WrappedDefault(None))
+ def setdefault_w(self, space, w_key, w_default):
+ w_value = self.data.get(w_key)
+ if w_value is not None:
+ return w_value
+ else:
+ self.data.set(w_key, w_default)
+ return w_default
+
+
+def W_WeakKeyIdDict___new__(space, w_subtype):
+ r = space.allocate_instance(W_WeakKeyIdDict, w_subtype)
+ r.__init__()
+ return space.wrap(r)
+
+W_WeakKeyIdDict.typedef = TypeDef(
+ '_weakref.weakkeyiddict',
+ __new__ = interp2app(W_WeakKeyIdDict___new__),
+ __getitem__ = interp2app(W_WeakKeyIdDict.getitem_w),
+ __setitem__ = interp2app(W_WeakKeyIdDict.setitem_w),
+ __delitem__ = interp2app(W_WeakKeyIdDict.delitem_w),
+ __contains__ = interp2app(W_WeakKeyIdDict.contains_w),
+ get = interp2app(W_WeakKeyIdDict.get_w),
+ pop = interp2app(W_WeakKeyIdDict.pop_w),
+ setdefault = interp2app(W_WeakKeyIdDict.setdefault_w),
+)
More information about the pypy-commit
mailing list