Skip to content

Commit c210029

Browse files
committed
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.
1 parent f3e7ea5 commit c210029

5 files changed

Lines changed: 33 additions & 22 deletions

File tree

Doc/library/stdtypes.rst

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,12 +3520,11 @@ copying.
35203520
is a single byte, but other types such as :class:`array.array` may have
35213521
bigger elements.
35223522

3523-
``len(view)`` is equal to the length of :class:`~memoryview.tolist`.
3524-
If ``view.ndim = 0``, the length is 1. If ``view.ndim = 1``, the length
3525-
is equal to the number of elements in the view. For higher dimensions,
3526-
the length is equal to the length of the nested list representation of
3527-
the view. The :class:`~memoryview.itemsize` attribute will give you the
3528-
number of bytes in a single element.
3523+
``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which
3524+
is the nested list representation of the view. If ``view.ndim = 1``,
3525+
this is equal to the number of elements in the view.
3526+
The :class:`~memoryview.itemsize` attribute will give you the number of
3527+
bytes in a single element.
35293528

35303529
A :class:`memoryview` supports slicing and indexing to expose its data.
35313530
One-dimensional slicing will result in a subview::

Lib/ctypes/test/test_pep3118.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_native_types(self):
2828
if shape:
2929
self.assertEqual(len(v), shape[0])
3030
else:
31-
self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob))
31+
self.assertRaises(TypeError, len, v)
3232
self.assertEqual(v.itemsize, sizeof(itemtp))
3333
self.assertEqual(v.shape, shape)
3434
# XXX Issue #12851: PyCData_NewGetBuffer() must provide strides
@@ -39,11 +39,10 @@ def test_native_types(self):
3939
# they are always read/write
4040
self.assertFalse(v.readonly)
4141

42-
if v.shape:
43-
n = 1
44-
for dim in v.shape:
45-
n = n * dim
46-
self.assertEqual(n * v.itemsize, len(v.tobytes()))
42+
n = 1
43+
for dim in v.shape:
44+
n = n * dim
45+
self.assertEqual(n * v.itemsize, len(v.tobytes()))
4746
except:
4847
# so that we can see the failing type
4948
print(tp)
@@ -58,7 +57,7 @@ def test_endian_types(self):
5857
if shape:
5958
self.assertEqual(len(v), shape[0])
6059
else:
61-
self.assertEqual(len(v) * sizeof(itemtp), sizeof(ob))
60+
self.assertRaises(TypeError, len, v)
6261
self.assertEqual(v.itemsize, sizeof(itemtp))
6362
self.assertEqual(v.shape, shape)
6463
# XXX Issue #12851
@@ -67,11 +66,10 @@ def test_endian_types(self):
6766
# they are always read/write
6867
self.assertFalse(v.readonly)
6968

70-
if v.shape:
71-
n = 1
72-
for dim in v.shape:
73-
n = n * dim
74-
self.assertEqual(n, len(v))
69+
n = 1
70+
for dim in v.shape:
71+
n = n * dim
72+
self.assertEqual(n * v.itemsize, len(v.tobytes()))
7573
except:
7674
# so that we can see the failing type
7775
print(tp)
@@ -224,7 +222,7 @@ class LEPoint(LittleEndianStructure):
224222
#
225223
endian_types = [
226224
(BEPoint, "T{>l:x:>l:y:}".replace('l', s_long), (), BEPoint),
227-
(LEPoint, "T{<l:x:<l:y:}".replace('l', s_long), (), LEPoint),
225+
(LEPoint * 1, "T{<l:x:<l:y:}".replace('l', s_long), (1,), LEPoint),
228226
(POINTER(BEPoint), "&T{>l:x:>l:y:}".replace('l', s_long), (), POINTER(BEPoint)),
229227
(POINTER(LEPoint), "&T{<l:x:<l:y:}".replace('l', s_long), (), POINTER(LEPoint)),
230228
]

Lib/test/test_buffer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -956,8 +956,10 @@ def check_memoryview(m, expected_readonly=readonly):
956956
self.assertEqual(m.strides, tuple(strides))
957957
self.assertEqual(m.suboffsets, tuple(suboffsets))
958958

959-
n = 1 if ndim == 0 else len(lst)
960-
self.assertEqual(len(m), n)
959+
if ndim == 0:
960+
self.assertRaises(TypeError, len, m)
961+
else:
962+
self.assertEqual(len(m), len(lst))
961963

962964
rep = result.tolist() if fmt else result.tobytes()
963965
self.assertEqual(rep, lst)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
0d ``memoryview`` objects no longer have a ``len``::
2+
3+
>>> import ctypes
4+
>>> mem_0d = memoryview(ctypes.c_uint8(42))
5+
>>> len(mem_0d)
6+
TypeError: ...
7+
8+
Previously this returned ``1``, which was not consistent with the behavior of ``mem_0d[0]``.

Objects/memoryobject.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2591,7 +2591,11 @@ static Py_ssize_t
25912591
memory_length(PyMemoryViewObject *self)
25922592
{
25932593
CHECK_RELEASED_INT(self);
2594-
return self->view.ndim == 0 ? 1 : self->view.shape[0];
2594+
if (self->view.ndim == 0) {
2595+
PyErr_SetString(PyExc_TypeError, "0-dim memory has no length");
2596+
return -1;
2597+
}
2598+
return self->view.shape[0];
25952599
}
25962600

25972601
/* As mapping */

0 commit comments

Comments
 (0)