Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: buffer-cleanup
Changeset: r89987:062ca5e63c28
Date: 2017-02-05 20:42 +0000
http://bitbucket.org/pypy/pypy/changeset/062ca5e63c28/

Log:    hg merge py3.5

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,70 @@
+from __pypy__ import reversed_dict, move_to_end
+from _operator import eq as _eq
+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/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py
--- a/lib_pypy/cffi/api.py
+++ b/lib_pypy/cffi/api.py
@@ -93,6 +93,7 @@
             # ctypes backend: attach these constants to the instance
             self.NULL = self.cast(self.BVoidP, 0)
             self.CData, self.CType = backend._get_types()
+        self.buffer = backend.buffer
 
     def cdef(self, csource, override=False, packed=False):
         """Parse the given C source.  This registers all declared functions,
@@ -316,18 +317,18 @@
         """
         return self._backend.unpack(cdata, length)
 
-    def buffer(self, cdata, size=-1):
-        """Return a read-write buffer object that references the raw C data
-        pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
-        an array.  Can be passed to functions expecting a buffer, or directly
-        manipulated with:
-
-            buf[:]          get a copy of it in a regular string, or
-            buf[idx]        as a single character
-            buf[:] = ...
-            buf[idx] = ...  change the content
-        """
-        return self._backend.buffer(cdata, size)
+   #def buffer(self, cdata, size=-1):
+   #    """Return a read-write buffer object that references the raw C data
+   #    pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
+   #    an array.  Can be passed to functions expecting a buffer, or directly
+   #    manipulated with:
+   #
+   #        buf[:]          get a copy of it in a regular string, or
+   #        buf[idx]        as a single character
+   #        buf[:] = ...
+   #        buf[idx] = ...  change the content
+   #    """
+   #    note that 'buffer' is a type, set on this instance by __init__
 
     def from_buffer(self, python_buffer):
         """Return a <cdata 'char[]'> that points to the data of the
@@ -593,11 +594,15 @@
             ensure('extra_link_args', '/MANIFEST')
 
     def set_source(self, module_name, source, source_extension='.c', **kwds):
+        import os
         if hasattr(self, '_assigned_source'):
             raise ValueError("set_source() cannot be called several times "
                              "per ffi object")
         if not isinstance(module_name, basestring):
             raise TypeError("'module_name' must be a string")
+        if os.sep in module_name or (os.altsep and os.altsep in module_name):
+            raise ValueError("'module_name' must not contain '/': use a dotted 
"
+                             "name to make a 'package.module' location")
         self._assigned_source = (str(module_name), source,
                                  source_extension, kwds)
 
diff --git a/pypy/doc/stackless.rst b/pypy/doc/stackless.rst
--- a/pypy/doc/stackless.rst
+++ b/pypy/doc/stackless.rst
@@ -190,7 +190,7 @@
 from GC issues: if the program "forgets" an unfinished greenlet, it will
 always be collected at the next garbage collection.
 
-.. _documentation of the greenlets: http://packages.python.org/greenlet/
+.. _documentation of the greenlets: https://greenlet.readthedocs.io/
 
 
 Unimplemented features
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)
+
+@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/module/_cffi_backend/__init__.py 
b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -50,7 +50,7 @@
 
         'string': 'func.string',
         'unpack': 'func.unpack',
-        'buffer': 'cbuffer.buffer',
+        'buffer': 'cbuffer.MiniBuffer',
         'memmove': 'func.memmove',
 
         'get_errno': 'cerrno.get_errno',
diff --git a/pypy/module/_cffi_backend/cbuffer.py 
b/pypy/module/_cffi_backend/cbuffer.py
--- a/pypy/module/_cffi_backend/cbuffer.py
+++ b/pypy/module/_cffi_backend/cbuffer.py
@@ -75,18 +75,8 @@
             self.buffer.setslice(start, value.as_str())
 
 
-MiniBuffer.typedef = TypeDef(
-    "_cffi_backend.buffer",
-    __len__ = interp2app(MiniBuffer.descr_len),
-    __getitem__ = interp2app(MiniBuffer.descr_getitem),
-    __setitem__ = interp2app(MiniBuffer.descr_setitem),
-    __weakref__ = make_weakref_descr(MiniBuffer),
-    )
-MiniBuffer.typedef.acceptable_as_base_class = False
-
-
 @unwrap_spec(w_cdata=cdataobj.W_CData, size=int)
-def buffer(space, w_cdata, size=-1):
+def MiniBuffer___new__(space, w_subtype, w_cdata, size=-1):
     ctype = w_cdata.ctype
     if isinstance(ctype, ctypeptr.W_CTypePointer):
         if size < 0:
@@ -107,3 +97,24 @@
                     "don't know the size pointed to by '%s'", ctype.name)
     ptr = w_cdata.unsafe_escaping_ptr()    # w_cdata kept alive by MiniBuffer()
     return space.wrap(MiniBuffer(LLBuffer(ptr, size), w_cdata))
+
+MiniBuffer.typedef = TypeDef(
+    "_cffi_backend.buffer",
+    __new__ = interp2app(MiniBuffer___new__),
+    __len__ = interp2app(MiniBuffer.descr_len),
+    __getitem__ = interp2app(MiniBuffer.descr_getitem),
+    __setitem__ = interp2app(MiniBuffer.descr_setitem),
+    __weakref__ = make_weakref_descr(MiniBuffer),
+    __doc__ = """ffi.buffer(cdata[, byte_size]):
+Return a read-write buffer object that references the raw C data
+pointed to by the given 'cdata'.  The 'cdata' must be a pointer or an
+array.  Can be passed to functions expecting a buffer, or directly
+manipulated with:
+
+    buf[:]          get a copy of it in a regular string, or
+    buf[idx]        as a single character
+    buf[:] = ...
+    buf[idx] = ...  change the content
+""",
+    )
+MiniBuffer.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ffi_obj.py 
b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -265,22 +265,6 @@
         return self.space.wrap(align)
 
 
-    @unwrap_spec(w_cdata=W_CData, size=int)
-    def descr_buffer(self, w_cdata, size=-1):
-        """\
-Return a read-write buffer object that references the raw C data
-ointed to by the given 'cdata'.  The 'cdata' must be a pointer or an
-array.  Can be passed to functions expecting a buffer, or directly
-manipulated with:
-
-    buf[:]          get a copy of it in a regular string, or
-    buf[idx]        as a single character
-    buf[:] = ...
-    buf[idx] = ...  change the content"""
-        #
-        return cbuffer.buffer(self.space, w_cdata, size)
-
-
     @unwrap_spec(w_name=WrappedDefault(None),
                  w_error=WrappedDefault(None),
                  w_onerror=WrappedDefault(None))
@@ -751,6 +735,9 @@
     return space.appexec([], """():
         return type('error', (Exception,), {'__module__': 'ffi'})""")
 
+def make_buffer(space):
+    return space.gettypefor(cbuffer.MiniBuffer)
+
 _extras = get_dict_rtld_constants()
 if sys.platform == 'win32':
     _extras['getwinerror'] = interp2app(W_FFIObject.descr_getwinerror)
@@ -770,7 +757,7 @@
                                      cls=W_FFIObject),
         addressof   = interp2app(W_FFIObject.descr_addressof),
         alignof     = interp2app(W_FFIObject.descr_alignof),
-        buffer      = interp2app(W_FFIObject.descr_buffer),
+        buffer      = ClassAttr(make_buffer),
         callback    = interp2app(W_FFIObject.descr_callback),
         cast        = interp2app(W_FFIObject.descr_cast),
         def_extern  = interp2app(W_FFIObject.descr_def_extern),
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py 
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -2288,6 +2288,7 @@
     buf = buffer(c)
     assert repr(buf).startswith('<_cffi_backend.buffer object at 0x')
     assert bytes(buf) == b"hi there\x00"
+    assert type(buf) is buffer
     if sys.version_info < (3,):
         assert str(buf) == "hi there\x00"
         assert unicode(buf) == u+"hi there\x00"
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py 
b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -256,6 +256,10 @@
         ffi = _cffi1_backend.FFI()
         a = ffi.new("signed char[]", [5, 6, 7])
         assert ffi.buffer(a)[:] == b'\x05\x06\x07'
+        assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06'
+        assert type(ffi.buffer(a)) is ffi.buffer
+        assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06'
+        assert type(ffi.buffer(a)) is ffi.buffer
 
     def test_ffi_from_buffer(self):
         import _cffi_backend as _cffi1_backend
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')
diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -40,6 +40,7 @@
 import pypy.module.cpyext.pyerrors
 import pypy.module.cpyext.typeobject
 import pypy.module.cpyext.object
+import pypy.module.cpyext.buffer
 import pypy.module.cpyext.bytesobject
 import pypy.module.cpyext.bytearrayobject
 import pypy.module.cpyext.tupleobject
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -126,7 +126,7 @@
 Py_TPFLAGS_HEAPTYPE
 Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_MAX_NDIMS
 Py_CLEANUP_SUPPORTED
-PyBUF_FORMAT PyBUF_ND PyBUF_STRIDES
+PyBUF_FORMAT PyBUF_ND PyBUF_STRIDES PyBUF_WRITABLE PyBUF_SIMPLE
 """.split()
 for name in constant_names:
     setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name))
