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 161Generator expression in function arguments 162 163 >>> list(*x for x in (range(5) for i in range(3))) 164 Traceback (most recent call last): 165 ... 166 list(*x for x in (range(5) for i in range(3))) 167 ^ 168 SyntaxError: invalid syntax 169 170 >>> dict(**x for x in [{1:2}]) 171 Traceback (most recent call last): 172 ... 173 dict(**x for x in [{1:2}]) 174 ^ 175 SyntaxError: invalid syntax 176 177Iterable argument unpacking 178 179 >>> print(*[1], *[2], 3) 180 1 2 3 181 182Make sure that they don't corrupt the passed-in dicts. 183 184 >>> def f(x, y): 185 ... print(x, y) 186 ... 187 >>> original_dict = {'x': 1} 188 >>> f(**original_dict, y=2) 189 1 2 190 >>> original_dict 191 {'x': 1} 192 193Now for some failures 194 195Make sure the raised errors are right for keyword argument unpackings 196 197 >>> from collections.abc import MutableMapping 198 >>> class CrazyDict(MutableMapping): 199 ... def __init__(self): 200 ... self.d = {} 201 ... 202 ... def __iter__(self): 203 ... for x in self.d.__iter__(): 204 ... if x == 'c': 205 ... self.d['z'] = 10 206 ... yield x 207 ... 208 ... def __getitem__(self, k): 209 ... return self.d[k] 210 ... 211 ... def __len__(self): 212 ... return len(self.d) 213 ... 214 ... def __setitem__(self, k, v): 215 ... self.d[k] = v 216 ... 217 ... def __delitem__(self, k): 218 ... del self.d[k] 219 ... 220 >>> d = CrazyDict() 221 >>> d.d = {chr(ord('a') + x): x for x in range(5)} 222 >>> e = {**d} 223 Traceback (most recent call last): 224 ... 225 RuntimeError: dictionary changed size during iteration 226 227 >>> d.d = {chr(ord('a') + x): x for x in range(5)} 228 >>> def f(**kwargs): print(kwargs) 229 >>> f(**d) 230 Traceback (most recent call last): 231 ... 232 RuntimeError: dictionary changed size during iteration 233 234Overridden parameters 235 236 >>> f(x=5, **{'x': 3}, y=2) 237 Traceback (most recent call last): 238 ... 239 TypeError: f() got multiple values for keyword argument 'x' 240 241 >>> f(**{'x': 3}, x=5, y=2) 242 Traceback (most recent call last): 243 ... 244 TypeError: f() got multiple values for keyword argument 'x' 245 246 >>> f(**{'x': 3}, **{'x': 5}, y=2) 247 Traceback (most recent call last): 248 ... 249 TypeError: f() got multiple values for keyword argument 'x' 250 251 >>> f(x=5, **{'x': 3}, **{'x': 2}) 252 Traceback (most recent call last): 253 ... 254 TypeError: f() got multiple values for keyword argument 'x' 255 256 >>> f(**{1: 3}, **{1: 5}) 257 Traceback (most recent call last): 258 ... 259 TypeError: f() keywords must be strings 260 261Unpacking non-sequence 262 263 >>> a, *b = 7 264 Traceback (most recent call last): 265 ... 266 TypeError: cannot unpack non-iterable int object 267 268Unpacking sequence too short 269 270 >>> a, *b, c, d, e = Seq() 271 Traceback (most recent call last): 272 ... 273 ValueError: not enough values to unpack (expected at least 4, got 3) 274 275Unpacking sequence too short and target appears last 276 277 >>> a, b, c, d, *e = Seq() 278 Traceback (most recent call last): 279 ... 280 ValueError: not enough values to unpack (expected at least 4, got 3) 281 282Unpacking a sequence where the test for too long raises a different kind of 283error 284 285 >>> class BozoError(Exception): 286 ... pass 287 ... 288 >>> class BadSeq: 289 ... def __getitem__(self, i): 290 ... if i >= 0 and i < 3: 291 ... return i 292 ... elif i == 3: 293 ... raise BozoError 294 ... else: 295 ... raise IndexError 296 ... 297 298Trigger code while not expecting an IndexError (unpack sequence too long, wrong 299error) 300 301 >>> a, *b, c, d, e = BadSeq() 302 Traceback (most recent call last): 303 ... 304 test.test_unpack_ex.BozoError 305 306Now some general starred expressions (all fail). 307 308 >>> a, *b, c, *d, e = range(10) # doctest:+ELLIPSIS 309 Traceback (most recent call last): 310 ... 311 SyntaxError: two starred expressions in assignment 312 313 >>> [*b, *c] = range(10) # doctest:+ELLIPSIS 314 Traceback (most recent call last): 315 ... 316 SyntaxError: two starred expressions in assignment 317 318 >>> *a = range(10) # doctest:+ELLIPSIS 319 Traceback (most recent call last): 320 ... 321 SyntaxError: starred assignment target must be in a list or tuple 322 323 >>> *a # doctest:+ELLIPSIS 324 Traceback (most recent call last): 325 ... 326 SyntaxError: can't use starred expression here 327 328 >>> *1 # doctest:+ELLIPSIS 329 Traceback (most recent call last): 330 ... 331 SyntaxError: can't use starred expression here 332 333 >>> x = *a # doctest:+ELLIPSIS 334 Traceback (most recent call last): 335 ... 336 SyntaxError: can't use starred expression here 337 338Some size constraints (all fail.) 339 340 >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)" 341 >>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS 342 Traceback (most recent call last): 343 ... 344 SyntaxError: too many expressions in star-unpacking assignment 345 346 >>> s = ", ".join("a%d" % i for i in range(1<<8 + 1)) + ", *rest = range(1<<8 + 2)" 347 >>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS 348 Traceback (most recent call last): 349 ... 350 SyntaxError: too many expressions in star-unpacking assignment 351 352(there is an additional limit, on the number of expressions after the 353'*rest', but it's 1<<24 and testing it takes too much memory.) 354 355""" 356 357__test__ = {'doctests' : doctests} 358 359def test_main(verbose=False): 360 from test import support 361 from test import test_unpack_ex 362 support.run_doctest(test_unpack_ex, verbose) 363 364if __name__ == "__main__": 365 test_main(verbose=True) 366