Skip to content

Commit 3597642

Browse files
gh-122239: Add actual count in unbalanced unpacking error message when possible (#122244)
1 parent 07f0bf5 commit 3597642

File tree

4 files changed

+83
-3
lines changed

4 files changed

+83
-3
lines changed

Doc/whatsnew/3.14.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@ Summary -- Release highlights
7070
New Features
7171
============
7272

73+
Improved Error Messages
74+
-----------------------
75+
76+
* When unpacking assignment fails due to incorrect number of variables, the
77+
error message prints the received number of values in more cases than before.
78+
(Contributed by Tushar Sadhwani in :gh:`122239`.)
79+
80+
.. code-block:: pycon
81+
82+
>>> x, y, z = 1, 2, 3, 4
83+
Traceback (most recent call last):
84+
File "<stdin>", line 1, in <module>
85+
x, y, z = 1, 2, 3, 4
86+
^^^^^^^
87+
ValueError: too many values to unpack (expected 3, got 4)
7388
7489
7590
Other Language Changes

Lib/test/test_unpack.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
>>> a == 4 and b == 5 and c == 6
1919
True
2020
21+
Unpack dict
22+
23+
>>> d = {4: 'four', 5: 'five', 6: 'six'}
24+
>>> a, b, c = d
25+
>>> a == 4 and b == 5 and c == 6
26+
True
27+
2128
Unpack implied tuple
2229
2330
>>> a, b, c = 7, 8, 9
@@ -66,14 +73,14 @@
6673
>>> a, b = t
6774
Traceback (most recent call last):
6875
...
69-
ValueError: too many values to unpack (expected 2)
76+
ValueError: too many values to unpack (expected 2, got 3)
7077
7178
Unpacking tuple of wrong size
7279
7380
>>> a, b = l
7481
Traceback (most recent call last):
7582
...
76-
ValueError: too many values to unpack (expected 2)
83+
ValueError: too many values to unpack (expected 2, got 3)
7784
7885
Unpacking sequence too short
7986
@@ -140,8 +147,52 @@
140147
>>> () = [42]
141148
Traceback (most recent call last):
142149
...
143-
ValueError: too many values to unpack (expected 0)
150+
ValueError: too many values to unpack (expected 0, got 1)
151+
152+
Unpacking a larger iterable should raise ValuleError, but it
153+
should not entirely consume the iterable
144154
155+
>>> it = iter(range(100))
156+
>>> x, y, z = it
157+
Traceback (most recent call last):
158+
...
159+
ValueError: too many values to unpack (expected 3)
160+
>>> next(it)
161+
4
162+
163+
Unpacking unbalanced dict
164+
165+
>>> d = {4: 'four', 5: 'five', 6: 'six', 7: 'seven'}
166+
>>> a, b, c = d
167+
Traceback (most recent call last):
168+
...
169+
ValueError: too many values to unpack (expected 3, got 4)
170+
171+
Ensure that custom `__len__()` is NOT called when showing the error message
172+
173+
>>> class LengthTooLong:
174+
... def __len__(self):
175+
... return 5
176+
... def __getitem__(self, i):
177+
... return i*2
178+
...
179+
>>> x, y, z = LengthTooLong()
180+
Traceback (most recent call last):
181+
...
182+
ValueError: too many values to unpack (expected 3)
183+
184+
For evil cases like these as well, no actual count to be shown
185+
186+
>>> class BadLength:
187+
... def __len__(self):
188+
... return 1
189+
... def __getitem__(self, i):
190+
... return i*2
191+
...
192+
>>> x, y, z = BadLength()
193+
Traceback (most recent call last):
194+
...
195+
ValueError: too many values to unpack (expected 3)
145196
"""
146197

147198
__test__ = {'doctests' : doctests}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When a :class:`list`, :class:`tuple` or :class:`dict`
2+
with too many elements is unpacked, show the actual
3+
length in the error message.

Python/ceval.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,17 @@ _PyEval_UnpackIterableStackRef(PyThreadState *tstate, _PyStackRef v_stackref,
21482148
return 1;
21492149
}
21502150
Py_DECREF(w);
2151+
2152+
if (PyList_CheckExact(v) || PyTuple_CheckExact(v)
2153+
|| PyDict_CheckExact(v)) {
2154+
ll = PyDict_CheckExact(v) ? PyDict_Size(v) : Py_SIZE(v);
2155+
if (ll > argcnt) {
2156+
_PyErr_Format(tstate, PyExc_ValueError,
2157+
"too many values to unpack (expected %d, got %zd)",
2158+
argcnt, ll);
2159+
goto Error;
2160+
}
2161+
}
21512162
_PyErr_Format(tstate, PyExc_ValueError,
21522163
"too many values to unpack (expected %d)",
21532164
argcnt);

0 commit comments

Comments
 (0)