@@ -571,6 +571,7 @@
     '_Py_BuildValue_SizeT', '_Py_VaBuildValue_SizeT',
 
     'PyErr_Format', 'PyErr_NewException', 'PyErr_NewExceptionWithDoc',
+    'PyErr_WarnFormat',
     'PySys_WriteStdout', 'PySys_WriteStderr',
 
     'PyEval_CallFunction', 'PyEval_CallMethod', 'PyObject_CallFunction',
@@ -612,6 +613,9 @@
     'Py_FrozenFlag', 'Py_TabcheckFlag', 'Py_UnicodeFlag', 
'Py_IgnoreEnvironmentFlag',
     'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 
'Py_NoUserSiteDirectory',
     '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', 
'_Py_PackageContext',
+
+    'PyMem_RawMalloc', 'PyMem_RawCalloc', 'PyMem_RawRealloc', 'PyMem_RawFree',
+    'PyMem_Malloc', 'PyMem_Calloc', 'PyMem_Realloc', 'PyMem_Free',
 ]
 TYPES = {}
 FORWARD_DECLS = []
@@ -1074,7 +1078,8 @@
     struct PyPyAPI {
     %(members)s
     } _pypyAPI;
-    RPY_EXTERN struct PyPyAPI* pypyAPI = &_pypyAPI;
+    RPY_EXTERN struct PyPyAPI* pypyAPI;
+    struct PyPyAPI* pypyAPI = &_pypyAPI;
     """ % dict(members=structmembers)
 
     global_objects = []
@@ -1318,6 +1323,7 @@
                          source_dir / "bytesobject.c",
                          source_dir / "complexobject.c",
                          source_dir / "import.c",
+                         source_dir / "_warnings.c",
                          ]
 
 def build_eci(code, use_micronumpy=False, translating=False):
diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/buffer.py
@@ -0,0 +1,72 @@
+from rpython.rtyper.lltypesystem import rffi, lltype
+from pypy.interpreter.error import oefmt
+from pypy.module.cpyext.api import (
+    cpython_api, Py_buffer, Py_ssize_t, Py_ssize_tP, CONST_STRINGP,
+    generic_cpy_call,
+    PyBUF_WRITABLE, PyBUF_FORMAT, PyBUF_ND, PyBUF_STRIDES, PyBUF_SIMPLE)
+from pypy.module.cpyext.pyobject import PyObject, Py_IncRef, Py_DecRef
+
+@cpython_api([PyObject, CONST_STRINGP, Py_ssize_tP], rffi.INT_real, error=-1)
+def PyObject_AsCharBuffer(space, obj, bufferp, sizep):
+    """Returns a pointer to a read-only memory location usable as
+    character-based input.  The obj argument must support the single-segment
+    character buffer interface.  On success, returns 0, sets buffer to the
+    memory location and size to the buffer length.  Returns -1 and sets a
+    TypeError on error.
+    """
+    pto = obj.c_ob_type
+    pb = pto.c_tp_as_buffer
+    if not (pb and pb.c_bf_getbuffer):
+        raise oefmt(space.w_TypeError,
+                    "expected an object with the buffer interface")
+    with lltype.scoped_alloc(Py_buffer) as view:
+        ret = generic_cpy_call(
+            space, pb.c_bf_getbuffer,
+            obj, view, rffi.cast(rffi.INT_real, PyBUF_SIMPLE))
+        if rffi.cast(lltype.Signed, ret) == -1:
+            return -1
+
+        bufferp[0] = rffi.cast(rffi.CCHARP, view.c_buf)
+        sizep[0] = view.c_len
+
+        if pb.c_bf_releasebuffer:
+            generic_cpy_call(space, pb.c_bf_releasebuffer,
+                             obj, view)
+        Py_DecRef(space, view.c_obj)
+    return 0
+
+
+@cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t,
+              lltype.Signed, lltype.Signed], rffi.INT, error=-1)
+def PyBuffer_FillInfo(space, view, obj, buf, length, readonly, flags):
+    """
+    Fills in a buffer-info structure correctly for an exporter that can only
+    share a contiguous chunk of memory of "unsigned bytes" of the given
+    length. Returns 0 on success and -1 (with raising an error) on error.
+    """
+    flags = rffi.cast(lltype.Signed, flags)
+    if flags & PyBUF_WRITABLE and readonly:
+        raise oefmt(space.w_ValueError, "Object is not writable")
+    view.c_buf = buf
+    view.c_len = length
+    view.c_obj = obj
+    if obj:
+        Py_IncRef(space, obj)
+    view.c_itemsize = 1
+    rffi.setintfield(view, 'c_readonly', readonly)
+    rffi.setintfield(view, 'c_ndim', 1)
+    view.c_format = lltype.nullptr(rffi.CCHARP.TO)
+    if (flags & PyBUF_FORMAT) == PyBUF_FORMAT:
+        view.c_format = rffi.str2charp("B")
+    view.c_shape = lltype.nullptr(Py_ssize_tP.TO)
+    if (flags & PyBUF_ND) == PyBUF_ND:
+        view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
+        view.c_shape[0] = view.c_len
+    view.c_strides = lltype.nullptr(Py_ssize_tP.TO)
+    if (flags & PyBUF_STRIDES) == PyBUF_STRIDES:
+        view.c_strides = rffi.cast(Py_ssize_tP, view.c__strides)
+        view.c_strides[0] = view.c_itemsize
+    view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
+    view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
+
+    return 0
diff --git a/pypy/module/cpyext/genobject.py b/pypy/module/cpyext/genobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/genobject.py
@@ -0,0 +1,7 @@
+from pypy.interpreter.generator import GeneratorIterator, Coroutine
+from pypy.module.cpyext.api import build_type_checkers
+
+
+PyGen_Check, PyGen_CheckExact = build_type_checkers("Gen", GeneratorIterator)
+
+_, PyCoro_CheckExact = build_type_checkers("Coro", Coroutine)
diff --git a/pypy/module/cpyext/include/pymem.h 
b/pypy/module/cpyext/include/pymem.h
--- a/pypy/module/cpyext/include/pymem.h
+++ b/pypy/module/cpyext/include/pymem.h
@@ -7,17 +7,22 @@
 extern "C" {
 #endif
 
-#define PyMem_MALLOC(n)                malloc((n) ? (n) : 1)
-#define PyMem_REALLOC(p, n)    realloc((p), (n) ? (n) : 1)
-#define PyMem_FREE             free
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size);
+PyAPI_FUNC(void *) PyMem_RawCalloc(size_t nelem, size_t elsize);
+PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
+#endif
 
-PyAPI_FUNC(void *) PyMem_Malloc(size_t);
-#define PyMem_Free  PyMem_FREE
-#define PyMem_Realloc  PyMem_REALLOC
+PyAPI_FUNC(void *) PyMem_Malloc(size_t size);
+PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize);
+PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_Free(void *ptr);
 
-#define PyMem_RawMalloc PyMem_Malloc
-#define PyMem_RawFree PyMem_Free
-#define PyMem_RawRealloc PyMem_Realloc
+#define PyMem_MALLOC(n)         PyMem_Malloc(n)
+#define PyMem_REALLOC(p, n)     PyMem_Realloc(p, n)
+#define PyMem_FREE(p)           PyMem_Free(p)
+
 
 /*
  * Type-oriented memory interface
diff --git a/pypy/module/cpyext/include/warnings.h 
b/pypy/module/cpyext/include/warnings.h
--- a/pypy/module/cpyext/include/warnings.h
+++ b/pypy/module/cpyext/include/warnings.h
@@ -6,6 +6,9 @@
 
 #define PyErr_WarnPy3k(msg, stacklevel) 0
 
+PyAPI_FUNC(int) PyErr_WarnFormat(PyObject *category, Py_ssize_t stack_level,
+                                 const char *format, ...);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -1,13 +1,11 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
-    cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t, Py_ssize_tP,
-    PyVarObject, Py_buffer, size_t, slot_function, cts,
-    PyBUF_FORMAT, PyBUF_ND, PyBUF_STRIDES,
+    cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t,
+    PyVarObject, size_t, slot_function, cts,
     Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
-    Py_GE, CONST_STRING, CONST_STRINGP, FILEP, fwrite)
+    Py_GE, CONST_STRING, FILEP, fwrite)
 from pypy.module.cpyext.pyobject import (
-    PyObject, PyObjectP, create_ref, from_ref, Py_IncRef, Py_DecRef,
-    get_typedescr, _Py_NewReference)
+    PyObject, PyObjectP, from_ref, Py_IncRef, Py_DecRef, get_typedescr)
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 from pypy.module.cpyext.pyerrors import PyErr_NoMemory, PyErr_BadInternalCall
 from pypy.objspace.std.typeobject import W_TypeObject
@@ -16,10 +14,6 @@
 import pypy.module.__builtin__.operation as operation
 
 
-# from include/object.h
-PyBUF_SIMPLE = 0x0000
-PyBUF_WRITABLE = 0x0001
-
 @cpython_api([size_t], rffi.VOIDP)
 def PyObject_Malloc(space, size):
     # returns non-zero-initialized memory, like CPython
@@ -444,36 +438,6 @@
     is active then NULL is returned but PyErr_Occurred() will return false."""
     return space.call_function(space.builtin.get('dir'), w_o)
 
-@cpython_api([PyObject, CONST_STRINGP, Py_ssize_tP], rffi.INT_real, error=-1)
-def PyObject_AsCharBuffer(space, obj, bufferp, sizep):
-    """Returns a pointer to a read-only memory location usable as
-    character-based input.  The obj argument must support the single-segment
-    character buffer interface.  On success, returns 0, sets buffer to the
-    memory location and size to the buffer length.  Returns -1 and sets a
-    TypeError on error.
-    """
-    pto = obj.c_ob_type
-
-    pb = pto.c_tp_as_buffer
-    if not (pb and pb.c_bf_getbuffer):
-        raise oefmt(space.w_TypeError,
-                    "expected an object with the buffer interface")
-    with lltype.scoped_alloc(Py_buffer) as view:
-        ret = generic_cpy_call(
-            space, pb.c_bf_getbuffer,
-            obj, view, rffi.cast(rffi.INT_real, PyBUF_SIMPLE))
-        if rffi.cast(lltype.Signed, ret) == -1:
-            return -1
-
-        bufferp[0] = rffi.cast(rffi.CCHARP, view.c_buf)
-        sizep[0] = view.c_len
-
-        if pb.c_bf_releasebuffer:
-            generic_cpy_call(space, pb.c_bf_releasebuffer,
-                             obj, view)
-        Py_DecRef(space, view.c_obj)
-    return 0
-
 # Also in include/object.h
 Py_PRINT_RAW = 1 # No string quotes etc.
 
@@ -493,41 +457,3 @@
     with rffi.scoped_nonmovingbuffer(data) as buf:
         fwrite(buf, 1, count, fp)
     return 0
-
-
-PyBUF_WRITABLE = 0x0001  # Copied from object.h
-
-@cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t,
-              lltype.Signed, lltype.Signed], rffi.INT, error=-1)
-def PyBuffer_FillInfo(space, view, obj, buf, length, readonly, flags):
-    """
-    Fills in a buffer-info structure correctly for an exporter that can only
-    share a contiguous chunk of memory of "unsigned bytes" of the given
-    length. Returns 0 on success and -1 (with raising an error) on error.
-    """
-    flags = rffi.cast(lltype.Signed, flags)
-    if flags & PyBUF_WRITABLE and readonly:
-        raise oefmt(space.w_ValueError, "Object is not writable")
-    view.c_buf = buf
-    view.c_len = length
-    view.c_obj = obj
-    if obj:
-        Py_IncRef(space, obj)
-    view.c_itemsize = 1
-    rffi.setintfield(view, 'c_readonly', readonly)
-    rffi.setintfield(view, 'c_ndim', 1)
-    view.c_format = lltype.nullptr(rffi.CCHARP.TO)
-    if (flags & PyBUF_FORMAT) == PyBUF_FORMAT:
-        view.c_format = rffi.str2charp("B")
-    view.c_shape = lltype.nullptr(Py_ssize_tP.TO)
-    if (flags & PyBUF_ND) == PyBUF_ND:
-        view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
-        view.c_shape[0] = view.c_len
-    view.c_strides = lltype.nullptr(Py_ssize_tP.TO)
-    if (flags & PyBUF_STRIDES) == PyBUF_STRIDES:
-        view.c_strides = rffi.cast(Py_ssize_tP, view.c__strides)
-        view.c_strides[0] = view.c_itemsize
-    view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
-    view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
-
-    return 0
diff --git a/pypy/module/cpyext/pystate.py b/pypy/module/cpyext/pystate.py
--- a/pypy/module/cpyext/pystate.py
+++ b/pypy/module/cpyext/pystate.py
@@ -1,6 +1,7 @@
 from pypy.module.cpyext.api import (
     cpython_api, generic_cpy_call, CANNOT_FAIL, CConfig, cpython_struct)
 from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref, from_ref
+from pypy.interpreter.error import OperationError
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib import rthread
 from rpython.rlib.objectmodel import we_are_translated
@@ -317,3 +318,16 @@
     be held.  The thread state must have been reset with a previous call to
     PyThreadState_Clear()."""
 
+@cpython_api([], lltype.Void)
+def PyOS_AfterFork(space):
+    """Function to update some internal state after a process fork; this 
should be
+    called in the new process if the Python interpreter will continue to be 
used.
+    If a new executable is loaded into the new process, this function does not 
need
+    to be called."""
+    if not space.config.translation.thread:
+        return
+    from pypy.module.thread import os_thread
+    try:
+        os_thread.reinit_threads(space)
+    except OperationError as e:
+        e.write_unraisable(space, "PyOS_AfterFork()")
diff --git a/pypy/module/cpyext/src/_warnings.c 
b/pypy/module/cpyext/src/_warnings.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/_warnings.c
@@ -0,0 +1,25 @@
+#include <Python.h>
+
+int
+PyErr_WarnFormat(PyObject *category, Py_ssize_t stack_level,
+                 const char *format, ...)
+{
+    int ret;
+    PyObject *message;
+    va_list vargs;
+
+#ifdef HAVE_STDARG_PROTOTYPES
+    va_start(vargs, format);
+#else
+    va_start(vargs);
+#endif
+    message = PyUnicode_FromFormatV(format, vargs);
+    if (message != NULL) {
+        ret = PyErr_WarnEx(category, PyUnicode_AsUTF8(message), stack_level);
+        Py_DECREF(message);
+    }
+    else
+        ret = -1;
+    va_end(vargs);
+    return ret;
+}
diff --git a/pypy/module/cpyext/src/pymem.c b/pypy/module/cpyext/src/pymem.c
--- a/pypy/module/cpyext/src/pymem.c
+++ b/pypy/module/cpyext/src/pymem.c
@@ -1,6 +1,86 @@
 #include <Python.h>
 
-void * PyMem_Malloc(size_t n)
+void *
+PyMem_RawMalloc(size_t size)
 {
-    return malloc((n) ? (n) : 1);
+    /*
+     * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
+     * Most python internals blindly use a signed Py_ssize_t to track
+     * things without checking for overflows or negatives.
+     * As size_t is unsigned, checking for size < 0 is not required.
+     */
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return malloc(size);
 }
+
+void *
+PyMem_RawCalloc(size_t nelem, size_t elsize)
+{
+    /* see PyMem_RawMalloc() */
+    if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize)
+        return NULL;
+    /* PyMem_RawCalloc(0, 0) means calloc(1, 1). Some systems would return NULL
+       for calloc(0, 0), which would be treated as an error. Some platforms
+       would return a pointer with no memory behind it, which would break
+       pymalloc.  To solve these problems, allocate an extra byte. */
+    if (nelem == 0 || elsize == 0) {
+        nelem = 1;
+        elsize = 1;
+    }
+    return calloc(nelem, elsize);
+}
+
+void*
+PyMem_RawRealloc(void *ptr, size_t size)
+{
+    /* see PyMem_RawMalloc() */
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return realloc(ptr, size);
+}
+
+void PyMem_RawFree(void *ptr)
+{
+    free(ptr);
+}
+
+
+/* the PyMem_Xxx functions are the same as PyMem_RawXxx in PyPy, for now */
+void *PyMem_Malloc(size_t size)
+{
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return malloc(size);
+}
+
+void *PyMem_Calloc(size_t nelem, size_t elsize)
+{
+    if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize)
+        return NULL;
+    if (nelem == 0 || elsize == 0) {
+        nelem = 1;
+        elsize = 1;
+    }
+    return calloc(nelem, elsize);
+}
+
+void* PyMem_Realloc(void *ptr, size_t size)
+{
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return realloc(ptr, size);
+}
+
+void PyMem_Free(void *ptr)
+{
+    free(ptr);
+}
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -405,14 +405,6 @@
     (sys.getfilesystemencoding())."""
     raise NotImplementedError
 
-@cpython_api([PyObject, Py_ssize_t, rffi.CCHARP, ], rffi.INT_real, error=-1)
-def PyErr_WarnFormat(space, category, stack_level, format, ):
-    """Function similar to PyErr_WarnEx(), but use
-    PyUnicode_FromFormat() to format the warning message.  format is
-    an ASCII-encoded string.
-    """
-    raise NotImplementedError
-
 
 @cpython_api([rffi.INT_real], rffi.INT_real, error=-1)
 def PySignal_SetWakeupFd(space, fd):
@@ -668,17 +660,6 @@
     extension modules."""
     raise NotImplementedError
 
-@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
-def PyGen_Check(space, ob):
-    """Return true if ob is a generator object; ob must not be NULL."""
-    raise NotImplementedError
-
-@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
-def PyGen_CheckExact(space, ob):
-    """Return true if ob's type is PyGen_Type is a generator object; ob must 
not
-    be NULL."""
-    raise NotImplementedError
-
 @cpython_api([PyFrameObject], PyObject)
 def PyGen_New(space, frame):
     """Create and return a new generator object based on the frame object. A
@@ -1487,14 +1468,6 @@
     one of the strings '<stdin>' or '???'."""
     raise NotImplementedError
 
-@cpython_api([], lltype.Void)
-def PyOS_AfterFork(space):
-    """Function to update some internal state after a process fork; this 
should be
-    called in the new process if the Python interpreter will continue to be 
used.
-    If a new executable is loaded into the new process, this function does not 
need
-    to be called."""
-    raise NotImplementedError
-
 @cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
 def PyOS_CheckStack(space):
     """Return true when the interpreter runs out of stack space.  This is a 
reliable
diff --git a/pypy/module/cpyext/test/test_genobject.py 
b/pypy/module/cpyext/test/test_genobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_genobject.py
@@ -0,0 +1,27 @@
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.genobject import PyGen_Check, PyGen_CheckExact
+from pypy.module.cpyext.genobject import PyCoro_CheckExact
+
+
+class TestGenObject(BaseApiTest):
+    def test_genobject(self, space):
+        w_geniter = space.appexec([], """():
+            def f():
+                yield 42
+            return f()
+        """)
+        assert PyGen_Check(space, w_geniter)
+        assert PyGen_CheckExact(space, w_geniter)
+        assert not PyCoro_CheckExact(space, w_geniter)
+        assert not PyGen_Check(space, space.wrap(2))
+        assert not PyGen_CheckExact(space, space.wrap("b"))
+        assert not PyCoro_CheckExact(space, space.wrap([]))
+
+        w_coroutine = space.appexec([], """():
+            async def f():
+                pass
+            return f()
+        """)
+        assert not PyGen_Check(space, w_coroutine)
+        assert not PyGen_CheckExact(space, w_coroutine)
+        assert PyCoro_CheckExact(space, w_coroutine)
diff --git a/pypy/module/cpyext/test/test_pyerrors.py 
b/pypy/module/cpyext/test/test_pyerrors.py
--- a/pypy/module/cpyext/test/test_pyerrors.py
+++ b/pypy/module/cpyext/test/test_pyerrors.py
@@ -428,3 +428,18 @@
             assert orig_exc_info == reset_sys_exc_info
             assert new_exc_info == (new_exc.__class__, new_exc, None)
             assert new_exc_info == new_sys_exc_info
