[pypy-svn] r78507 - in pypy/trunk/pypy: annotation objspace/std objspace/std/test rlib rpython/lltypesystem rpython/test
arigo at codespeak.net
arigo at codespeak.net
Sat Oct 30 12:18:12 CEST 2010
Author: arigo
Date: Sat Oct 30 12:18:10 2010
New Revision: 78507
Modified:
pypy/trunk/pypy/annotation/unaryop.py
pypy/trunk/pypy/objspace/std/dictmultiobject.py
pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py
pypy/trunk/pypy/rlib/objectmodel.py
pypy/trunk/pypy/rpython/lltypesystem/rdict.py
pypy/trunk/pypy/rpython/test/test_rdict.py
Log:
Add dict.popitem() as an RPython operation, which gives it a better implementation
(see POPITEMINDEX in rdict.py)
Modified: pypy/trunk/pypy/annotation/unaryop.py
==============================================================================
--- pypy/trunk/pypy/annotation/unaryop.py (original)
+++ pypy/trunk/pypy/annotation/unaryop.py Sat Oct 30 12:18:10 2010
@@ -434,6 +434,9 @@
def method_clear(dct):
pass
+ def method_popitem(dct):
+ return dct.getanyitem('items')
+
def _can_only_throw(dic, *ignore):
if dic1.dictdef.dictkey.custom_eq_hash:
return None # r_dict: can throw anything
Modified: pypy/trunk/pypy/objspace/std/dictmultiobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/dictmultiobject.py (original)
+++ pypy/trunk/pypy/objspace/std/dictmultiobject.py Sat Oct 30 12:18:10 2010
@@ -157,6 +157,16 @@
key = OPTIMIZED_BUILTINS[i]
return self.impl_getitem_str(key)
+ def impl_popitem(self):
+ # default implementation
+ space = self.space
+ iterator = self.impl_iter()
+ w_key, w_value = iterator.next()
+ if w_key is None:
+ raise KeyError
+ self.impl_delitem(w_key)
+ return w_key, w_value
+
# _________________________________________________________________
# fallback implementation methods
@@ -196,6 +206,9 @@
key = OPTIMIZED_BUILTINS[i]
return self.impl_fallback_getitem_str(key)
+ def impl_fallback_popitem(self):
+ return self.r_dict_content.popitem()
+
implementation_methods = [
("getitem", 1),
@@ -210,6 +223,7 @@
("keys", 0),
("clear", 0),
("get_builtin_indexed", 1),
+ ("popitem", 0),
]
@@ -779,16 +793,11 @@
return w_item
def dict_popitem__DictMulti(space, w_dict):
- # XXX should somehow use the same trick as CPython: saving the index
- # of the last popped item in the hash table, so that the next call to
- # popitem() can be more efficient, instead of always starting from the
- # beginning of the hash table.
- iterator = w_dict.iter()
- w_key, w_value = iterator.next()
- if w_key is None:
+ try:
+ w_key, w_value = w_dict.popitem()
+ except KeyError:
raise OperationError(space.w_KeyError,
space.wrap("popitem(): dictionary is empty"))
- w_dict.delitem(w_key)
return space.newtuple([w_key, w_value])
app = gateway.applevel('''
Modified: pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py (original)
+++ pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py Sat Oct 30 12:18:10 2010
@@ -238,7 +238,16 @@
assert len(d) == 0
assert (it!=it1) and (it1==(1,2) or it1==(3,4))
raises(KeyError, d.popitem)
-
+
+ def test_popitem_2(self):
+ class A(object):
+ pass
+ d = A().__dict__
+ d['x'] = 5
+ it1 = d.popitem()
+ assert it1 == ('x', 5)
+ raises(KeyError, d.popitem)
+
def test_setdefault(self):
d = {1:2, 3:4}
dd = d.copy()
Modified: pypy/trunk/pypy/rlib/objectmodel.py
==============================================================================
--- pypy/trunk/pypy/rlib/objectmodel.py (original)
+++ pypy/trunk/pypy/rlib/objectmodel.py Sat Oct 30 12:18:10 2010
@@ -475,6 +475,10 @@
def setdefault(self, key, default):
return self._dict.setdefault(_r_dictkey(self, key), default)
+ def popitem(self):
+ dk, value = self._dict.popitem()
+ return dk.key, value
+
def copy(self):
result = r_dict(self.key_eq, self.key_hash)
result.update(self)
Modified: pypy/trunk/pypy/rpython/lltypesystem/rdict.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/rdict.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/rdict.py Sat Oct 30 12:18:10 2010
@@ -306,6 +306,13 @@
hop.exception_cannot_occur()
return hop.gendirectcall(ll_clear, v_dict)
+ def rtype_method_popitem(self, hop):
+ v_dict, = hop.inputargs(self)
+ r_tuple = hop.r_result
+ cTUPLE = hop.inputconst(lltype.Void, r_tuple.lowleveltype)
+ hop.exception_is_here()
+ return hop.gendirectcall(ll_popitem, cTUPLE, v_dict)
+
class __extend__(pairtype(DictRepr, rmodel.Repr)):
def rtype_getitem((r_dict, r_key), hop):
@@ -465,6 +472,10 @@
i = ll_dict_lookup(d, key, d.keyhash(key))
if not d.entries.valid(i):
raise KeyError
+ _ll_dict_del(d, i)
+ll_dict_delitem.oopspec = 'dict.delitem(d, key)'
+
+def _ll_dict_del(d, i):
d.entries.mark_deleted(i)
d.num_items -= 1
# clear the key and the value if they are GC pointers
@@ -481,7 +492,6 @@
num_entries = len(d.entries)
if num_entries > DICT_INITSIZE and d.num_items < num_entries / 4:
ll_dict_resize(d)
-ll_dict_delitem.oopspec = 'dict.delitem(d, key)'
def ll_dict_resize(d):
old_entries = d.entries
@@ -810,3 +820,26 @@
i = ll_dict_lookup(d, key, d.keyhash(key))
return d.entries.valid(i)
ll_contains.oopspec = 'dict.contains(d, key)'
+
+POPITEMINDEX = lltype.Struct('PopItemIndex', ('nextindex', lltype.Signed))
+global_popitem_index = lltype.malloc(POPITEMINDEX, zero=True, immortal=True)
+
+def ll_popitem(ELEM, dic):
+ entries = dic.entries
+ dmask = len(entries) - 1
+ base = global_popitem_index.nextindex
+ counter = 0
+ while counter <= dmask:
+ i = (base + counter) & dmask
+ counter += 1
+ if entries.valid(i):
+ break
+ else:
+ raise KeyError
+ global_popitem_index.nextindex += counter
+ entry = entries[i]
+ r = lltype.malloc(ELEM.TO)
+ r.item0 = recast(ELEM.TO.item0, entry.key)
+ r.item1 = recast(ELEM.TO.item1, entry.value)
+ _ll_dict_del(dic, i)
+ return r
Modified: pypy/trunk/pypy/rpython/test/test_rdict.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rdict.py (original)
+++ pypy/trunk/pypy/rpython/test/test_rdict.py Sat Oct 30 12:18:10 2010
@@ -682,6 +682,26 @@
# if it does not crash, we are fine. It crashes if you forget the hash field.
self.interpret(func, [])
+ def test_dict_popitem(self):
+ def func():
+ d = {}
+ d[5] = 2
+ d[6] = 3
+ k1, v1 = d.popitem()
+ assert len(d) == 1
+ k2, v2 = d.popitem()
+ try:
+ d.popitem()
+ except KeyError:
+ pass
+ else:
+ assert 0, "should have raised KeyError"
+ assert len(d) == 0
+ return k1*1000 + v1*100 + k2*10 + v2
+
+ res = self.interpret(func, [])
+ assert res in [5263, 6352]
+
# ____________________________________________________________
def test_opt_nullkeymarker(self):
More information about the Pypy-commit
mailing list