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