Author: Armin Rigo <ar...@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) + +@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() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit