2021-09-20 05:36:57 -03:00
|
|
|
import doctest
|
|
|
|
import unittest
|
|
|
|
|
|
|
|
|
2004-09-24 18:36:52 -03:00
|
|
|
doctests = """
|
|
|
|
|
|
|
|
Unpack tuple
|
|
|
|
|
|
|
|
>>> t = (1, 2, 3)
|
|
|
|
>>> a, b, c = t
|
|
|
|
>>> a == 1 and b == 2 and c == 3
|
|
|
|
True
|
|
|
|
|
|
|
|
Unpack list
|
|
|
|
|
|
|
|
>>> l = [4, 5, 6]
|
|
|
|
>>> a, b, c = l
|
|
|
|
>>> a == 4 and b == 5 and c == 6
|
|
|
|
True
|
|
|
|
|
2024-09-10 12:07:30 -03:00
|
|
|
Unpack dict
|
|
|
|
|
|
|
|
>>> d = {4: 'four', 5: 'five', 6: 'six'}
|
|
|
|
>>> a, b, c = d
|
|
|
|
>>> a == 4 and b == 5 and c == 6
|
|
|
|
True
|
|
|
|
|
2004-09-24 18:36:52 -03:00
|
|
|
Unpack implied tuple
|
|
|
|
|
|
|
|
>>> a, b, c = 7, 8, 9
|
|
|
|
>>> a == 7 and b == 8 and c == 9
|
|
|
|
True
|
|
|
|
|
|
|
|
Unpack string... fun!
|
|
|
|
|
|
|
|
>>> a, b, c = 'one'
|
|
|
|
>>> a == 'o' and b == 'n' and c == 'e'
|
|
|
|
True
|
|
|
|
|
|
|
|
Unpack generic sequence
|
|
|
|
|
|
|
|
>>> class Seq:
|
|
|
|
... def __getitem__(self, i):
|
|
|
|
... if i >= 0 and i < 3: return i
|
|
|
|
... raise IndexError
|
|
|
|
...
|
|
|
|
>>> a, b, c = Seq()
|
|
|
|
>>> a == 0 and b == 1 and c == 2
|
|
|
|
True
|
|
|
|
|
|
|
|
Single element unpacking, with extra syntax
|
|
|
|
|
|
|
|
>>> st = (99,)
|
|
|
|
>>> sl = [100]
|
|
|
|
>>> a, = st
|
|
|
|
>>> a
|
|
|
|
99
|
|
|
|
>>> b, = sl
|
|
|
|
>>> b
|
|
|
|
100
|
|
|
|
|
|
|
|
Now for some failures
|
|
|
|
|
|
|
|
Unpacking non-sequence
|
|
|
|
|
|
|
|
>>> a, b, c = 7
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2017-12-26 06:30:41 -04:00
|
|
|
TypeError: cannot unpack non-iterable int object
|
2004-09-24 18:36:52 -03:00
|
|
|
|
|
|
|
Unpacking tuple of wrong size
|
|
|
|
|
|
|
|
>>> a, b = t
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2024-09-10 12:07:30 -03:00
|
|
|
ValueError: too many values to unpack (expected 2, got 3)
|
2004-09-24 18:36:52 -03:00
|
|
|
|
|
|
|
Unpacking tuple of wrong size
|
|
|
|
|
|
|
|
>>> a, b = l
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2024-09-10 12:07:30 -03:00
|
|
|
ValueError: too many values to unpack (expected 2, got 3)
|
2004-09-24 18:36:52 -03:00
|
|
|
|
|
|
|
Unpacking sequence too short
|
|
|
|
|
|
|
|
>>> a, b, c, d = Seq()
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2015-04-15 18:08:45 -03:00
|
|
|
ValueError: not enough values to unpack (expected 4, got 3)
|
2004-09-24 18:36:52 -03:00
|
|
|
|
|
|
|
Unpacking sequence too long
|
|
|
|
|
|
|
|
>>> a, b = Seq()
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2010-07-10 07:32:36 -03:00
|
|
|
ValueError: too many values to unpack (expected 2)
|
2004-09-24 18:36:52 -03:00
|
|
|
|
|
|
|
Unpacking a sequence where the test for too long raises a different kind of
|
|
|
|
error
|
|
|
|
|
|
|
|
>>> class BozoError(Exception):
|
|
|
|
... pass
|
|
|
|
...
|
|
|
|
>>> class BadSeq:
|
|
|
|
... def __getitem__(self, i):
|
|
|
|
... if i >= 0 and i < 3:
|
|
|
|
... return i
|
|
|
|
... elif i == 3:
|
|
|
|
... raise BozoError
|
|
|
|
... else:
|
|
|
|
... raise IndexError
|
|
|
|
...
|
|
|
|
|
|
|
|
Trigger code while not expecting an IndexError (unpack sequence too long, wrong
|
|
|
|
error)
|
|
|
|
|
|
|
|
>>> a, b, c, d, e = BadSeq()
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2006-08-26 17:37:44 -03:00
|
|
|
test.test_unpack.BozoError
|
2004-09-24 18:36:52 -03:00
|
|
|
|
|
|
|
Trigger code while expecting an IndexError (unpack sequence too short, wrong
|
|
|
|
error)
|
|
|
|
|
|
|
|
>>> a, b, c = BadSeq()
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2006-08-26 17:37:44 -03:00
|
|
|
test.test_unpack.BozoError
|
2004-09-24 18:36:52 -03:00
|
|
|
|
2016-05-18 02:44:29 -03:00
|
|
|
Allow unpacking empty iterables
|
|
|
|
|
|
|
|
>>> () = []
|
|
|
|
>>> [] = ()
|
|
|
|
>>> [] = []
|
|
|
|
>>> () = ()
|
|
|
|
|
|
|
|
Unpacking non-iterables should raise TypeError
|
|
|
|
|
|
|
|
>>> () = 42
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2017-12-26 06:30:41 -04:00
|
|
|
TypeError: cannot unpack non-iterable int object
|
2016-05-18 02:44:29 -03:00
|
|
|
|
|
|
|
Unpacking to an empty iterable should raise ValueError
|
|
|
|
|
|
|
|
>>> () = [42]
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
2024-09-10 12:07:30 -03:00
|
|
|
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
|
2016-05-18 02:44:29 -03:00
|
|
|
|
2024-09-10 12:07:30 -03:00
|
|
|
>>> 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)
|
2004-09-24 18:36:52 -03:00
|
|
|
"""
|
|
|
|
|
|
|
|
__test__ = {'doctests' : doctests}
|
|
|
|
|
2021-09-20 05:36:57 -03:00
|
|
|
def load_tests(loader, tests, pattern):
|
|
|
|
tests.addTest(doctest.DocTestSuite())
|
|
|
|
return tests
|
|
|
|
|
2004-09-24 18:36:52 -03:00
|
|
|
|
2022-04-17 15:04:29 -03:00
|
|
|
class TestCornerCases(unittest.TestCase):
|
|
|
|
def test_extended_oparg_not_ignored(self):
|
|
|
|
# https://github.com/python/cpython/issues/91625
|
|
|
|
target = "(" + "y,"*400 + ")"
|
|
|
|
code = f"""def unpack_400(x):
|
|
|
|
{target} = x
|
|
|
|
return y
|
|
|
|
"""
|
|
|
|
ns = {}
|
|
|
|
exec(code, ns)
|
|
|
|
unpack_400 = ns["unpack_400"]
|
2023-09-20 13:58:23 -03:00
|
|
|
# Warm up the function for quickening (PEP 659)
|
2022-04-17 15:04:29 -03:00
|
|
|
for _ in range(30):
|
|
|
|
y = unpack_400(range(400))
|
|
|
|
self.assertEqual(y, 399)
|
|
|
|
|
2004-09-24 18:36:52 -03:00
|
|
|
if __name__ == "__main__":
|
2021-09-20 05:36:57 -03:00
|
|
|
unittest.main()
|