[pypy-commit] pypy default: graft 6974fd5f5c47: Add __pypy__.move_to_end(), similar to
arigo
pypy.commits at gmail.com
Sun Feb 5 15:51:40 EST 2017
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r89959:e0ea69f0a7d8
Date: 2017-02-05 21:25 +0100
http://bitbucket.org/pypy/pypy/changeset/e0ea69f0a7d8/
Log: graft 6974fd5f5c47: 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
@@ -80,6 +80,7 @@
'newdict' : 'interp_dict.newdict',
'reversed_dict' : 'interp_dict.reversed_dict',
'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
'specialized_zip_2_lists' : 'interp_magic.specialized_zip_2_lists',
'set_debug' : 'interp_magic.set_debug',
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
@@ -57,3 +57,14 @@
if not isinstance(w_obj, W_DictMultiObject):
raise OperationError(space.w_TypeError, space.w_None)
return w_obj.nondescr_delitem_if_value_is(space, w_key, w_value)
+
+ 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 used in Python 3.x to implement OrderedDict.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
@@ -307,6 +307,37 @@
"""D.has_key(k) -> True if D has a key k, else False"""
return space.newbool(self.getitem(w_key) is not None)
+ 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()
@@ -578,7 +609,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)
@@ -849,6 +882,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):
@@ -1013,6 +1049,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
@@ -285,6 +285,29 @@
__pypy__.delitem_if_value_is(d, 2, x3)
assert d == {3: x3}
+ 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 = d.keys()
More information about the pypy-commit
mailing list