Skip to content

Commit d08ea70

Browse files
authored
bpo-35845: Add order={'C', 'F', 'A'} parameter to memoryview.tobytes(). (python#11730)
1 parent 4860f01 commit d08ea70

4 files changed

Lines changed: 59 additions & 10 deletions

File tree

Doc/library/stdtypes.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3600,7 +3600,7 @@ copying.
36003600
Previous versions compared the raw memory disregarding the item format
36013601
and the logical array structure.
36023602

3603-
.. method:: tobytes()
3603+
.. method:: tobytes(order=None)
36043604

36053605
Return the data in the buffer as a bytestring. This is equivalent to
36063606
calling the :class:`bytes` constructor on the memoryview. ::
@@ -3616,6 +3616,13 @@ copying.
36163616
supports all format strings, including those that are not in
36173617
:mod:`struct` module syntax.
36183618

3619+
.. versionadded:: 3.8
3620+
*Order* can be {'C', 'F', 'A'}. When *order* is 'C' or 'F', the data
3621+
of the original array is converted to C or Fortran order. For contiguous
3622+
views, 'A' returns an exact copy of the physical memory. In particular,
3623+
in-memory Fortran order is preserved. For non-contiguous views, the
3624+
data is converted to C first. *order=None* is the same as *order='C'*.
3625+
36193626
.. method:: hex()
36203627

36213628
Return a string object containing two hexadecimal digits for each

Lib/test/test_buffer.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,15 @@ def verify(self, result, *, obj,
893893
y = ndarray(initlst, shape=shape, flags=ro, format=fmt)
894894
self.assertEqual(memoryview(y), memoryview(result))
895895

896+
contig_bytes = memoryview(result).tobytes()
897+
self.assertEqual(contig_bytes, contig)
898+
899+
contig_bytes = memoryview(result).tobytes(order=None)
900+
self.assertEqual(contig_bytes, contig)
901+
902+
contig_bytes = memoryview(result).tobytes(order='C')
903+
self.assertEqual(contig_bytes, contig)
904+
896905
# To 'F'
897906
contig = py_buffer_to_contiguous(result, 'F', PyBUF_FULL_RO)
898907
self.assertEqual(len(contig), nmemb * itemsize)
@@ -905,6 +914,9 @@ def verify(self, result, *, obj,
905914
format=fmt)
906915
self.assertEqual(memoryview(y), memoryview(result))
907916

917+
contig_bytes = memoryview(result).tobytes(order='F')
918+
self.assertEqual(contig_bytes, contig)
919+
908920
# To 'A'
909921
contig = py_buffer_to_contiguous(result, 'A', PyBUF_FULL_RO)
910922
self.assertEqual(len(contig), nmemb * itemsize)
@@ -917,6 +929,9 @@ def verify(self, result, *, obj,
917929
y = ndarray(initlst, shape=shape, flags=f|ro, format=fmt)
918930
self.assertEqual(memoryview(y), memoryview(result))
919931

932+
contig_bytes = memoryview(result).tobytes(order='A')
933+
self.assertEqual(contig_bytes, contig)
934+
920935
if is_memoryview_format(fmt):
921936
try:
922937
m = memoryview(result)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add 'order' parameter to memoryview.tobytes().

Objects/memoryobject.c

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,22 +2120,39 @@ memory_tolist(PyMemoryViewObject *mv, PyObject *noargs)
21202120
}
21212121

21222122
static PyObject *
2123-
memory_tobytes(PyMemoryViewObject *self, PyObject *dummy)
2123+
memory_tobytes(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
21242124
{
2125+
static char *kwlist[] = {"order", NULL};
21252126
Py_buffer *src = VIEW_ADDR(self);
2126-
PyObject *bytes = NULL;
2127+
char *order = NULL;
2128+
char ord = 'C';
2129+
PyObject *bytes;
21272130

21282131
CHECK_RELEASED(self);
21292132

2130-
if (MV_C_CONTIGUOUS(self->flags)) {
2131-
return PyBytes_FromStringAndSize(src->buf, src->len);
2133+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|z", kwlist, &order)) {
2134+
return NULL;
2135+
}
2136+
2137+
if (order) {
2138+
if (strcmp(order, "F") == 0) {
2139+
ord = 'F';
2140+
}
2141+
else if (strcmp(order, "A") == 0) {
2142+
ord = 'A';
2143+
}
2144+
else if (strcmp(order, "C") != 0) {
2145+
PyErr_SetString(PyExc_ValueError,
2146+
"order must be 'C', 'F' or 'A'");
2147+
return NULL;
2148+
}
21322149
}
21332150

21342151
bytes = PyBytes_FromStringAndSize(NULL, src->len);
21352152
if (bytes == NULL)
21362153
return NULL;
21372154

2138-
if (buffer_to_contiguous(PyBytes_AS_STRING(bytes), src, 'C') < 0) {
2155+
if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, ord) < 0) {
21392156
Py_DECREF(bytes);
21402157
return NULL;
21412158
}
@@ -2156,10 +2173,15 @@ memory_hex(PyMemoryViewObject *self, PyObject *dummy)
21562173
return _Py_strhex(src->buf, src->len);
21572174
}
21582175

2159-
bytes = memory_tobytes(self, dummy);
2176+
bytes = PyBytes_FromStringAndSize(NULL, src->len);
21602177
if (bytes == NULL)
21612178
return NULL;
21622179

2180+
if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, 'C') < 0) {
2181+
Py_DECREF(bytes);
2182+
return NULL;
2183+
}
2184+
21632185
ret = _Py_strhex(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
21642186
Py_DECREF(bytes);
21652187

@@ -3061,9 +3083,13 @@ PyDoc_STRVAR(memory_release_doc,
30613083
\n\
30623084
Release the underlying buffer exposed by the memoryview object.");
30633085
PyDoc_STRVAR(memory_tobytes_doc,
3064-
"tobytes($self, /)\n--\n\
3086+
"tobytes($self, /, order=None)\n--\n\
30653087
\n\
3066-
Return the data in the buffer as a byte string.");
3088+
Return the data in the buffer as a byte string. Order can be {'C', 'F', 'A'}.\n\
3089+
When order is 'C' or 'F', the data of the original array is converted to C or\n\
3090+
Fortran order. For contiguous views, 'A' returns an exact copy of the physical\n\
3091+
memory. In particular, in-memory Fortran order is preserved. For non-contiguous\n\
3092+
views, the data is converted to C first. order=None is the same as order='C'.");
30673093
PyDoc_STRVAR(memory_hex_doc,
30683094
"hex($self, /)\n--\n\
30693095
\n\
@@ -3083,7 +3109,7 @@ Return a readonly version of the memoryview.");
30833109

30843110
static PyMethodDef memory_methods[] = {
30853111
{"release", (PyCFunction)memory_release, METH_NOARGS, memory_release_doc},
3086-
{"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, memory_tobytes_doc},
3112+
{"tobytes", (PyCFunction)memory_tobytes, METH_VARARGS|METH_KEYWORDS, memory_tobytes_doc},
30873113
{"hex", (PyCFunction)memory_hex, METH_NOARGS, memory_hex_doc},
30883114
{"tolist", (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc},
30893115
{"cast", (PyCFunction)(void(*)(void))memory_cast, METH_VARARGS|METH_KEYWORDS, memory_cast_doc},

0 commit comments

Comments
 (0)