Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r90061:63aaa4e32c86
Date: 2017-02-12 09:24 +0100
http://bitbucket.org/pypy/pypy/changeset/63aaa4e32c86/

Log:    Move OrderedDict.popitem() to interp-level as an attempt to increase
        its multi-threaded resistence

diff --git a/lib-python/2.7/collections.py b/lib-python/2.7/collections.py
--- a/lib-python/2.7/collections.py
+++ b/lib-python/2.7/collections.py
@@ -33,6 +33,10 @@
     from __pypy__ import reversed_dict as _reversed_dict
 except ImportError:
     _reversed_dict = None     # don't have ordered dicts
+try:
+    from __pypy__ import dict_popitem_first as _dict_popitem_first
+except ImportError:
+    _dict_popitem_first = None
 
 try:
     from thread import get_ident as _get_ident
@@ -44,6 +48,17 @@
 ### OrderedDict
 
################################################################################
 
+if _dict_popitem_first is None:
+    def _dict_popitem_first(self):
+        it = dict.iteritems(self)
+        try:
+            k, v = it.next()
+        except StopIteration:
+            raise KeyError('dictionary is empty')
+        dict.__delitem__(self, k)
+        return (k, v)
+
+
 class OrderedDict(dict):
     '''Dictionary that remembers insertion order.
 
@@ -68,12 +83,7 @@
         if last:
             return dict.popitem(self)
         else:
-            it = dict.__iter__(self)
-            try:
-                k = it.next()
-            except StopIteration:
-                raise KeyError('dictionary is empty')
-            return (k, self.pop(k))
+            return _dict_popitem_first(self)
 
     def __repr__(self, _repr_running={}):
         'od.__repr__() <==> repr(od)'
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -79,6 +79,7 @@
         'add_memory_pressure'       : 'interp_magic.add_memory_pressure',
         'newdict'                   : 'interp_dict.newdict',
         'reversed_dict'             : 'interp_dict.reversed_dict',
+        'dict_popitem_first'        : 'interp_dict.dict_popitem_first',
         'delitem_if_value_is'       : 'interp_dict.delitem_if_value_is',
         'move_to_end'               : 'interp_dict.move_to_end',
         'strategy'                  : 'interp_magic.strategy',  # dict,set,list
diff --git a/pypy/module/__pypy__/interp_dict.py 
b/pypy/module/__pypy__/interp_dict.py
--- a/pypy/module/__pypy__/interp_dict.py
+++ b/pypy/module/__pypy__/interp_dict.py
@@ -45,6 +45,14 @@
         raise OperationError(space.w_TypeError, space.w_None)
     return w_obj.nondescr_reversed_dict(space)
 
+def dict_popitem_first(space, w_obj):
+    """Interp-level implementation of OrderedDict.popitem(last=False).
+    """
+    from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+    if not isinstance(w_obj, W_DictMultiObject):
+        raise OperationError(space.w_TypeError, space.w_None)
+    return w_obj.nondescr_popitem_first(space)
+
 def delitem_if_value_is(space, w_obj, w_key, w_value):
     """Atomic equivalent to: 'if dict.get(key) is value: del dict[key]'.
 
diff --git a/pypy/objspace/std/dictmultiobject.py 
b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -338,6 +338,15 @@
                     for i in range(len(keys_w)):
                         self.setitem(keys_w[i], values_w[i])
 
+    def nondescr_popitem_first(self, space):
+        """Not exposed directly to app-level, but via __pypy__.popitem_first().
+        """
+        w_key, w_value = self.iteritems().next_item()
+        if w_key is None:
+            raise oefmt(space.w_KeyError, "popitem(): dictionary is empty")
+        self.delitem(w_key)
+        return space.newtuple([w_key, w_value])
+
     def descr_clear(self, space):
         """D.clear() -> None.  Remove all items from D."""
         self.clear()
@@ -604,8 +613,7 @@
 
     has_iterreversed = False
     has_move_to_end = False
-    # no 'getiterreversed' and no 'move_to_end': no default
-    # implementation available
+    # ^^^ no default implementation available for these methods
 
     def rev_update1_dict_dict(self, w_dict, w_updatedict):
         iteritems = self.iteritems(w_dict)
diff --git a/pypy/objspace/std/test/test_dictmultiobject.py 
b/pypy/objspace/std/test/test_dictmultiobject.py
--- a/pypy/objspace/std/test/test_dictmultiobject.py
+++ b/pypy/objspace/std/test/test_dictmultiobject.py
@@ -267,10 +267,22 @@
         d = {1: 2, 3: 4, 5: 6}
         it = __pypy__.reversed_dict(d)
         key = it.next()
-        assert key in [1, 3, 5]
+        assert key in [1, 3, 5]   # on CPython, dicts are not ordered
         del d[key]
         raises(RuntimeError, it.next)
 
+    def test_dict_popitem_first(self):
+        import __pypy__
+        d = {"a": 5}
+        assert __pypy__.dict_popitem_first(d) == ("a", 5)
+        raises(KeyError, __pypy__.dict_popitem_first, d)
+
+        def kwdict(**k):
+            return k
+        d = kwdict(a=55)
+        assert __pypy__.dict_popitem_first(d) == ("a", 55)
+        raises(KeyError, __pypy__.dict_popitem_first, d)
+
     def test_delitem_if_value_is(self):
         import __pypy__
         class X:
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to