+
+    def test_PyErr_WarnFormat(self):
+        import warnings
+
+        module = self.import_extension('foo', [
+                ("test", "METH_NOARGS",
+                 '''
+                 PyErr_WarnFormat(PyExc_UserWarning, 1, "foo %d bar", 42);
+                 Py_RETURN_NONE;
+                 '''),
+                ])
+        with warnings.catch_warnings(record=True) as l:
+            module.test()
+        assert len(l) == 1
+        assert "foo 42 bar" in str(l[0])
diff --git a/pypy/module/cpyext/test/test_pymem.py 
b/pypy/module/cpyext/test/test_pymem.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_pymem.py
@@ -0,0 +1,34 @@
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
+
+class AppTestPyMem(AppTestCpythonExtensionBase):
+    def test_pymem_alloc(self):
+        module = self.import_extension('foo', [
+            ("test", "METH_NOARGS",
+             """
+                int *a, *b;
+                a = PyMem_RawCalloc(4, 50);
+                if (a[49] != 0) {
+                    PyErr_SetString(PyExc_ValueError, "1");
+                    return NULL;
+                }
+                a[49] = 123456;
+                b = PyMem_RawRealloc(a, 2000);
+                b[499] = 789123;
+                PyMem_RawFree(b);
+
+                a = PyMem_Calloc(4, 50);
+                if (a[49] != 0) {
+                    PyErr_SetString(PyExc_ValueError, "2");
+                    return NULL;
+                }
+                a[49] = 123456;
+                b = PyMem_Realloc(a, 2000);
+                b[499] = 789123;
+                PyMem_Free(b);
+
+                Py_RETURN_NONE;
+             """),
+            ])
+        res = module.test()
+        assert res is None
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -548,14 +548,14 @@
 @slot_function([PyObject, lltype.Ptr(Py_buffer), rffi.INT_real], 
rffi.INT_real, error=-1)
 def bytes_getbuffer(space, w_str, view, flags):
     from pypy.module.cpyext.bytesobject import PyBytes_AsString
-    from pypy.module.cpyext.object import PyBuffer_FillInfo
+    from pypy.module.cpyext.buffer import PyBuffer_FillInfo
     c_buf = rffi.cast(rffi.VOIDP, PyBytes_AsString(space, w_str))
     return PyBuffer_FillInfo(space, view, w_str, c_buf,
                              space.len_w(w_str), 1, flags)
 
 @slot_function([PyObject, lltype.Ptr(Py_buffer), rffi.INT_real], 
rffi.INT_real, error=-1)
 def bf_getbuffer(space, w_obj, view, flags):
-    from pypy.module.cpyext.object import PyBuffer_FillInfo
+    from pypy.module.cpyext.buffer import PyBuffer_FillInfo
     buf = space.buffer_w(w_obj, rffi.cast(lltype.Signed, flags))
     c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
     return PyBuffer_FillInfo(space, view, w_obj, c_buf,
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py
@@ -1131,6 +1131,7 @@
             b = ffi.buffer(a)
         except NotImplementedError as e:
             py.test.skip(str(e))
+        assert type(b) is ffi.buffer
         content = b[:]
         assert len(content) == len(b) == 2
         if sys.byteorder == 'little':
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
@@ -234,6 +234,7 @@
     a = ffi.new("signed char[]", [5, 6, 7])
     assert ffi.buffer(a)[:] == b'\x05\x06\x07'
     assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06'
+    assert type(ffi.buffer(a)) is ffi.buffer
 
 def test_ffi_from_buffer():
     import array
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -37,6 +37,11 @@
                                       ['-Werror'])
     return recompiler._verify(ffi, module_name, source, *args, **kwds)
 
+def test_set_source_no_slashes():
+    ffi = FFI()
+    py.test.raises(ValueError, ffi.set_source, "abc/def", None)
+    py.test.raises(ValueError, ffi.set_source, "abc/def", "C code")
+
 
 def test_type_table_func():
     check_type_table("double sin(double);",
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())
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -14,7 +14,7 @@
     SomeUnicodeCodePoint, SomeInstance, SomeBuiltin, SomeBuiltinMethod,
     SomeFloat, SomeIterator, SomePBC, SomeNone, SomeTypeOf, s_ImpossibleValue,
     s_Bool, s_None, s_Int, unionof, add_knowntypedata,
-    SomeWeakRef, SomeUnicodeString, SomeByteArray)
+    SomeWeakRef, SomeUnicodeString, SomeByteArray, SomeOrderedDict)
 from rpython.annotator.bookkeeper import getbookkeeper, immutablevalue
 from rpython.annotator.binaryop import _clone ## XXX where to put this?
 from rpython.annotator.binaryop import _dict_can_only_throw_keyerror
@@ -575,6 +575,13 @@
         pair(self, s_key).delitem()
     method_delitem_with_hash.can_only_throw = _dict_can_only_throw_keyerror
 
+class __extend__(SomeOrderedDict):
+
+    def method_move_to_end(self, s_key, s_last):
+        assert s_Bool.contains(s_last)
+        pair(self, s_key).delitem()
+    method_move_to_end.can_only_throw = _dict_can_only_throw_keyerror
+
 @op.contains.register(SomeString)
 @op.contains.register(SomeUnicodeString)
 def contains_String(annotator, string, char):
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -934,6 +934,24 @@
         return
     d.delitem_with_hash(key, h)
 
+def _untranslated_move_to_end(d, key, last):
+    "NOT_RPYTHON"
+    value = d.pop(key)
+    if last:
+        d[key] = value
+    else:
+        items = d.items()
+        d.clear()
+        d[key] = value
+        d.update(items)
+
+@specialize.call_location()
+def move_to_end(d, key, last=True):
+    if not we_are_translated():
+        _untranslated_move_to_end(d, key, last)
+        return
+    d.move_to_end(key, last)
+
 # ____________________________________________________________
 
 def import_from_mixin(M, special_methods=['__init__', '__del__']):
diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py
--- a/rpython/rlib/rposix_stat.py
+++ b/rpython/rlib/rposix_stat.py
@@ -602,7 +602,7 @@
 
 if rposix.HAVE_FSTATAT:
     from rpython.rlib.rposix import AT_FDCWD, AT_SYMLINK_NOFOLLOW
-    c_fstatat = rffi.llexternal('fstatat',
+    c_fstatat = rffi.llexternal('fstatat64' if _LINUX else 'fstatat',
         [rffi.INT, rffi.CCHARP, STAT_STRUCT, rffi.INT], rffi.INT,
         compilation_info=compilation_info,
         save_err=rffi.RFFI_SAVE_ERRNO, macro=True)
diff --git a/rpython/rlib/test/test_objectmodel.py 
b/rpython/rlib/test/test_objectmodel.py
--- a/rpython/rlib/test/test_objectmodel.py
+++ b/rpython/rlib/test/test_objectmodel.py
@@ -7,7 +7,7 @@
     resizelist_hint, is_annotation_constant, always_inline, NOT_CONSTANT,
     iterkeys_with_hash, iteritems_with_hash, contains_with_hash,
     setitem_with_hash, getitem_with_hash, delitem_with_hash, import_from_mixin,
-    fetch_translated_config, try_inline)
+    fetch_translated_config, try_inline, move_to_end)
 from rpython.translator.translator import TranslationContext, graphof
 from rpython.rtyper.test.tool import BaseRtypingTest
 from rpython.rtyper.test.test_llinterp import interpret
