1# Tests for extended unpacking, starred expressions.
2
3import doctest
4import unittest
5
6
7doctests = """
8
9Unpack tuple
10
11    >>> t = (1, 2, 3)
12    >>> a, *b, c = t
13    >>> a == 1 and b == [2] and c == 3
14    True
15
16Unpack list
17
18    >>> l = [4, 5, 6]
19    >>> a, *b = l
20    >>> a == 4 and b == [5, 6]
21    True
22
23Unpack implied tuple
24
25    >>> *a, = 7, 8, 9
26    >>> a == [7, 8, 9]
27    True
28
29Unpack string... fun!
30
31    >>> a, *b = 'one'
32    >>> a == 'o' and b == ['n', 'e']
33    True
34
35Unpack 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
41Unpack short sequence
42
43    >>> a, *b, c = (1, 2)
44    >>> a == 1 and c == 2 and b == []
45    True
46
47Unpack 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
58Unpack 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
66Unpack in list
67
68    >>> [a, *b, c] = range(5)
69    >>> a == 0 and b == [1, 2, 3] and c == 4
70    True
71
72Multiple 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
78Assignment 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
87Set 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
98Dict 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
138List 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
187Iterable argument unpacking
188
189    >>> print(*[1], *[2], 3)
190    1 2 3
191
192Make 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
203Now for some failures
204
205Make 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
244Overridden 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
271Unpacking non-sequence
272
273    >>> a, *b = 7
274    Traceback (most recent call last):
275      ...
276    TypeError: cannot unpack non-iterable int object
277
278Unpacking 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
285Unpacking 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
292Unpacking a sequence where the test for too long raises a different kind of
293error
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
308Trigger code while not expecting an IndexError (unpack sequence too long, wrong
309error)
310
311    >>> a, *b, c, d, e = BadSeq()
312    Traceback (most recent call last):
313      ...
314    test.test_unpack_ex.BozoError
315
316Now 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
378Some 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
399def load_tests(loader, tests, pattern):
400    tests.addTest(doctest.DocTestSuite())
401    return tests
402
403
404if __name__ == "__main__":
405    unittest.main()
406