Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r89936:5764bb3f54a2 Date: 2017-02-05 11:08 +0100 http://bitbucket.org/pypy/pypy/changeset/5764bb3f54a2/
Log: PyPy's simplified version of OrderedDict diff --git a/lib_pypy/_collections.py b/lib_pypy/_collections.py --- a/lib_pypy/_collections.py +++ b/lib_pypy/_collections.py @@ -439,3 +439,8 @@ return (type(self), (self.default_factory,), None, None, iter(self.items())) + +try: + from _pypy_collections import OrderedDict +except ImportError: + pass diff --git a/lib_pypy/_pypy_collections.py b/lib_pypy/_pypy_collections.py new file mode 100644 --- /dev/null +++ b/lib_pypy/_pypy_collections.py @@ -0,0 +1,69 @@ +from __pypy__ import reversed_dict, move_to_end +from reprlib import recursive_repr as _recursive_repr + +class OrderedDict(dict): + '''Dictionary that remembers insertion order. + + In PyPy all dicts are ordered anyway. This is mostly useful as a + placeholder to mean "this dict must be ordered even on CPython". + + Known difference: iterating over an OrderedDict which is being + concurrently modified raises RuntimeError in PyPy. In CPython + instead we get some behavior that appears reasonable in some + cases but is nonsensical in other cases. This is officially + forbidden by the CPython docs, so we forbid it explicitly for now. + ''' + + def __reversed__(self): + return reversed_dict(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + 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)) + + def move_to_end(self, key, last=True): + '''Move an existing element to the end (or beginning if last==False). + + Raises KeyError if the element does not exist. + When last=True, acts like a fast version of self[key]=self.pop(key). + + ''' + return move_to_end(self, key, last) + + @_recursive_repr() + def __repr__(self): + 'od.__repr__() <==> repr(od)' + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self.items())) + + def __reduce__(self): + 'Return state information for pickling' + inst_dict = vars(self).copy() + return self.__class__, (), inst_dict or None, None, iter(self.items()) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return dict.__eq__(self, other) and all(map(_eq, self, other)) + return dict.__eq__(self, other) + + __ne__ = object.__ne__ diff --git a/pypy/module/_collections/__init__.py b/pypy/module/_collections/__init__.py --- a/pypy/module/_collections/__init__.py +++ b/pypy/module/_collections/__init__.py @@ -25,3 +25,15 @@ space = self.space space.getattr(self, space.wrap('defaultdict')) # force importing space.delattr(self, space.wrap('__missing__')) + + def startup(self, space): + # OrderedDict is normally present, but in some cases the line + # "from __pypy__ import reversed_dict, move_to_end" from + # _pypy_collections.py raises + space.appexec([space.wrap(self)], """(mod): + try: + from _pypy_collections import OrderedDict + mod.OrderedDict = OrderedDict + except ImportError: + pass + """) diff --git a/pypy/module/_collections/test/test_ordereddict.py b/pypy/module/_collections/test/test_ordereddict.py new file mode 100644 --- /dev/null +++ b/pypy/module/_collections/test/test_ordereddict.py @@ -0,0 +1,8 @@ + +class AppTestBasic: + spaceconfig = dict(usemodules=['_collections']) + + def test_ordereddict_present(self): + from _collections import OrderedDict + assert issubclass(OrderedDict, dict) + assert hasattr(OrderedDict, 'move_to_end') _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit