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

This commit is contained in:
Tushar Sadhwani 2024-09-10 20:37:30 +05:30 committed by GitHub
parent 07f0bf5aa4
commit 3597642ed5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 3 deletions

View File

@ -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

View File

@ -18,6 +18,13 @@ Unpack list
>>> 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 @@ Unpacking tuple of wrong size
>>> 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 @@ Unpacking to an empty iterable should raise ValueError
>>> () = [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}

View File

@ -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.

View File

@ -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);