From a400767e52ba47baa9de1acf5dba7800cfaa3ef4 Mon Sep 17 00:00:00 2001 From: Jaemin Park <56078563+jamie2779@users.noreply.github.com> Date: Fri, 24 Jul 2026 22:17:06 +0900 Subject: [PATCH] gh-91484: Allow memoryview cast for F-contiguous (GH-137803) --- Doc/library/stdtypes.rst | 9 +++++-- Doc/whatsnew/3.16.rst | 4 +++ Lib/test/test_buffer.py | 26 +++++++++++++++++++ ...5-08-15-06-28-45.gh-issue-91484.huCgHt.rst | 1 + Objects/memoryobject.c | 8 +++--- 5 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-06-28-45.gh-issue-91484.huCgHt.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 9df5eb78d286..0b6a1b488fb6 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4876,8 +4876,9 @@ copying. Cast a memoryview to a new format or shape. *shape* defaults to ``[byte_length//new_itemsize]``, which means that the result view will be one-dimensional. The return value is a new memoryview, but - the buffer itself is not copied. Supported casts are 1D -> C-:term:`contiguous` - and C-contiguous -> 1D. + the buffer itself is not copied. Supported casts are + 1D -> C-:term:`contiguous`, C-contiguous -> 1D, and + F-contiguous -> 1D. The destination format is restricted to a single element native format in :mod:`struct` syntax. One of the formats must be a byte format @@ -4964,6 +4965,10 @@ copying. .. versionchanged:: 3.5 The source format is no longer restricted when casting to a byte view. + .. versionchanged:: next + Casting a multi-dimensional F-contiguous view to a one-dimensional + view is now supported. + .. method:: count(value, /) Count the number of occurrences of *value*. diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 662defa709a2..06e831ea1e34 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -75,6 +75,10 @@ New features Other language changes ====================== +* :meth:`memoryview.cast` now allows casting a multidimensional + F-contiguous view to a one-dimensional view. + (Contributed by Jaemin Park in :gh:`91484`.) + * :ref:`Frame objects ` now support :mod:`weak references `. This allows associating extra data with active frames, for example in debuggers, without keeping the frames (and everything diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 453dafe2709e..579e680448e9 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -2877,6 +2877,32 @@ class TestBufferProtocol(unittest.TestCase): self.assertEqual(m2.strides, (1,)) self.assertEqual(m2.suboffsets, ()) + def test_memoryview_cast_f_contiguous_ND_1D(self): + nd = ndarray(list(range(12)), shape=[3, 4], format='B', flags=ND_FORTRAN) + m = memoryview(nd) + self.assertTrue(m.f_contiguous) + self.assertTrue(m.contiguous) + + m1 = m.cast('B') + self.assertEqual(m1.ndim, 1) + self.assertEqual(m1.shape, (m.nbytes,)) + self.assertEqual(m1.strides, (1,)) + self.assertTrue(m1.c_contiguous) + self.assertTrue(m1.contiguous) + self.assertEqual(m1.tobytes(), memoryview(nd).tobytes(order='F')) + + for fmt in ('B', 'b', 'c', 'H', 'I'): + size = struct.calcsize(fmt) + if m.nbytes % size == 0: + m2 = m.cast(fmt) + self.assertEqual(m2.ndim, 1) + self.assertEqual(m2.shape, (m.nbytes // size,)) + self.assertTrue(m2.contiguous) + + m3 = m[::-1] + with self.assertRaises(TypeError): + m3.cast('B') + def test_memoryview_tolist(self): # Most tolist() tests are in self.verify() etc. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-06-28-45.gh-issue-91484.huCgHt.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-06-28-45.gh-issue-91484.huCgHt.rst new file mode 100644 index 000000000000..7bba0da22d1c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-15-06-28-45.gh-issue-91484.huCgHt.rst @@ -0,0 +1 @@ +:meth:`memoryview.cast` now allows casting from N-D to 1-D for F-contiguous. diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index d92c7daff15d..f6ffa337c632 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -1485,9 +1485,11 @@ memoryview_cast_impl(PyMemoryViewObject *self, PyObject *format, CHECK_RESTRICTED(self); if (!MV_C_CONTIGUOUS(self->flags)) { - PyErr_SetString(PyExc_TypeError, - "memoryview: casts are restricted to C-contiguous views"); - return NULL; + if (shape || !MV_F_CONTIGUOUS(self->flags)) { + PyErr_SetString(PyExc_TypeError, + "memoryview: casts are restricted to contiguous views"); + return NULL; + } } if ((shape || self->view.ndim != 1) && zero_in_shape(self)) { PyErr_SetString(PyExc_TypeError, -- 2.47.3