[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