diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 88749785221..d1b1b8c636e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3600,7 +3600,7 @@ copying. Previous versions compared the raw memory disregarding the item format and the logical array structure. - .. method:: tobytes() + .. method:: tobytes(order=None) Return the data in the buffer as a bytestring. This is equivalent to calling the :class:`bytes` constructor on the memoryview. :: @@ -3616,6 +3616,13 @@ copying. supports all format strings, including those that are not in :mod:`struct` module syntax. + .. versionadded:: 3.8 + *Order* can be {'C', 'F', 'A'}. When *order* is 'C' or 'F', the data + of the original array is converted to C or Fortran order. For contiguous + views, 'A' returns an exact copy of the physical memory. In particular, + in-memory Fortran order is preserved. For non-contiguous views, the + data is converted to C first. *order=None* is the same as *order='C'*. + .. method:: hex() Return a string object containing two hexadecimal digits for each diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 761ed0a9a9b..47413c03d66 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -893,6 +893,15 @@ class TestBufferProtocol(unittest.TestCase): y = ndarray(initlst, shape=shape, flags=ro, format=fmt) self.assertEqual(memoryview(y), memoryview(result)) + contig_bytes = memoryview(result).tobytes() + self.assertEqual(contig_bytes, contig) + + contig_bytes = memoryview(result).tobytes(order=None) + self.assertEqual(contig_bytes, contig) + + contig_bytes = memoryview(result).tobytes(order='C') + self.assertEqual(contig_bytes, contig) + # To 'F' contig = py_buffer_to_contiguous(result, 'F', PyBUF_FULL_RO) self.assertEqual(len(contig), nmemb * itemsize) @@ -905,6 +914,9 @@ class TestBufferProtocol(unittest.TestCase): format=fmt) self.assertEqual(memoryview(y), memoryview(result)) + contig_bytes = memoryview(result).tobytes(order='F') + self.assertEqual(contig_bytes, contig) + # To 'A' contig = py_buffer_to_contiguous(result, 'A', PyBUF_FULL_RO) self.assertEqual(len(contig), nmemb * itemsize) @@ -917,6 +929,9 @@ class TestBufferProtocol(unittest.TestCase): y = ndarray(initlst, shape=shape, flags=f|ro, format=fmt) self.assertEqual(memoryview(y), memoryview(result)) + contig_bytes = memoryview(result).tobytes(order='A') + self.assertEqual(contig_bytes, contig) + if is_memoryview_format(fmt): try: m = memoryview(result) diff --git a/Misc/NEWS.d/next/Library/2019-02-02-00-04-01.bpo-35845.1jx2wk.rst b/Misc/NEWS.d/next/Library/2019-02-02-00-04-01.bpo-35845.1jx2wk.rst new file mode 100644 index 00000000000..755baf7b615 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-02-02-00-04-01.bpo-35845.1jx2wk.rst @@ -0,0 +1 @@ +Add 'order' parameter to memoryview.tobytes(). diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index 40e6308c87a..d835704bdaa 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2120,22 +2120,39 @@ memory_tolist(PyMemoryViewObject *mv, PyObject *noargs) } static PyObject * -memory_tobytes(PyMemoryViewObject *self, PyObject *dummy) +memory_tobytes(PyMemoryViewObject *self, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"order", NULL}; Py_buffer *src = VIEW_ADDR(self); - PyObject *bytes = NULL; + char *order = NULL; + char ord = 'C'; + PyObject *bytes; CHECK_RELEASED(self); - if (MV_C_CONTIGUOUS(self->flags)) { - return PyBytes_FromStringAndSize(src->buf, src->len); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|z", kwlist, &order)) { + return NULL; + } + + if (order) { + if (strcmp(order, "F") == 0) { + ord = 'F'; + } + else if (strcmp(order, "A") == 0) { + ord = 'A'; + } + else if (strcmp(order, "C") != 0) { + PyErr_SetString(PyExc_ValueError, + "order must be 'C', 'F' or 'A'"); + return NULL; + } } bytes = PyBytes_FromStringAndSize(NULL, src->len); if (bytes == NULL) return NULL; - if (buffer_to_contiguous(PyBytes_AS_STRING(bytes), src, 'C') < 0) { + if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, ord) < 0) { Py_DECREF(bytes); return NULL; } @@ -2156,10 +2173,15 @@ memory_hex(PyMemoryViewObject *self, PyObject *dummy) return _Py_strhex(src->buf, src->len); } - bytes = memory_tobytes(self, dummy); + bytes = PyBytes_FromStringAndSize(NULL, src->len); if (bytes == NULL) return NULL; + if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, 'C') < 0) { + Py_DECREF(bytes); + return NULL; + } + ret = _Py_strhex(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes)); Py_DECREF(bytes); @@ -3061,9 +3083,13 @@ PyDoc_STRVAR(memory_release_doc, \n\ Release the underlying buffer exposed by the memoryview object."); PyDoc_STRVAR(memory_tobytes_doc, -"tobytes($self, /)\n--\n\ +"tobytes($self, /, order=None)\n--\n\ \n\ -Return the data in the buffer as a byte string."); +Return the data in the buffer as a byte string. Order can be {'C', 'F', 'A'}.\n\ +When order is 'C' or 'F', the data of the original array is converted to C or\n\ +Fortran order. For contiguous views, 'A' returns an exact copy of the physical\n\ +memory. In particular, in-memory Fortran order is preserved. For non-contiguous\n\ +views, the data is converted to C first. order=None is the same as order='C'."); PyDoc_STRVAR(memory_hex_doc, "hex($self, /)\n--\n\ \n\ @@ -3083,7 +3109,7 @@ Return a readonly version of the memoryview."); static PyMethodDef memory_methods[] = { {"release", (PyCFunction)memory_release, METH_NOARGS, memory_release_doc}, - {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, memory_tobytes_doc}, + {"tobytes", (PyCFunction)memory_tobytes, METH_VARARGS|METH_KEYWORDS, memory_tobytes_doc}, {"hex", (PyCFunction)memory_hex, METH_NOARGS, memory_hex_doc}, {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc}, {"cast", (PyCFunction)(void(*)(void))memory_cast, METH_VARARGS|METH_KEYWORDS, memory_cast_doc},