From c210029730580ba3c94782b5087d638525366a33 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 11 Feb 2020 15:47:02 +0000 Subject: [PATCH 1/5] bpo-39610: Raise TypeError for len(memoryview_0d) This makes `memoryview.__len__` consistent with `memoryview.__getitem__`. Also activates and fixes a bug in dead code in the `test_endian` test, which was only being run on 0d arrays. --- Doc/library/stdtypes.rst | 11 ++++----- Lib/ctypes/test/test_pep3118.py | 24 +++++++++---------- Lib/test/test_buffer.py | 6 +++-- .../2020-02-11-15-54-40.bpo-39610.fvgsCl.rst | 8 +++++++ Objects/memoryobject.c | 6 ++++- 5 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 47d64f1e8d65f0..1dee1ecedc2a12 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3520,12 +3520,11 @@ copying. is a single byte, but other types such as :class:`array.array` may have bigger elements. - ``len(view)`` is equal to the length of :class:`~memoryview.tolist`. - If ``view.ndim = 0``, the length is 1. If ``view.ndim = 1``, the length - is equal to the number of elements in the view. For higher dimensions, - the length is equal to the length of the nested list representation of - the view. The :class:`~memoryview.itemsize` attribute will give you the - number of bytes in a single element. + ``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which + is the nested list representation of the view. If ``view.ndim = 1``, + this is equal to the number of elements in the view. + The :class:`~memoryview.itemsize` attribute will give you the number of + bytes in a single element. A :class:`memoryview` supports slicing and indexing to expose its data. One-dimensional slicing will result in a subview:: diff --git a/Lib/ctypes/test/test_pep3118.py b/Lib/ctypes/test/test_pep3118.py index 81e8ca7638fdeb..0c6b392b1607c8 100644 --- a/Lib/ctypes/test/test_pep3118.py +++ b/Lib/ctypes/test/test_pep3118.py @@ -28,7 +28,7 @@ def test_native_types(self): if shape: self.assertEqual(len(v), shape[0]) else: - self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob)) + self.assertRaises(TypeError, len, v) self.assertEqual(v.itemsize, sizeof(itemtp)) self.assertEqual(v.shape, shape) # XXX Issue #12851: PyCData_NewGetBuffer() must provide strides @@ -39,11 +39,10 @@ def test_native_types(self): # they are always read/write self.assertFalse(v.readonly) - if v.shape: - n = 1 - for dim in v.shape: - n = n * dim - self.assertEqual(n * v.itemsize, len(v.tobytes())) + n = 1 + for dim in v.shape: + n = n * dim + self.assertEqual(n * v.itemsize, len(v.tobytes())) except: # so that we can see the failing type print(tp) @@ -58,7 +57,7 @@ def test_endian_types(self): if shape: self.assertEqual(len(v), shape[0]) else: - self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob)) + self.assertRaises(TypeError, len, v) self.assertEqual(v.itemsize, sizeof(itemtp)) self.assertEqual(v.shape, shape) # XXX Issue #12851 @@ -67,11 +66,10 @@ def test_endian_types(self): # they are always read/write self.assertFalse(v.readonly) - if v.shape: - n = 1 - for dim in v.shape: - n = n * dim - self.assertEqual(n, len(v)) + n = 1 + for dim in v.shape: + n = n * dim + self.assertEqual(n * v.itemsize, len(v.tobytes())) except: # so that we can see the failing type print(tp) @@ -224,7 +222,7 @@ class LEPoint(LittleEndianStructure): # endian_types = [ (BEPoint, "T{>l:x:>l:y:}".replace('l', s_long), (), BEPoint), - (LEPoint, "T{l:x:>l:y:}".replace('l', s_long), (), POINTER(BEPoint)), (POINTER(LEPoint), "&T{>> import ctypes + >>> mem_0d = memoryview(ctypes.c_uint8(42)) + >>> len(mem_0d) + TypeError: ... + +Previously this returned ``1``, which was not consistent with the behavior of ``mem_0d[0]``. \ No newline at end of file diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index d9dd11733ef1a1..9744e8d9a14393 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2591,7 +2591,11 @@ static Py_ssize_t memory_length(PyMemoryViewObject *self) { CHECK_RELEASED_INT(self); - return self->view.ndim == 0 ? 1 : self->view.shape[0]; + if (self->view.ndim == 0) { + PyErr_SetString(PyExc_TypeError, "0-dim memory has no length"); + return -1; + } + return self->view.shape[0]; } /* As mapping */ From a35288871540b0bd982a416e7f1a03039f24a9d8 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 29 Aug 2020 13:36:04 +0100 Subject: [PATCH 2/5] Update Doc/library/stdtypes.rst --- Doc/library/stdtypes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 1dee1ecedc2a12..c9197758e677fc 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3523,6 +3523,9 @@ copying. ``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which is the nested list representation of the view. If ``view.ndim = 1``, this is equal to the number of elements in the view. + + .. versionchanged:: 3.10 + If ``view.ndim = 0``, ``len(view)`` now raises `TypeError` instead of returning 1. The :class:`~memoryview.itemsize` attribute will give you the number of bytes in a single element. From dbad24a47bb41e08d674a113c87d1e707195f0ca Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 7 Jul 2022 20:56:13 +0000 Subject: [PATCH 3/5] Fix documentation build --- Doc/library/stdtypes.rst | 7 ++++--- .../2020-02-11-15-54-40.bpo-39610.fvgsCl.rst | 10 ++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index bb0eb817b62fd8..20df6659a0cc47 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3693,9 +3693,10 @@ copying. ``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which is the nested list representation of the view. If ``view.ndim = 1``, this is equal to the number of elements in the view. - - .. versionchanged:: 3.10 - If ``view.ndim = 0``, ``len(view)`` now raises `TypeError` instead of returning 1. + + .. versionchanged:: 3.11 + If ``view.ndim == 0``, ``len(view)`` now raises :exc:`TypeError` instead of returning 1. + The :class:`~memoryview.itemsize` attribute will give you the number of bytes in a single element. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst b/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst index 389f2f19ed7f7b..9889a7ea2d61ea 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst @@ -1,8 +1,2 @@ -0d ``memoryview`` objects no longer have a ``len``:: - - >>> import ctypes - >>> mem_0d = memoryview(ctypes.c_uint8(42)) - >>> len(mem_0d) - TypeError: ... - -Previously this returned ``1``, which was not consistent with the behavior of ``mem_0d[0]``. \ No newline at end of file +``len()`` for 0-dimensional ``memoryview`` objects (such as ``memoryview(ctypes.c_uint8(42))``) now raises a ``TypeError``. +Previously this returned ``1``, which was not consistent with ``mem_0d[0]`` raising an ``IndexError``. From d6b38b90162471c356a35e635ab03ec9451dd0e0 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 21 Feb 2023 18:09:10 +0000 Subject: [PATCH 4/5] Bump version in `versionchanged` --- Doc/library/stdtypes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 89f1680420e572..3a99189d3e7d13 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3717,7 +3717,7 @@ copying. is the nested list representation of the view. If ``view.ndim = 1``, this is equal to the number of elements in the view. - .. versionchanged:: 3.11 + .. versionchanged:: 3.12 If ``view.ndim == 0``, ``len(view)`` now raises :exc:`TypeError` instead of returning 1. The :class:`~memoryview.itemsize` attribute will give you the number of From 4e2495296c23b2f822cd7383b5d8f401c9a3786d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 12 Apr 2023 23:38:12 +0100 Subject: [PATCH 5/5] Sphinx formatting --- .../2020-02-11-15-54-40.bpo-39610.fvgsCl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst b/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst index 9889a7ea2d61ea..d65e0f3db9d6f5 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst @@ -1,2 +1,2 @@ -``len()`` for 0-dimensional ``memoryview`` objects (such as ``memoryview(ctypes.c_uint8(42))``) now raises a ``TypeError``. -Previously this returned ``1``, which was not consistent with ``mem_0d[0]`` raising an ``IndexError``. +``len()`` for 0-dimensional :class:`memoryview`` objects (such as ``memoryview(ctypes.c_uint8(42))``) now raises a :exc:`TypeError`. +Previously this returned ``1``, which was not consistent with ``mem_0d[0]`` raising an :exc:`IndexError``.