(root)/
Python-3.12.0/
Lib/
test/
test_unpack_ex.py
       1  # Tests for extended unpacking, starred expressions.
       2  
       3  import doctest
       4  import unittest
       5  
       6  
       7  doctests = """
       8  
       9  Unpack tuple
      10  
      11      >>> t = (1, 2, 3)
      12      >>> a, *b, c = t
      13      >>> a == 1 and b == [2] and c == 3
      14      True
      15  
      16  Unpack list
      17  
      18      >>> l = [4, 5, 6]
      19      >>> a, *b = l
      20      >>> a == 4 and b == [5, 6]
      21      True
      22  
      23  Unpack implied tuple
      24  
      25      >>> *a, = 7, 8, 9
      26      >>> a == [7, 8, 9]
      27      True
      28  
      29  Unpack string... fun!
      30  
      31      >>> a, *b = 'one'
      32      >>> a == 'o' and b == ['n', 'e']
      33      True
      34  
      35  Unpack long sequence
      36  
      37      >>> a, b, c, *d, e, f, g = range(10)
      38      >>> (a, b, c, d, e, f, g) == (0, 1, 2, [3, 4, 5, 6], 7, 8, 9)
      39      True
      40  
      41  Unpack short sequence
      42  
      43      >>> a, *b, c = (1, 2)
      44      >>> a == 1 and c == 2 and b == []
      45      True
      46  
      47  Unpack generic sequence
      48  
      49      >>> class Seq:
      50      ...     def __getitem__(self, i):
      51      ...         if i >= 0 and i < 3: return i
      52      ...         raise IndexError
      53      ...
      54      >>> a, *b = Seq()
      55      >>> a == 0 and b == [1, 2]
      56      True
      57  
      58  Unpack in for statement
      59  
      60      >>> for a, *b, c in [(1,2,3), (4,5,6,7)]:
      61      ...     print(a, b, c)
      62      ...
      63      1 [2] 3
      64      4 [5, 6] 7
      65  
      66  Unpack in list
      67  
      68      >>> [a, *b, c] = range(5)
      69      >>> a == 0 and b == [1, 2, 3] and c == 4
      70      True
      71  
      72  Multiple targets
      73  
      74      >>> a, *b, c = *d, e = range(5)
      75      >>> a == 0 and b == [1, 2, 3] and c == 4 and d == [0, 1, 2, 3] and e == 4
      76      True
      77  
      78  Assignment unpacking
      79  
      80      >>> a, b, *c = range(5)
      81      >>> a, b, c
      82      (0, 1, [2, 3, 4])
      83      >>> *a, b, c = a, b, *c
      84      >>> a, b, c
      85      ([0, 1, 2], 3, 4)
      86  
      87  Set display element unpacking
      88  
      89      >>> a = [1, 2, 3]
      90      >>> sorted({1, *a, 0, 4})
      91      [0, 1, 2, 3, 4]
      92  
      93      >>> {1, *1, 0, 4}
      94      Traceback (most recent call last):
      95        ...
      96      TypeError: 'int' object is not iterable
      97  
      98  Dict display element unpacking
      99  
     100      >>> kwds = {'z': 0, 'w': 12}
     101      >>> sorted({'x': 1, 'y': 2, **kwds}.items())
     102      [('w', 12), ('x', 1), ('y', 2), ('z', 0)]
     103  
     104      >>> sorted({**{'x': 1}, 'y': 2, **{'z': 3}}.items())
     105      [('x', 1), ('y', 2), ('z', 3)]
     106  
     107      >>> sorted({**{'x': 1}, 'y': 2, **{'x': 3}}.items())
     108      [('x', 3), ('y', 2)]
     109  
     110      >>> sorted({**{'x': 1}, **{'x': 3}, 'x': 4}.items())
     111      [('x', 4)]
     112  
     113      >>> {**{}}
     114      {}
     115  
     116      >>> a = {}
     117      >>> {**a}[0] = 1
     118      >>> a
     119      {}
     120  
     121      >>> {**1}
     122      Traceback (most recent call last):
     123      ...
     124      TypeError: 'int' object is not a mapping
     125  
     126      >>> {**[]}
     127      Traceback (most recent call last):
     128      ...
     129      TypeError: 'list' object is not a mapping
     130  
     131      >>> len(eval("{" + ", ".join("**{{{}: {}}}".format(i, i)
     132      ...                          for i in range(1000)) + "}"))
     133      1000
     134  
     135      >>> {0:1, **{0:2}, 0:3, 0:4}
     136      {0: 4}
     137  
     138  List comprehension element unpacking
     139  
     140      >>> a, b, c = [0, 1, 2], 3, 4
     141      >>> [*a, b, c]
     142      [0, 1, 2, 3, 4]
     143  
     144      >>> l = [a, (3, 4), {5}, {6: None}, (i for i in range(7, 10))]
     145      >>> [*item for item in l]
     146      Traceback (most recent call last):
     147      ...
     148      SyntaxError: iterable unpacking cannot be used in comprehension
     149  
     150      >>> [*[0, 1] for i in range(10)]
     151      Traceback (most recent call last):
     152      ...
     153      SyntaxError: iterable unpacking cannot be used in comprehension
     154  
     155      >>> [*'a' for i in range(10)]
     156      Traceback (most recent call last):
     157      ...
     158      SyntaxError: iterable unpacking cannot be used in comprehension
     159  
     160      >>> [*[] for i in range(10)]
     161      Traceback (most recent call last):
     162      ...
     163      SyntaxError: iterable unpacking cannot be used in comprehension
     164  
     165      >>> {**{} for a in [1]}
     166      Traceback (most recent call last):
     167      ...
     168      SyntaxError: dict unpacking cannot be used in dict comprehension
     169  
     170  # Pegen is better here.
     171  # Generator expression in function arguments
     172  
     173  #     >>> list(*x for x in (range(5) for i in range(3)))
     174  #     Traceback (most recent call last):
     175  #     ...
     176  #         list(*x for x in (range(5) for i in range(3)))
     177  #                   ^
     178  #     SyntaxError: invalid syntax
     179  
     180      >>> dict(**x for x in [{1:2}])
     181      Traceback (most recent call last):
     182      ...
     183          dict(**x for x in [{1:2}])
     184                     ^
     185      SyntaxError: invalid syntax
     186  
     187  Iterable argument unpacking
     188  
     189      >>> print(*[1], *[2], 3)
     190      1 2 3
     191  
     192  Make sure that they don't corrupt the passed-in dicts.
     193  
     194      >>> def f(x, y):
     195      ...     print(x, y)
     196      ...
     197      >>> original_dict = {'x': 1}
     198      >>> f(**original_dict, y=2)
     199      1 2
     200      >>> original_dict
     201      {'x': 1}
     202  
     203  Now for some failures
     204  
     205  Make sure the raised errors are right for keyword argument unpackings
     206  
     207      >>> from collections.abc import MutableMapping
     208      >>> class CrazyDict(MutableMapping):
     209      ...     def __init__(self):
     210      ...         self.d = {}
     211      ...
     212      ...     def __iter__(self):
     213      ...         for x in self.d.__iter__():
     214      ...             if x == 'c':
     215      ...                 self.d['z'] = 10
     216      ...             yield x
     217      ...
     218      ...     def __getitem__(self, k):
     219      ...         return self.d[k]
     220      ...
     221      ...     def __len__(self):
     222      ...         return len(self.d)
     223      ...
     224      ...     def __setitem__(self, k, v):
     225      ...         self.d[k] = v
     226      ...
     227      ...     def __delitem__(self, k):
     228      ...         del self.d[k]
     229      ...
     230      >>> d = CrazyDict()
     231      >>> d.d = {chr(ord('a') + x): x for x in range(5)}
     232      >>> e = {**d}
     233      Traceback (most recent call last):
     234      ...
     235      RuntimeError: dictionary changed size during iteration
     236  
     237      >>> d.d = {chr(ord('a') + x): x for x in range(5)}
     238      >>> def f(**kwargs): print(kwargs)
     239      >>> f(**d)
     240      Traceback (most recent call last):
     241      ...
     242      RuntimeError: dictionary changed size during iteration
     243  
     244  Overridden parameters
     245  
     246      >>> f(x=5, **{'x': 3}, y=2)
     247      Traceback (most recent call last):
     248        ...
     249      TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
     250  
     251      >>> f(**{'x': 3}, x=5, y=2)
     252      Traceback (most recent call last):
     253        ...
     254      TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
     255  
     256      >>> f(**{'x': 3}, **{'x': 5}, y=2)
     257      Traceback (most recent call last):
     258        ...
     259      TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
     260  
     261      >>> f(x=5, **{'x': 3}, **{'x': 2})
     262      Traceback (most recent call last):
     263        ...
     264      TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
     265  
     266      >>> f(**{1: 3}, **{1: 5})
     267      Traceback (most recent call last):
     268        ...
     269      TypeError: test.test_unpack_ex.f() got multiple values for keyword argument '1'
     270  
     271  Unpacking non-sequence
     272  
     273      >>> a, *b = 7
     274      Traceback (most recent call last):
     275        ...
     276      TypeError: cannot unpack non-iterable int object
     277  
     278  Unpacking sequence too short
     279  
     280      >>> a, *b, c, d, e = Seq()
     281      Traceback (most recent call last):
     282        ...
     283      ValueError: not enough values to unpack (expected at least 4, got 3)
     284  
     285  Unpacking sequence too short and target appears last
     286  
     287      >>> a, b, c, d, *e = Seq()
     288      Traceback (most recent call last):
     289        ...
     290      ValueError: not enough values to unpack (expected at least 4, got 3)
     291  
     292  Unpacking a sequence where the test for too long raises a different kind of
     293  error
     294  
     295      >>> class BozoError(Exception):
     296      ...     pass
     297      ...
     298      >>> class BadSeq:
     299      ...     def __getitem__(self, i):
     300      ...         if i >= 0 and i < 3:
     301      ...             return i
     302      ...         elif i == 3:
     303      ...             raise BozoError
     304      ...         else:
     305      ...             raise IndexError
     306      ...
     307  
     308  Trigger code while not expecting an IndexError (unpack sequence too long, wrong
     309  error)
     310  
     311      >>> a, *b, c, d, e = BadSeq()
     312      Traceback (most recent call last):
     313        ...
     314      test.test_unpack_ex.BozoError
     315  
     316  Now some general starred expressions (all fail).
     317  
     318      >>> a, *b, c, *d, e = range(10) # doctest:+ELLIPSIS
     319      Traceback (most recent call last):
     320        ...
     321      SyntaxError: multiple starred expressions in assignment
     322  
     323      >>> [*b, *c] = range(10) # doctest:+ELLIPSIS
     324      Traceback (most recent call last):
     325        ...
     326      SyntaxError: multiple starred expressions in assignment
     327  
     328      >>> a,*b,*c,*d = range(4) # doctest:+ELLIPSIS
     329      Traceback (most recent call last):
     330        ...
     331      SyntaxError: multiple starred expressions in assignment
     332  
     333      >>> *a = range(10) # doctest:+ELLIPSIS
     334      Traceback (most recent call last):
     335        ...
     336      SyntaxError: starred assignment target must be in a list or tuple
     337  
     338      >>> *a # doctest:+ELLIPSIS
     339      Traceback (most recent call last):
     340        ...
     341      SyntaxError: can't use starred expression here
     342  
     343      >>> *1 # doctest:+ELLIPSIS
     344      Traceback (most recent call last):
     345        ...
     346      SyntaxError: can't use starred expression here
     347  
     348      >>> x = *a # doctest:+ELLIPSIS
     349      Traceback (most recent call last):
     350        ...
     351      SyntaxError: can't use starred expression here
     352  
     353      >>> (*x),y = 1, 2 # doctest:+ELLIPSIS
     354      Traceback (most recent call last):
     355        ...
     356      SyntaxError: cannot use starred expression here
     357  
     358      >>> (((*x))),y = 1, 2 # doctest:+ELLIPSIS
     359      Traceback (most recent call last):
     360        ...
     361      SyntaxError: cannot use starred expression here
     362  
     363      >>> z,(*x),y = 1, 2, 4 # doctest:+ELLIPSIS
     364      Traceback (most recent call last):
     365        ...
     366      SyntaxError: cannot use starred expression here
     367  
     368      >>> z,(*x) = 1, 2 # doctest:+ELLIPSIS
     369      Traceback (most recent call last):
     370        ...
     371      SyntaxError: cannot use starred expression here
     372  
     373      >>> ((*x),y) = 1, 2 # doctest:+ELLIPSIS
     374      Traceback (most recent call last):
     375        ...
     376      SyntaxError: cannot use starred expression here
     377  
     378  Some size constraints (all fail.)
     379  
     380      >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)"
     381      >>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS
     382      Traceback (most recent call last):
     383       ...
     384      SyntaxError: too many expressions in star-unpacking assignment
     385  
     386      >>> s = ", ".join("a%d" % i for i in range(1<<8 + 1)) + ", *rest = range(1<<8 + 2)"
     387      >>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS
     388      Traceback (most recent call last):
     389       ...
     390      SyntaxError: too many expressions in star-unpacking assignment
     391  
     392  (there is an additional limit, on the number of expressions after the
     393  '*rest', but it's 1<<24 and testing it takes too much memory.)
     394  
     395  """
     396  
     397  __test__ = {'doctests' : doctests}
     398  
     399  def load_tests(loader, tests, pattern):
     400      tests.addTest(doctest.DocTestSuite())
     401      return tests
     402  
     403  
     404  if __name__ == "__main__":
     405      unittest.main()