Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit