[pypy-commit] pypy py3.5: Add __pypy__.move_to_end(), similar to __pypy__.reversed_dict().
arigo
pypy.commits at gmail.com
Sun Feb 5 05:08:43 EST 2017
Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r89935:6974fd5f5c47
Date: 2017-02-05 10:57 +0100
http://bitbucket.org/pypy/pypy/changeset/6974fd5f5c47/
Log: Add __pypy__.move_to_end(), similar to __pypy__.reversed_dict().
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
@@ -78,6 +78,7 @@
'add_memory_pressure' : 'interp_magic.add_memory_pressure',
'newdict' : 'interp_dict.newdict',
'reversed_dict' : 'interp_dict.reversed_dict',
+ 'move_to_end' : 'interp_dict.move_to_end',
'strategy' : 'interp_magic.strategy', # dict,set,list
'set_debug' : 'interp_magic.set_debug',
'locals_to_fast' : 'interp_magic.locals_to_fast',
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
@@ -44,3 +44,18 @@
if not isinstance(w_obj, W_DictMultiObject):
raise OperationError(space.w_TypeError, space.w_None)
return w_obj.nondescr_reversed_dict(space)
+
+ at unwrap_spec(last=bool)
+def move_to_end(space, w_obj, w_key, last=True):
+ """Move the key in a dictionary object into the first or last position.
+
+ This is a __pypy__ function instead of being simply done by calling
+ dict.move_to_end(), for CPython compatibility: dictionaries are only
+ ordered on PyPy. You should use the collections.OrderedDict class for
+ cases where ordering is important. That class implements the
+ move_to_end() method by calling __pypy__.move_to_end().
+ """
+ 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_move_to_end(space, w_key, last)
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
@@ -232,6 +232,37 @@
w_keys = self.w_keys()
return space.call_method(w_keys, '__reversed__')
+ def nondescr_move_to_end(self, space, w_key, last_flag):
+ """Not exposed directly to app-level, but via __pypy__.move_to_end().
+ """
+ strategy = self.get_strategy()
+ if strategy.has_move_to_end:
+ strategy.move_to_end(self, w_key, last_flag)
+ else:
+ # fall-back
+ w_value = self.getitem(w_key)
+ if w_value is None:
+ space.raise_key_error(w_key)
+ else:
+ self.delitem(w_key)
+ if last_flag:
+ self.setitem(w_key, w_value)
+ else:
+ # *very slow* fall-back
+ keys_w = []
+ values_w = []
+ iteratorimplementation = self.iteritems()
+ while True:
+ w_k, w_v = iteratorimplementation.next_item()
+ if w_k is None:
+ break
+ keys_w.append(w_k)
+ values_w.append(w_v)
+ self.clear()
+ self.setitem(w_key, w_value)
+ for i in range(len(keys_w)):
+ self.setitem(keys_w[i], values_w[i])
+
def descr_clear(self, space):
"""D.clear() -> None. Remove all items from D."""
self.clear()
@@ -499,7 +530,9 @@
raise NotImplementedError
has_iterreversed = False
- # no 'getiterreversed': no default implementation available
+ has_move_to_end = False
+ # no 'getiterreversed' and no 'move_to_end': no default
+ # implementation available
def rev_update1_dict_dict(self, w_dict, w_updatedict):
iteritems = self.iteritems(w_dict)
@@ -783,6 +816,9 @@
dictimpl.iterreversed = iterreversed
dictimpl.has_iterreversed = True
+ if hasattr(dictimpl, 'move_to_end'):
+ dictimpl.has_move_to_end = True
+
@jit.look_inside_iff(lambda self, w_dict, w_updatedict:
w_dict_unrolling_heuristic(w_dict))
def rev_update1_dict_dict(self, w_dict, w_updatedict):
@@ -962,6 +998,15 @@
def getiterreversed(self, w_dict):
return objectmodel.reversed_dict(self.unerase(w_dict.dstorage))
+ def move_to_end(self, w_dict, w_key, last_flag):
+ if self.is_correct_type(w_key):
+ d = self.unerase(w_dict.dstorage)
+ key = self.unwrap(w_key)
+ objectmodel.move_to_end(d, key, last_flag)
+ else:
+ self.switch_to_object_strategy(w_dict)
+ w_dict.nondescr_move_to_end(w_dict.space, w_key, last_flag)
+
def prepare_update(self, w_dict, num_extra):
objectmodel.prepare_dict_update(self.unerase(w_dict.dstorage),
num_extra)
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
@@ -270,6 +270,29 @@
del d[key]
raises(RuntimeError, next, it)
+ def test_move_to_end(self):
+ import __pypy__
+ raises(KeyError, __pypy__.move_to_end, {}, 'foo')
+ raises(KeyError, __pypy__.move_to_end, {}, 'foo', last=True)
+ raises(KeyError, __pypy__.move_to_end, {}, 'foo', last=False)
+ def kwdict(**k):
+ return k
+ for last in [False, True]:
+ for d, key in [({1: 2, 3: 4, 5: 6}, 3),
+ ({"a": 5, "b": 2, "c": 6}, "b"),
+ (kwdict(d=7, e=8, f=9), "e")]:
+ other_keys = [k for k in d if k != key]
+ __pypy__.move_to_end(d, key, last=last)
+ if not self.on_pypy:
+ # when running tests on CPython, the underlying
+ # dicts are not ordered. We don't get here if
+ # we're running tests on PyPy or with -A.
+ assert set(d.keys()) == set(other_keys + [key])
+ elif last:
+ assert list(d) == other_keys + [key]
+ else:
+ assert list(d) == [key] + other_keys
+
def test_keys(self):
d = {1: 2, 3: 4}
kys = list(d.keys())
More information about the pypy-commit
mailing list