@@ -679,6 +679,16 @@
     assert f(29) == 0
     interpret(f, [27])
 
+def test_rordereddict_move_to_end():
+    d = OrderedDict()
+    d['key1'] = 'val1'
+    d['key2'] = 'val2'
+    d['key3'] = 'val3'
+    move_to_end(d, 'key1')
+    assert d.items() == [('key2', 'val2'), ('key3', 'val3'), ('key1', 'val1')]
+    move_to_end(d, 'key1', last=False)
+    assert d.items() == [('key1', 'val1'), ('key2', 'val2'), ('key3', 'val3')]
+
 def test_import_from_mixin():
     class M:    # old-style
         def f(self):
diff --git a/rpython/rtyper/lltypesystem/rordereddict.py 
b/rpython/rtyper/lltypesystem/rordereddict.py
--- a/rpython/rtyper/lltypesystem/rordereddict.py
+++ b/rpython/rtyper/lltypesystem/rordereddict.py
@@ -407,6 +407,15 @@
         hop.exception_is_here()
         hop.gendirectcall(ll_dict_delitem_with_hash, v_dict, v_key, v_hash)
 
+    def rtype_method_move_to_end(self, hop):
+        v_dict, v_key, v_last = hop.inputargs(
+            self, self.key_repr, lltype.Bool)
+        if not self.custom_eq_hash:
+            hop.has_implicit_exception(KeyError)  # record that we know about 
it
+        hop.exception_is_here()
+        hop.gendirectcall(ll_dict_move_to_end, v_dict, v_key, v_last)
+
+
 class __extend__(pairtype(OrderedDictRepr, rmodel.Repr)):
 
     def rtype_getitem((r_dict, r_key), hop):
@@ -542,16 +551,18 @@
         ll_assert(False, "ll_call_insert_clean_function(): invalid lookup_fun")
         assert False
 
-def ll_call_delete_by_entry_index(d, hash, i):
+def ll_call_delete_by_entry_index(d, hash, i, replace_with):
+    # only called from _ll_dict_del, whose @jit.look_inside_iff
+    # condition should control when we get inside here with the jit
     fun = d.lookup_function_no & FUNC_MASK
     if fun == FUNC_BYTE:
-        ll_dict_delete_by_entry_index(d, hash, i, TYPE_BYTE)
+        ll_dict_delete_by_entry_index(d, hash, i, replace_with, TYPE_BYTE)
     elif fun == FUNC_SHORT:
-        ll_dict_delete_by_entry_index(d, hash, i, TYPE_SHORT)
+        ll_dict_delete_by_entry_index(d, hash, i, replace_with, TYPE_SHORT)
     elif IS_64BIT and fun == FUNC_INT:
-        ll_dict_delete_by_entry_index(d, hash, i, TYPE_INT)
+        ll_dict_delete_by_entry_index(d, hash, i, replace_with, TYPE_INT)
     elif fun == FUNC_LONG:
-        ll_dict_delete_by_entry_index(d, hash, i, TYPE_LONG)
+        ll_dict_delete_by_entry_index(d, hash, i, replace_with, TYPE_LONG)
     else:
         # can't be still FUNC_MUST_REINDEX here
         ll_assert(False, "ll_call_delete_by_entry_index(): invalid lookup_fun")
@@ -805,13 +816,12 @@
     ll_dict_delitem_with_hash(d, key, d.keyhash(key))
 
 def ll_dict_delitem_with_hash(d, key, hash):
-    index = d.lookup_function(d, key, hash, FLAG_DELETE)
+    index = d.lookup_function(d, key, hash, FLAG_LOOKUP)
     if index < 0:
         raise KeyError
-    _ll_dict_del(d, index)
+    _ll_dict_del(d, hash, index)
 
-@jit.look_inside_iff(lambda d, i: jit.isvirtual(d) and jit.isconstant(i))
-def _ll_dict_del(d, index):
+def _ll_dict_del_entry(d, index):
     d.entries.mark_deleted(index)
     d.num_live_items -= 1
     # clear the key and the value if they are GC pointers
@@ -823,6 +833,11 @@
     if ENTRIES.must_clear_value:
         entry.value = lltype.nullptr(ENTRY.value.TO)
 
+@jit.look_inside_iff(lambda d, h, i: jit.isvirtual(d) and jit.isconstant(i))
+def _ll_dict_del(d, hash, index):
+    ll_call_delete_by_entry_index(d, hash, index, DELETED)
+    _ll_dict_del_entry(d, index)
+
     if d.num_live_items == 0:
         # Dict is now empty.  Reset these fields.
         d.num_ever_used_items = 0
@@ -963,7 +978,6 @@
 
 FLAG_LOOKUP = 0
 FLAG_STORE = 1
-FLAG_DELETE = 2
 
 @specialize.memo()
 def _ll_ptr_to_array_of(T):
@@ -985,8 +999,6 @@
     if index >= VALID_OFFSET:
         checkingkey = entries[index - VALID_OFFSET].key
         if direct_compare and checkingkey == key:
-            if store_flag == FLAG_DELETE:
-                indexes[i] = rffi.cast(T, DELETED)
             return index - VALID_OFFSET   # found the entry
         if d.keyeq is not None and entries.hash(index - VALID_OFFSET) == hash:
             # correct hash, maybe the key is e.g. a different pointer to
@@ -1000,8 +1012,6 @@
                     # the compare did major nasty stuff to the dict: start over
                     return ll_dict_lookup(d, key, hash, store_flag, T)
             if found:
-                if store_flag == FLAG_DELETE:
-                    indexes[i] = rffi.cast(T, DELETED)
                 return index - VALID_OFFSET
         deletedslot = -1
     elif index == DELETED:
@@ -1030,8 +1040,6 @@
         elif index >= VALID_OFFSET:
             checkingkey = entries[index - VALID_OFFSET].key
             if direct_compare and checkingkey == key:
-                if store_flag == FLAG_DELETE:
-                    indexes[i] = rffi.cast(T, DELETED)
                 return index - VALID_OFFSET   # found the entry
             if d.keyeq is not None and entries.hash(index - VALID_OFFSET) == 
hash:
                 # correct hash, maybe the key is e.g. a different pointer to
@@ -1044,8 +1052,6 @@
                         # the compare did major nasty stuff to the dict: start 
over
                         return ll_dict_lookup(d, key, hash, store_flag, T)
                 if found:
-                    if store_flag == FLAG_DELETE:
-                        indexes[i] = rffi.cast(T, DELETED)
                     return index - VALID_OFFSET
         elif deletedslot == -1:
             deletedslot = intmask(i)
@@ -1066,7 +1072,11 @@
         perturb >>= PERTURB_SHIFT
     indexes[i] = rffi.cast(T, index + VALID_OFFSET)
 
-def ll_dict_delete_by_entry_index(d, hash, locate_index, T):
+# the following function is only called from _ll_dict_del, whose
+# @jit.look_inside_iff condition should control when we get inside
+# here with the jit
+@jit.unroll_safe
+def ll_dict_delete_by_entry_index(d, hash, locate_index, replace_with, T):
     # Another simplified version of ll_dict_lookup() which locates a
     # hashtable entry with the given 'index' stored in it, and deletes it.
     # This *should* be safe against evil user-level __eq__/__hash__
