bpo-35845: Add order={'C', 'F', 'A'} parameter to memoryview.tobytes(). (#11730)
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 8874978..d1b1b8c6 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -3600,7 +3600,7 @@
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 @@
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 761ed0a..47413c0 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -893,6 +893,15 @@
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 @@
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 @@
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 0000000..755baf7
--- /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 40e6308..d835704 100644
--- a/Objects/memoryobject.c
+++ b/Objects/memoryobject.c
@@ -2120,22 +2120,39 @@
}
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 @@
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 @@
\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 @@
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},