gh-83791: Raise TypeError for len(memoryview_0d) (#18463)

Changes the behaviour of `len` on a zero-dimensional `memoryview` to raise `TypeError`. Previously, `len` would return `1`.
This commit is contained in:
Eric Wieser 2023-04-22 17:32:47 +01:00 committed by GitHub
parent caed49448d
commit 3d2a46845b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 22 deletions

View File

@ -3715,12 +3715,15 @@ copying.
types such as :class:`bytes` and :class:`bytearray`, an element 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.
.. 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
bytes in a single element.
A :class:`memoryview` supports slicing and indexing to expose its data.
One-dimensional slicing will result in a subview::

View File

@ -965,8 +965,10 @@ class TestBufferProtocol(unittest.TestCase):
self.assertEqual(m.strides, tuple(strides))
self.assertEqual(m.suboffsets, tuple(suboffsets))
n = 1 if ndim == 0 else len(lst)
self.assertEqual(len(m), n)
if ndim == 0:
self.assertRaises(TypeError, len, m)
else:
self.assertEqual(len(m), len(lst))
rep = result.tolist() if fmt else result.tobytes()
self.assertEqual(rep, lst)

View File

@ -28,7 +28,7 @@ class Test(unittest.TestCase):
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,7 +39,6 @@ class Test(unittest.TestCase):
# they are always read/write
self.assertFalse(v.readonly)
if v.shape:
n = 1
for dim in v.shape:
n = n * dim
@ -58,7 +57,7 @@ class Test(unittest.TestCase):
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 @@ class Test(unittest.TestCase):
# 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))
self.assertEqual(n * v.itemsize, len(v.tobytes()))
except:
# so that we can see the failing type
print(tp)
@ -243,7 +241,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), (), LEPoint),
(LEPoint * 1, "T{<l:x:<l:y:}".replace('l', s_long), (1,), LEPoint),
(POINTER(BEPoint), "&T{>l:x:>l:y:}".replace('l', s_long), (), POINTER(BEPoint)),
(POINTER(LEPoint), "&T{<l:x:<l:y:}".replace('l', s_long), (), POINTER(LEPoint)),
]

View File

@ -0,0 +1,2 @@
``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``.

View File

@ -2642,7 +2642,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 */