https://github.com/python/cpython/commit/3597642ed57d184511ca2dbd1a382ffe8e280ac4
commit: 3597642ed57d184511ca2dbd1a382ffe8e280ac4
branch: main
author: Tushar Sadhwani <[email protected]>
committer: pablogsal <[email protected]>
date: 2024-09-10T16:07:30+01:00
summary:

gh-122239: Add actual count in unbalanced unpacking error message when possible 
(#122244)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-07-25-01-45-21.gh-issue-122239.7zh-sW.rst
M Doc/whatsnew/3.14.rst
M Lib/test/test_unpack.py
M Python/ceval.c

diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index ebe4b91c5028a6..7aca6bd2117173 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -70,6 +70,21 @@ Summary -- Release highlights
 New Features
 ============
 
+Improved Error Messages
+-----------------------
+
+* When unpacking assignment fails due to incorrect number of variables, the
+  error message prints the received number of values in more cases than before.
+  (Contributed by Tushar Sadhwani in :gh:`122239`.)
+
+  .. code-block:: pycon
+
+     >>> x, y, z = 1, 2, 3, 4
+     Traceback (most recent call last):
+       File "<stdin>", line 1, in <module>
+         x, y, z = 1, 2, 3, 4
+         ^^^^^^^
+     ValueError: too many values to unpack (expected 3, got 4)
 
 
 Other Language Changes
diff --git a/Lib/test/test_unpack.py b/Lib/test/test_unpack.py
index 515ec128a08a9c..adb30c0febbb6f 100644
--- a/Lib/test/test_unpack.py
+++ b/Lib/test/test_unpack.py
@@ -18,6 +18,13 @@
     >>> a == 4 and b == 5 and c == 6
     True
 
+Unpack dict
+
+    >>> d = {4: 'four', 5: 'five', 6: 'six'}
+    >>> a, b, c = d
+    >>> a == 4 and b == 5 and c == 6
+    True
+
 Unpack implied tuple
 
     >>> a, b, c = 7, 8, 9
@@ -66,14 +73,14 @@
     >>> a, b = t
     Traceback (most recent call last):
       ...
-    ValueError: too many values to unpack (expected 2)
+    ValueError: too many values to unpack (expected 2, got 3)
 
 Unpacking tuple of wrong size
 
     >>> a, b = l
     Traceback (most recent call last):
       ...
-    ValueError: too many values to unpack (expected 2)
+    ValueError: too many values to unpack (expected 2, got 3)
 
 Unpacking sequence too short
 
@@ -140,8 +147,52 @@
     >>> () = [42]
     Traceback (most recent call last):
       ...
-    ValueError: too many values to unpack (expected 0)
+    ValueError: too many values to unpack (expected 0, got 1)
+
+Unpacking a larger iterable should raise ValuleError, but it
+should not entirely consume the iterable
 
+    >>> it = iter(range(100))
+    >>> x, y, z = it
+    Traceback (most recent call last):
+      ...
+    ValueError: too many values to unpack (expected 3)
+    >>> next(it)
+    4
+
+Unpacking unbalanced dict
+
+    >>> d = {4: 'four', 5: 'five', 6: 'six', 7: 'seven'}
+    >>> a, b, c = d
+    Traceback (most recent call last):
+      ...
+    ValueError: too many values to unpack (expected 3, got 4)
+
+Ensure that custom `__len__()` is NOT called when showing the error message
+
+    >>> class LengthTooLong:
+    ...     def __len__(self):
+    ...         return 5
+    ...     def __getitem__(self, i):
+    ...         return i*2
+    ...
+    >>> x, y, z = LengthTooLong()
+    Traceback (most recent call last):
+      ...
+    ValueError: too many values to unpack (expected 3)
+
+For evil cases like these as well, no actual count to be shown
+
+    >>> class BadLength:
+    ...     def __len__(self):
+    ...         return 1
+    ...     def __getitem__(self, i):
+    ...         return i*2
+    ...
+    >>> x, y, z = BadLength()
+    Traceback (most recent call last):
+      ...
+    ValueError: too many values to unpack (expected 3)
 """
 
 __test__ = {'doctests' : doctests}
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-07-25-01-45-21.gh-issue-122239.7zh-sW.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-07-25-01-45-21.gh-issue-122239.7zh-sW.rst
new file mode 100644
index 00000000000000..3e8116ba7d28f1
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-07-25-01-45-21.gh-issue-122239.7zh-sW.rst
@@ -0,0 +1,3 @@
+When a :class:`list`, :class:`tuple` or :class:`dict`
+with too many elements is unpacked, show the actual
+length in the error message.
diff --git a/Python/ceval.c b/Python/ceval.c
index 2a5c16aa101985..0ebd5bb58c859c 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -2148,6 +2148,17 @@ _PyEval_UnpackIterableStackRef(PyThreadState *tstate, 
_PyStackRef v_stackref,
             return 1;
         }
         Py_DECREF(w);
+
+        if (PyList_CheckExact(v) || PyTuple_CheckExact(v)
+              || PyDict_CheckExact(v)) {
+            ll = PyDict_CheckExact(v) ? PyDict_Size(v) : Py_SIZE(v);
+            if (ll > argcnt) {
+                _PyErr_Format(tstate, PyExc_ValueError,
+                              "too many values to unpack (expected %d, got 
%zd)",
+                              argcnt, ll);
+                goto Error;
+            }
+        }
         _PyErr_Format(tstate, PyExc_ValueError,
                       "too many values to unpack (expected %d)",
                       argcnt);

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to