]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-83791: Raise TypeError for len(memoryview_0d) (#18463)
authorEric Wieser <wieser.eric@gmail.com>
Sat, 22 Apr 2023 16:32:47 +0000 (17:32 +0100)
committerGitHub <noreply@github.com>
Sat, 22 Apr 2023 16:32:47 +0000 (17:32 +0100)
Changes the behaviour of `len` on a zero-dimensional `memoryview` to raise `TypeError`. Previously, `len` would return `1`.

Doc/library/stdtypes.rst
Lib/test/test_buffer.py
Lib/test/test_ctypes/test_pep3118.py
Misc/NEWS.d/next/Core and Builtins/2020-02-11-15-54-40.bpo-39610.fvgsCl.rst [new file with mode: 0644]
Objects/memoryobject.c

index aea2410bae354b59f2f0b3b5c6c596f80d067471..2360472b31f175e2c57b7cdfddd7d233bd0ca3b8 100644 (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::
index 8ac3b7e7eb29d138e94b5bd42da6824e6238fc1b..098d2d999643cb2bf7f7afd8981723328e55c6f9 100644 (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)
index c8a70e3e335693385efcaa488076b43a46851f51..038161745df90531ba393b47b0d25a3bd57ef01c 100644 (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,11 +39,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 * 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 @@ 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))
+                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)
@@ -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)),
     ]
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
new file mode 100644 (file)
index 0000000..d65e0f3
--- /dev/null
@@ -0,0 +1,2 @@
+``len()`` for 0-dimensional :class:`memoryview`` objects (such as ``memoryview(ctypes.c_uint8(42))``) now raises a :exc:`TypeError`.\r
+Previously this returned ``1``, which was not consistent with ``mem_0d[0]`` raising an :exc:`IndexError``.\r
index 1d6cc3b508448de90f236f13f8163786c3947076..34cc797b404cdaec9a68b48ab833cefc8a6ea797 100644 (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 */