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