From 3597642ed57d184511ca2dbd1a382ffe8e280ac4 Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Tue, 10 Sep 2024 20:37:30 +0530 Subject: [PATCH] gh-122239: Add actual count in unbalanced unpacking error message when possible (#122244) --- Doc/whatsnew/3.14.rst | 15 +++++ Lib/test/test_unpack.py | 57 ++++++++++++++++++- ...-07-25-01-45-21.gh-issue-122239.7zh-sW.rst | 3 + Python/ceval.c | 11 ++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-07-25-01-45-21.gh-issue-122239.7zh-sW.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index ebe4b91c502..7aca6bd2117 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 "", line 1, in + 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 515ec128a08..adb30c0febb 100644 --- a/Lib/test/test_unpack.py +++ b/Lib/test/test_unpack.py @@ -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} 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 00000000000..3e8116ba7d2 --- /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 2a5c16aa101..0ebd5bb58c8 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);