@@ -1083,7 +1093,7 @@
         i = (i << 2) + i + perturb + 1
         i = i & mask
         perturb >>= PERTURB_SHIFT
-    indexes[i] = rffi.cast(T, DELETED)
+    indexes[i] = rffi.cast(T, replace_with)
 
 # ____________________________________________________________
 #
@@ -1253,18 +1263,8 @@
     if hasattr(DICT, 'fnkeyhash'):
         newdict.fnkeyhash = dict.fnkeyhash
 
-    i = 0
-    while i < newdict.num_ever_used_items:
-        d_entry = newdict.entries[i]
-        entry = dict.entries[i]
-        ENTRY = lltype.typeOf(newdict.entries).TO.OF
-        d_entry.key = entry.key
-        if hasattr(ENTRY, 'f_valid'):
-            d_entry.f_valid = entry.f_valid
-        d_entry.value = entry.value
-        if hasattr(ENTRY, 'f_hash'):
-            d_entry.f_hash = entry.f_hash
-        i += 1
+    rgc.ll_arraycopy(dict.entries, newdict.entries, 0, 0,
+                     newdict.num_ever_used_items)
 
     ll_dict_reindex(newdict, _ll_len_of_d_indexes(dict))
     return newdict
@@ -1390,8 +1390,6 @@
             break
         dic.num_ever_used_items -= 1
 
-    # we must remove the precise entry in the hashtable that points to 'i'
-    ll_call_delete_by_entry_index(dic, entries.hash(i), i)
     return i
 
 def ll_dict_popitem(ELEM, dic):
@@ -1400,21 +1398,139 @@
     r = lltype.malloc(ELEM.TO)
     r.item0 = recast(ELEM.TO.item0, entry.key)
     r.item1 = recast(ELEM.TO.item1, entry.value)
-    _ll_dict_del(dic, i)
+    _ll_dict_del(dic, dic.entries.hash(i), i)
     return r
 
 def ll_dict_pop(dic, key):
-    index = dic.lookup_function(dic, key, dic.keyhash(key), FLAG_DELETE)
+    hash = dic.keyhash(key)
+    index = dic.lookup_function(dic, key, hash, FLAG_LOOKUP)
     if index < 0:
         raise KeyError
     value = dic.entries[index].value
-    _ll_dict_del(dic, index)
+    _ll_dict_del(dic, hash, index)
     return value
 
 def ll_dict_pop_default(dic, key, dfl):
-    index = dic.lookup_function(dic, key, dic.keyhash(key), FLAG_DELETE)
+    hash = dic.keyhash(key)
+    index = dic.lookup_function(dic, key, hash, FLAG_LOOKUP)
     if index < 0:
         return dfl
     value = dic.entries[index].value
-    _ll_dict_del(dic, index)
+    _ll_dict_del(dic, hash, index)
     return value
+
+def ll_dict_move_to_end(d, key, last):
+    if last:
+        ll_dict_move_to_last(d, key)
+    else:
+        ll_dict_move_to_first(d, key)
+
+def ll_dict_move_to_last(d, key):
+    hash = d.keyhash(key)
+    old_index = d.lookup_function(d, key, hash, FLAG_LOOKUP)
+    if old_index < 0:
+        raise KeyError
+
+    if old_index == d.num_ever_used_items - 1:
+        return
+
+    # remove the entry at the old position
+    old_entry = d.entries[old_index]
+    key = old_entry.key
+    value = old_entry.value
+    _ll_dict_del_entry(d, old_index)
+
+    # note a corner case: it is possible that 'replace_with' is just too
+    # large to fit in the type T used so far for the index.  But in that
+    # case, the list 'd.entries' is full, and the following call to
+    # _ll_dict_setitem_lookup_done() will necessarily reindex the dict.
+    # So in that case, this value of 'replace_with' should be ignored.
+    ll_call_delete_by_entry_index(d, hash, old_index,
+            replace_with = VALID_OFFSET + d.num_ever_used_items)
+    _ll_dict_setitem_lookup_done(d, key, value, hash, -1)
+
+def ll_dict_move_to_first(d, key):
+    # In this function, we might do a bit more than the strict minimum
+    # of walks over parts of the array, trying to keep the code at least
+    # semi-reasonable, while the goal is still amortized constant-time
+    # over many calls.
+
+    # Call ll_dict_remove_deleted_items() first if there are too many
+    # deleted items.  Not a perfect solution, because lookup_function()
+    # might do random things with the dict and create many new deleted
+    # items.  Still, should be fine, because nothing crucially depends
+    # on this: the goal is to avoid the dictionary's list growing
+    # forever.
+    if d.num_live_items < len(d.entries) // 2 - 16:
+        ll_dict_remove_deleted_items(d)
+
+    hash = d.keyhash(key)
+    old_index = d.lookup_function(d, key, hash, FLAG_LOOKUP)
+    if old_index <= 0:
+        if old_index < 0:
+            raise KeyError
+        else:
+            return
+
+    # the goal of the following is to set 'idst' to the number of
+    # deleted entries at the beginning, ensuring 'idst > 0'
+    must_reindex = False
+    if d.entries.valid(0):
+        # the first entry is valid, so we need to make room before.
+        new_allocated = _overallocate_entries_len(d.num_ever_used_items)
+        idst = ((new_allocated - d.num_ever_used_items) * 3) // 4
+        ll_assert(idst > 0, "overallocate did not do enough")
+        newitems = lltype.malloc(lltype.typeOf(d).TO.entries.TO, new_allocated)
+        rgc.ll_arraycopy(d.entries, newitems, 0, idst, d.num_ever_used_items)
+        d.entries = newitems
+        i = 0
+        while i < idst:
+            d.entries.mark_deleted(i)
+            i += 1
+        d.num_ever_used_items += idst
+        old_index += idst
+        must_reindex = True
+        idst -= 1
+    else:
+        idst = d.lookup_function_no >> FUNC_SHIFT
+        # All entries in range(0, idst) are deleted.  Check if more are
+        while not d.entries.valid(idst):
+            idst += 1
+        if idst == old_index:
+            d.lookup_function_no = ((d.lookup_function_no & FUNC_MASK) |
+                                    (old_index << FUNC_SHIFT))
+            return
+        idst -= 1
+        d.lookup_function_no = ((d.lookup_function_no & FUNC_MASK) |
+                                (idst << FUNC_SHIFT))
+
+    # remove the entry at the old position
+    ll_assert(d.entries.valid(old_index),
+              "ll_dict_move_to_first: lost old_index")
+    ENTRY = lltype.typeOf(d.entries).TO.OF
+    old_entry = d.entries[old_index]
+    key = old_entry.key
+    value = old_entry.value
+    if hasattr(ENTRY, 'f_hash'):
+        ll_assert(old_entry.f_hash == hash,
+                  "ll_dict_move_to_first: bad hash")
+    _ll_dict_del_entry(d, old_index)
+
+    # put the entry at its new position
+    ll_assert(not d.entries.valid(idst),
+              "ll_dict_move_to_first: overwriting idst")
+    new_entry = d.entries[idst]
+    new_entry.key = key
+    new_entry.value = value
+    if hasattr(ENTRY, 'f_hash'):
+        new_entry.f_hash = hash
+    if hasattr(ENTRY, 'f_valid'):
+        new_entry.f_valid = True
+    d.num_live_items += 1
+
+    # fix the index
+    if must_reindex:
+        ll_dict_reindex(d, _ll_len_of_d_indexes(d))
+    else:
+        ll_call_delete_by_entry_index(d, hash, old_index,
+                replace_with = VALID_OFFSET + idst)
diff --git a/rpython/rtyper/test/test_rdict.py 
b/rpython/rtyper/test/test_rdict.py
--- a/rpython/rtyper/test/test_rdict.py
+++ b/rpython/rtyper/test/test_rdict.py
@@ -1188,6 +1188,12 @@
         assert not self.ll_contains(self.l_dict, ll_key)
         self.removed_keys.append(key)
 
+    def move_to_end(self, key, last=True):
+        "For test_rordereddict"
+
+    def move_to_first(self, key):
+        self.move_to_end(key, last=False)
+
     def copydict(self):
         self.l_dict = self.ll_copy(self.l_dict)
         assert self.ll_len(self.l_dict) == len(self.reference)
@@ -1250,6 +1256,15 @@
         return builds(Action,
             just('delitem'), tuples(sampled_from(self.space.reference)))
 
+    def st_move_to_end(self):
+        return builds(Action,
+            just('move_to_end'), tuples(sampled_from(self.space.reference)))
+
+    def st_move_to_first(self):
+        return builds(Action,
+            just('move_to_first'),
+                tuples(sampled_from(self.space.reference)))
+
     def steps(self):
         if not self.space:
             return builds(Action, just('setup'), tuples(st_keys, st_values))
@@ -1258,7 +1273,8 @@
         if self.space.reference:
             return (
                 self.st_setitem() | sampled_from(global_actions) |
-                self.st_updateitem() | self.st_delitem())
+                self.st_updateitem() | self.st_delitem() |
+                self.st_move_to_end() | self.st_move_to_first())
         else:
             return (self.st_setitem() | sampled_from(global_actions))
 
diff --git a/rpython/rtyper/test/test_rordereddict.py 
b/rpython/rtyper/test/test_rordereddict.py
--- a/rpython/rtyper/test/test_rordereddict.py
+++ b/rpython/rtyper/test/test_rordereddict.py
@@ -1,7 +1,8 @@
 import py
+import random
 from collections import OrderedDict
 
-from hypothesis import settings
+from hypothesis import settings, given, strategies
 from hypothesis.stateful import run_state_machine_as_test
 
 from rpython.rtyper.lltypesystem import lltype, rffi
@@ -145,14 +146,18 @@
         ll_d = rordereddict.ll_newdict(DICT)
         rordereddict.ll_dict_setitem(ll_d, llstr("k"), 1)
         rordereddict.ll_dict_setitem(ll_d, llstr("j"), 2)
-        ITER = rordereddict.get_ll_dictiter(lltype.Ptr(DICT))
+        assert [hlstr(entry.key) for entry in self._ll_iter(ll_d)] == ["k", 
"j"]
+
+    def _ll_iter(self, ll_d):
+        ITER = rordereddict.get_ll_dictiter(lltype.typeOf(ll_d))
         ll_iter = rordereddict.ll_dictiter(ITER, ll_d)
         ll_dictnext = rordereddict._ll_dictnext
-        num = ll_dictnext(ll_iter)
-        assert hlstr(ll_d.entries[num].key) == "k"
-        num = ll_dictnext(ll_iter)
-        assert hlstr(ll_d.entries[num].key) == "j"
-        py.test.raises(StopIteration, ll_dictnext, ll_iter)
+        while True:
+            try:
+                num = ll_dictnext(ll_iter)
+            except StopIteration:
+                break
+            yield ll_d.entries[num]
 
     def test_popitem(self):
         DICT = self._get_str_dict()
@@ -337,6 +342,31 @@
             num_nonfrees += (got > 0)
         assert d.resize_counter <= idx.getlength() * 2 - num_nonfrees * 3
 
+    @given(strategies.lists(strategies.integers(min_value=1, max_value=5)))
+    def test_direct_move_to_end(self, lst):
+        DICT = self._get_int_dict()
+        ll_d = rordereddict.ll_newdict(DICT)
+        rordereddict.ll_dict_setitem(ll_d, 1, 11)
+        rordereddict.ll_dict_setitem(ll_d, 2, 22)
+        def content():
+            return [(entry.key, entry.value) for entry in self._ll_iter(ll_d)]
+        for case in lst:
+            if case == 1:
+                rordereddict.ll_dict_move_to_end(ll_d, 1, True)
+                assert content() == [(2, 22), (1, 11)]
+            elif case == 2:
+                rordereddict.ll_dict_move_to_end(ll_d, 2, True)
+                assert content() == [(1, 11), (2, 22)]
+            elif case == 3:
+                py.test.raises(KeyError, rordereddict.ll_dict_move_to_end,
+                                                 ll_d, 3, True)
+            elif case == 4:
+                rordereddict.ll_dict_move_to_end(ll_d, 2, False)
+                assert content() == [(2, 22), (1, 11)]
+            elif case == 5:
+                rordereddict.ll_dict_move_to_end(ll_d, 1, False)
+                assert content() == [(1, 11), (2, 22)]
+
 
 class TestRDictDirectDummyKey(TestRDictDirect):
     class dummykeyobj:
@@ -369,10 +399,29 @@
         res = self.interpret(func, [5])
         assert res == 6
 
+    def test_move_to_end(self):
+        def func():
+            d1 = OrderedDict()
+            d1['key1'] = 'value1'
+            d1['key2'] = 'value2'
+            for i in range(20):
+                objectmodel.move_to_end(d1, 'key1')
+                assert d1.keys() == ['key2', 'key1']
+                objectmodel.move_to_end(d1, 'key2')
+                assert d1.keys() == ['key1', 'key2']
+            for i in range(20):
+                objectmodel.move_to_end(d1, 'key2', last=False)
+                assert d1.keys() == ['key2', 'key1']
+                objectmodel.move_to_end(d1, 'key1', last=False)
+                assert d1.keys() == ['key1', 'key2']
+        func()
+        self.interpret(func, [])
+
 
 class ODictSpace(MappingSpace):
     MappingRepr = rodct.OrderedDictRepr
     new_reference = OrderedDict
+    moved_around = False
     ll_getitem = staticmethod(rodct.ll_dict_getitem)
     ll_setitem = staticmethod(rodct.ll_dict_setitem)
     ll_delitem = staticmethod(rodct.ll_dict_delitem)
@@ -417,9 +466,23 @@
     def removeindex(self):
         # remove the index, as done during translation for prebuilt dicts
         # (but cannot be done if we already removed a key)
-        if not self.removed_keys:
+        if not self.removed_keys and not self.moved_around:
             rodct.ll_no_initial_index(self.l_dict)
 
+    def move_to_end(self, key, last=True):
+        ll_key = self.ll_key(key)
+        rodct.ll_dict_move_to_end(self.l_dict, ll_key, last)
+        value = self.reference.pop(key)
+        if last:
+            self.reference[key] = value
+        else:
+            items = self.reference.items()
+            self.reference.clear()
+            self.reference[key] = value
+            self.reference.update(items)
+        # prevent ll_no_initial_index()
+        self.moved_around = True
+
     def fullcheck(self):
         # overridden to also check key order
         assert self.ll_len(self.l_dict) == len(self.reference)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to