1"""Tests for the unparse.py script in the Tools/parser directory.""" 2 3import unittest 4import test.support 5import pathlib 6import random 7import tokenize 8import ast 9 10 11def read_pyfile(filename): 12 """Read and return the contents of a Python source file (as a 13 string), taking into account the file encoding.""" 14 with tokenize.open(filename) as stream: 15 return stream.read() 16 17 18for_else = """\ 19def f(): 20 for x in range(10): 21 break 22 else: 23 y = 2 24 z = 3 25""" 26 27while_else = """\ 28def g(): 29 while True: 30 break 31 else: 32 y = 2 33 z = 3 34""" 35 36relative_import = """\ 37from . import fred 38from .. import barney 39from .australia import shrimp as prawns 40""" 41 42nonlocal_ex = """\ 43def f(): 44 x = 1 45 def g(): 46 nonlocal x 47 x = 2 48 y = 7 49 def h(): 50 nonlocal x, y 51""" 52 53# also acts as test for 'except ... as ...' 54raise_from = """\ 55try: 56 1 / 0 57except ZeroDivisionError as e: 58 raise ArithmeticError from e 59""" 60 61class_decorator = """\ 62@f1(arg) 63@f2 64class Foo: pass 65""" 66 67elif1 = """\ 68if cond1: 69 suite1 70elif cond2: 71 suite2 72else: 73 suite3 74""" 75 76elif2 = """\ 77if cond1: 78 suite1 79elif cond2: 80 suite2 81""" 82 83try_except_finally = """\ 84try: 85 suite1 86except ex1: 87 suite2 88except ex2: 89 suite3 90else: 91 suite4 92finally: 93 suite5 94""" 95 96try_except_star_finally = """\ 97try: 98 suite1 99except* ex1: 100 suite2 101except* ex2: 102 suite3 103else: 104 suite4 105finally: 106 suite5 107""" 108 109with_simple = """\ 110with f(): 111 suite1 112""" 113 114with_as = """\ 115with f() as x: 116 suite1 117""" 118 119with_two_items = """\ 120with f() as x, g() as y: 121 suite1 122""" 123 124docstring_prefixes = ( 125 "", 126 "class foo:\n ", 127 "def foo():\n ", 128 "async def foo():\n ", 129) 130 131class ASTTestCase(unittest.TestCase): 132 def assertASTEqual(self, ast1, ast2): 133 # Ensure the comparisons start at an AST node 134 self.assertIsInstance(ast1, ast.AST) 135 self.assertIsInstance(ast2, ast.AST) 136 137 # An AST comparison routine modeled after ast.dump(), but 138 # instead of string building, it traverses the two trees 139 # in lock-step. 140 def traverse_compare(a, b, missing=object()): 141 if type(a) is not type(b): 142 self.fail(f"{type(a)!r} is not {type(b)!r}") 143 if isinstance(a, ast.AST): 144 for field in a._fields: 145 value1 = getattr(a, field, missing) 146 value2 = getattr(b, field, missing) 147 # Singletons are equal by definition, so further 148 # testing can be skipped. 149 if value1 is not value2: 150 traverse_compare(value1, value2) 151 elif isinstance(a, list): 152 try: 153 for node1, node2 in zip(a, b, strict=True): 154 traverse_compare(node1, node2) 155 except ValueError: 156 # Attempt a "pretty" error ala assertSequenceEqual() 157 len1 = len(a) 158 len2 = len(b) 159 if len1 > len2: 160 what = "First" 161 diff = len1 - len2 162 else: 163 what = "Second" 164 diff = len2 - len1 165 msg = f"{what} list contains {diff} additional elements." 166 raise self.failureException(msg) from None 167 elif a != b: 168 self.fail(f"{a!r} != {b!r}") 169 traverse_compare(ast1, ast2) 170 171 def check_ast_roundtrip(self, code1, **kwargs): 172 with self.subTest(code1=code1, ast_parse_kwargs=kwargs): 173 ast1 = ast.parse(code1, **kwargs) 174 code2 = ast.unparse(ast1) 175 ast2 = ast.parse(code2, **kwargs) 176 self.assertASTEqual(ast1, ast2) 177 178 def check_invalid(self, node, raises=ValueError): 179 with self.subTest(node=node): 180 self.assertRaises(raises, ast.unparse, node) 181 182 def get_source(self, code1, code2=None): 183 code2 = code2 or code1 184 code1 = ast.unparse(ast.parse(code1)) 185 return code1, code2 186 187 def check_src_roundtrip(self, code1, code2=None): 188 code1, code2 = self.get_source(code1, code2) 189 with self.subTest(code1=code1, code2=code2): 190 self.assertEqual(code2, code1) 191 192 def check_src_dont_roundtrip(self, code1, code2=None): 193 code1, code2 = self.get_source(code1, code2) 194 with self.subTest(code1=code1, code2=code2): 195 self.assertNotEqual(code2, code1) 196 197class UnparseTestCase(ASTTestCase): 198 # Tests for specific bugs found in earlier versions of unparse 199 200 def test_fstrings(self): 201 self.check_ast_roundtrip("f'a'") 202 self.check_ast_roundtrip("f'{{}}'") 203 self.check_ast_roundtrip("f'{{5}}'") 204 self.check_ast_roundtrip("f'{{5}}5'") 205 self.check_ast_roundtrip("f'X{{}}X'") 206 self.check_ast_roundtrip("f'{a}'") 207 self.check_ast_roundtrip("f'{ {1:2}}'") 208 self.check_ast_roundtrip("f'a{a}a'") 209 self.check_ast_roundtrip("f'a{a}{a}a'") 210 self.check_ast_roundtrip("f'a{a}a{a}a'") 211 self.check_ast_roundtrip("f'{a!r}x{a!s}12{{}}{a!a}'") 212 self.check_ast_roundtrip("f'{a:10}'") 213 self.check_ast_roundtrip("f'{a:100_000{10}}'") 214 self.check_ast_roundtrip("f'{a!r:10}'") 215 self.check_ast_roundtrip("f'{a:a{b}10}'") 216 self.check_ast_roundtrip( 217 "f'a{b}{c!s}{d!r}{e!a}{f:a}{g:a{b}}{h!s:a}" 218 "{j!s:{a}b}{k!s:a{b}c}{l!a:{b}c{d}}{x+y=}'" 219 ) 220 221 def test_fstrings_special_chars(self): 222 # See issue 25180 223 self.check_ast_roundtrip(r"""f'{f"{0}"*3}'""") 224 self.check_ast_roundtrip(r"""f'{f"{y}"*3}'""") 225 self.check_ast_roundtrip("""f''""") 226 self.check_ast_roundtrip('''f"""'end' "quote\\""""''') 227 228 def test_fstrings_complicated(self): 229 # See issue 28002 230 self.check_ast_roundtrip("""f'''{"'"}'''""") 231 self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') 232 self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-'single quote\\'\'\'\'''') 233 self.check_ast_roundtrip('f"""{\'\'\'\n\'\'\'}"""') 234 self.check_ast_roundtrip('f"""{g(\'\'\'\n\'\'\')}"""') 235 self.check_ast_roundtrip('''f"a\\r\\nb"''') 236 self.check_ast_roundtrip('''f"\\u2028{'x'}"''') 237 238 def test_strings(self): 239 self.check_ast_roundtrip("u'foo'") 240 self.check_ast_roundtrip("r'foo'") 241 self.check_ast_roundtrip("b'foo'") 242 243 def test_del_statement(self): 244 self.check_ast_roundtrip("del x, y, z") 245 246 def test_shifts(self): 247 self.check_ast_roundtrip("45 << 2") 248 self.check_ast_roundtrip("13 >> 7") 249 250 def test_for_else(self): 251 self.check_ast_roundtrip(for_else) 252 253 def test_while_else(self): 254 self.check_ast_roundtrip(while_else) 255 256 def test_unary_parens(self): 257 self.check_ast_roundtrip("(-1)**7") 258 self.check_ast_roundtrip("(-1.)**8") 259 self.check_ast_roundtrip("(-1j)**6") 260 self.check_ast_roundtrip("not True or False") 261 self.check_ast_roundtrip("True or not False") 262 263 def test_integer_parens(self): 264 self.check_ast_roundtrip("3 .__abs__()") 265 266 def test_huge_float(self): 267 self.check_ast_roundtrip("1e1000") 268 self.check_ast_roundtrip("-1e1000") 269 self.check_ast_roundtrip("1e1000j") 270 self.check_ast_roundtrip("-1e1000j") 271 272 def test_nan(self): 273 self.assertASTEqual( 274 ast.parse(ast.unparse(ast.Constant(value=float('nan')))), 275 ast.parse('1e1000 - 1e1000') 276 ) 277 278 def test_min_int(self): 279 self.check_ast_roundtrip(str(-(2 ** 31))) 280 self.check_ast_roundtrip(str(-(2 ** 63))) 281 282 def test_imaginary_literals(self): 283 self.check_ast_roundtrip("7j") 284 self.check_ast_roundtrip("-7j") 285 self.check_ast_roundtrip("0j") 286 self.check_ast_roundtrip("-0j") 287 288 def test_lambda_parentheses(self): 289 self.check_ast_roundtrip("(lambda: int)()") 290 291 def test_chained_comparisons(self): 292 self.check_ast_roundtrip("1 < 4 <= 5") 293 self.check_ast_roundtrip("a is b is c is not d") 294 295 def test_function_arguments(self): 296 self.check_ast_roundtrip("def f(): pass") 297 self.check_ast_roundtrip("def f(a): pass") 298 self.check_ast_roundtrip("def f(b = 2): pass") 299 self.check_ast_roundtrip("def f(a, b): pass") 300 self.check_ast_roundtrip("def f(a, b = 2): pass") 301 self.check_ast_roundtrip("def f(a = 5, b = 2): pass") 302 self.check_ast_roundtrip("def f(*, a = 1, b = 2): pass") 303 self.check_ast_roundtrip("def f(*, a = 1, b): pass") 304 self.check_ast_roundtrip("def f(*, a, b = 2): pass") 305 self.check_ast_roundtrip("def f(a, b = None, *, c, **kwds): pass") 306 self.check_ast_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass") 307 self.check_ast_roundtrip("def f(*args, **kwargs): pass") 308 309 def test_relative_import(self): 310 self.check_ast_roundtrip(relative_import) 311 312 def test_nonlocal(self): 313 self.check_ast_roundtrip(nonlocal_ex) 314 315 def test_raise_from(self): 316 self.check_ast_roundtrip(raise_from) 317 318 def test_bytes(self): 319 self.check_ast_roundtrip("b'123'") 320 321 def test_annotations(self): 322 self.check_ast_roundtrip("def f(a : int): pass") 323 self.check_ast_roundtrip("def f(a: int = 5): pass") 324 self.check_ast_roundtrip("def f(*args: [int]): pass") 325 self.check_ast_roundtrip("def f(**kwargs: dict): pass") 326 self.check_ast_roundtrip("def f() -> None: pass") 327 328 def test_set_literal(self): 329 self.check_ast_roundtrip("{'a', 'b', 'c'}") 330 331 def test_empty_set(self): 332 self.assertASTEqual( 333 ast.parse(ast.unparse(ast.Set(elts=[]))), 334 ast.parse('{*()}') 335 ) 336 337 def test_set_comprehension(self): 338 self.check_ast_roundtrip("{x for x in range(5)}") 339 340 def test_dict_comprehension(self): 341 self.check_ast_roundtrip("{x: x*x for x in range(10)}") 342 343 def test_class_decorators(self): 344 self.check_ast_roundtrip(class_decorator) 345 346 def test_class_definition(self): 347 self.check_ast_roundtrip("class A(metaclass=type, *[], **{}): pass") 348 349 def test_elifs(self): 350 self.check_ast_roundtrip(elif1) 351 self.check_ast_roundtrip(elif2) 352 353 def test_try_except_finally(self): 354 self.check_ast_roundtrip(try_except_finally) 355 356 def test_try_except_star_finally(self): 357 self.check_ast_roundtrip(try_except_star_finally) 358 359 def test_starred_assignment(self): 360 self.check_ast_roundtrip("a, *b, c = seq") 361 self.check_ast_roundtrip("a, (*b, c) = seq") 362 self.check_ast_roundtrip("a, *b[0], c = seq") 363 self.check_ast_roundtrip("a, *(b, c) = seq") 364 365 def test_with_simple(self): 366 self.check_ast_roundtrip(with_simple) 367 368 def test_with_as(self): 369 self.check_ast_roundtrip(with_as) 370 371 def test_with_two_items(self): 372 self.check_ast_roundtrip(with_two_items) 373 374 def test_dict_unpacking_in_dict(self): 375 # See issue 26489 376 self.check_ast_roundtrip(r"""{**{'y': 2}, 'x': 1}""") 377 self.check_ast_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""") 378 379 def test_slices(self): 380 self.check_ast_roundtrip("a[i]") 381 self.check_ast_roundtrip("a[i,]") 382 self.check_ast_roundtrip("a[i, j]") 383 # The AST for these next two both look like `a[(*a,)]` 384 self.check_ast_roundtrip("a[(*a,)]") 385 self.check_ast_roundtrip("a[*a]") 386 self.check_ast_roundtrip("a[b, *a]") 387 self.check_ast_roundtrip("a[*a, c]") 388 self.check_ast_roundtrip("a[b, *a, c]") 389 self.check_ast_roundtrip("a[*a, *a]") 390 self.check_ast_roundtrip("a[b, *a, *a]") 391 self.check_ast_roundtrip("a[*a, b, *a]") 392 self.check_ast_roundtrip("a[*a, *a, b]") 393 self.check_ast_roundtrip("a[b, *a, *a, c]") 394 self.check_ast_roundtrip("a[(a:=b)]") 395 self.check_ast_roundtrip("a[(a:=b,c)]") 396 self.check_ast_roundtrip("a[()]") 397 self.check_ast_roundtrip("a[i:j]") 398 self.check_ast_roundtrip("a[:j]") 399 self.check_ast_roundtrip("a[i:]") 400 self.check_ast_roundtrip("a[i:j:k]") 401 self.check_ast_roundtrip("a[:j:k]") 402 self.check_ast_roundtrip("a[i::k]") 403 self.check_ast_roundtrip("a[i:j,]") 404 self.check_ast_roundtrip("a[i:j, k]") 405 406 def test_invalid_raise(self): 407 self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X"))) 408 409 def test_invalid_fstring_value(self): 410 self.check_invalid( 411 ast.JoinedStr( 412 values=[ 413 ast.Name(id="test"), 414 ast.Constant(value="test") 415 ] 416 ) 417 ) 418 419 def test_invalid_fstring_backslash(self): 420 self.check_invalid(ast.FormattedValue(value=ast.Constant(value="\\\\"))) 421 422 def test_invalid_yield_from(self): 423 self.check_invalid(ast.YieldFrom(value=None)) 424 425 def test_import_from_level_none(self): 426 tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')]) 427 self.assertEqual(ast.unparse(tree), "from mod import x") 428 tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')], level=None) 429 self.assertEqual(ast.unparse(tree), "from mod import x") 430 431 def test_docstrings(self): 432 docstrings = ( 433 'this ends with double quote"', 434 'this includes a """triple quote"""', 435 '\r', 436 '\\r', 437 '\t', 438 '\\t', 439 '\n', 440 '\\n', 441 '\r\\r\t\\t\n\\n', 442 '""">>> content = \"\"\"blabla\"\"\" <<<"""', 443 r'foo\n\x00', 444 "' \\'\\'\\'\"\"\" \"\"\\'\\' \\'", 445 '⛎üéş^\\\\X\\\\BB\N{LONG RIGHTWARDS SQUIGGLE ARROW}' 446 ) 447 for docstring in docstrings: 448 # check as Module docstrings for easy testing 449 self.check_ast_roundtrip(f"'''{docstring}'''") 450 451 def test_constant_tuples(self): 452 self.check_src_roundtrip(ast.Constant(value=(1,), kind=None), "(1,)") 453 self.check_src_roundtrip( 454 ast.Constant(value=(1, 2, 3), kind=None), "(1, 2, 3)" 455 ) 456 457 def test_function_type(self): 458 for function_type in ( 459 "() -> int", 460 "(int, int) -> int", 461 "(Callable[complex], More[Complex(call.to_typevar())]) -> None" 462 ): 463 self.check_ast_roundtrip(function_type, mode="func_type") 464 465 def test_type_comments(self): 466 for statement in ( 467 "a = 5 # type:", 468 "a = 5 # type: int", 469 "a = 5 # type: int and more", 470 "def x(): # type: () -> None\n\tpass", 471 "def x(y): # type: (int) -> None and more\n\tpass", 472 "async def x(): # type: () -> None\n\tpass", 473 "async def x(y): # type: (int) -> None and more\n\tpass", 474 "for x in y: # type: int\n\tpass", 475 "async for x in y: # type: int\n\tpass", 476 "with x(): # type: int\n\tpass", 477 "async with x(): # type: int\n\tpass" 478 ): 479 self.check_ast_roundtrip(statement, type_comments=True) 480 481 def test_type_ignore(self): 482 for statement in ( 483 "a = 5 # type: ignore", 484 "a = 5 # type: ignore and more", 485 "def x(): # type: ignore\n\tpass", 486 "def x(y): # type: ignore and more\n\tpass", 487 "async def x(): # type: ignore\n\tpass", 488 "async def x(y): # type: ignore and more\n\tpass", 489 "for x in y: # type: ignore\n\tpass", 490 "async for x in y: # type: ignore\n\tpass", 491 "with x(): # type: ignore\n\tpass", 492 "async with x(): # type: ignore\n\tpass" 493 ): 494 self.check_ast_roundtrip(statement, type_comments=True) 495 496 497class CosmeticTestCase(ASTTestCase): 498 """Test if there are cosmetic issues caused by unnecessary additions""" 499 500 def test_simple_expressions_parens(self): 501 self.check_src_roundtrip("(a := b)") 502 self.check_src_roundtrip("await x") 503 self.check_src_roundtrip("x if x else y") 504 self.check_src_roundtrip("lambda x: x") 505 self.check_src_roundtrip("1 + 1") 506 self.check_src_roundtrip("1 + 2 / 3") 507 self.check_src_roundtrip("(1 + 2) / 3") 508 self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2)") 509 self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2) ** 2") 510 self.check_src_roundtrip("~x") 511 self.check_src_roundtrip("x and y") 512 self.check_src_roundtrip("x and y and z") 513 self.check_src_roundtrip("x and (y and x)") 514 self.check_src_roundtrip("(x and y) and z") 515 self.check_src_roundtrip("(x ** y) ** z ** q") 516 self.check_src_roundtrip("x >> y") 517 self.check_src_roundtrip("x << y") 518 self.check_src_roundtrip("x >> y and x >> z") 519 self.check_src_roundtrip("x + y - z * q ^ t ** k") 520 self.check_src_roundtrip("P * V if P and V else n * R * T") 521 self.check_src_roundtrip("lambda P, V, n: P * V == n * R * T") 522 self.check_src_roundtrip("flag & (other | foo)") 523 self.check_src_roundtrip("not x == y") 524 self.check_src_roundtrip("x == (not y)") 525 self.check_src_roundtrip("yield x") 526 self.check_src_roundtrip("yield from x") 527 self.check_src_roundtrip("call((yield x))") 528 self.check_src_roundtrip("return x + (yield x)") 529 530 def test_class_bases_and_keywords(self): 531 self.check_src_roundtrip("class X:\n pass") 532 self.check_src_roundtrip("class X(A):\n pass") 533 self.check_src_roundtrip("class X(A, B, C, D):\n pass") 534 self.check_src_roundtrip("class X(x=y):\n pass") 535 self.check_src_roundtrip("class X(metaclass=z):\n pass") 536 self.check_src_roundtrip("class X(x=y, z=d):\n pass") 537 self.check_src_roundtrip("class X(A, x=y):\n pass") 538 self.check_src_roundtrip("class X(A, **kw):\n pass") 539 self.check_src_roundtrip("class X(*args):\n pass") 540 self.check_src_roundtrip("class X(*args, **kwargs):\n pass") 541 542 def test_fstrings(self): 543 self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') 544 self.check_src_roundtrip('''f"\\u2028{'x'}"''') 545 self.check_src_roundtrip(r"f'{x}\n'") 546 self.check_src_roundtrip('''f''\'{"""\n"""}\\n''\'''') 547 self.check_src_roundtrip('''f''\'{f"""{x}\n"""}\\n''\'''') 548 549 def test_docstrings(self): 550 docstrings = ( 551 '"""simple doc string"""', 552 '''"""A more complex one 553 with some newlines"""''', 554 '''"""Foo bar baz 555 556 empty newline"""''', 557 '"""With some \t"""', 558 '"""Foo "bar" baz """', 559 '"""\\r"""', 560 '""""""', 561 '"""\'\'\'"""', 562 '"""\'\'\'\'\'\'"""', 563 '"""⛎üéş^\\\\X\\\\BB⟿"""', 564 '"""end in single \'quote\'"""', 565 "'''end in double \"quote\"'''", 566 '"""almost end in double "quote"."""', 567 ) 568 569 for prefix in docstring_prefixes: 570 for docstring in docstrings: 571 self.check_src_roundtrip(f"{prefix}{docstring}") 572 573 def test_docstrings_negative_cases(self): 574 # Test some cases that involve strings in the children of the 575 # first node but aren't docstrings to make sure we don't have 576 # False positives. 577 docstrings_negative = ( 578 'a = """false"""', 579 '"""false""" + """unless its optimized"""', 580 '1 + 1\n"""false"""', 581 'f"""no, top level but f-fstring"""' 582 ) 583 for prefix in docstring_prefixes: 584 for negative in docstrings_negative: 585 # this cases should be result with single quote 586 # rather then triple quoted docstring 587 src = f"{prefix}{negative}" 588 self.check_ast_roundtrip(src) 589 self.check_src_dont_roundtrip(src) 590 591 def test_unary_op_factor(self): 592 for prefix in ("+", "-", "~"): 593 self.check_src_roundtrip(f"{prefix}1") 594 for prefix in ("not",): 595 self.check_src_roundtrip(f"{prefix} 1") 596 597 def test_slices(self): 598 self.check_src_roundtrip("a[()]") 599 self.check_src_roundtrip("a[1]") 600 self.check_src_roundtrip("a[1, 2]") 601 # Note that `a[*a]`, `a[*a,]`, and `a[(*a,)]` all evaluate to the same 602 # thing at runtime and have the same AST, but only `a[*a,]` passes 603 # this test, because that's what `ast.unparse` produces. 604 self.check_src_roundtrip("a[*a,]") 605 self.check_src_roundtrip("a[1, *a]") 606 self.check_src_roundtrip("a[*a, 2]") 607 self.check_src_roundtrip("a[1, *a, 2]") 608 self.check_src_roundtrip("a[*a, *a]") 609 self.check_src_roundtrip("a[1, *a, *a]") 610 self.check_src_roundtrip("a[*a, 1, *a]") 611 self.check_src_roundtrip("a[*a, *a, 1]") 612 self.check_src_roundtrip("a[1, *a, *a, 2]") 613 self.check_src_roundtrip("a[1:2, *a]") 614 self.check_src_roundtrip("a[*a, 1:2]") 615 616 def test_lambda_parameters(self): 617 self.check_src_roundtrip("lambda: something") 618 self.check_src_roundtrip("four = lambda: 2 + 2") 619 self.check_src_roundtrip("lambda x: x * 2") 620 self.check_src_roundtrip("square = lambda n: n ** 2") 621 self.check_src_roundtrip("lambda x, y: x + y") 622 self.check_src_roundtrip("add = lambda x, y: x + y") 623 self.check_src_roundtrip("lambda x, y, /, z, q, *, u: None") 624 self.check_src_roundtrip("lambda x, *y, **z: None") 625 626 def test_star_expr_assign_target(self): 627 for source_type, source in [ 628 ("single assignment", "{target} = foo"), 629 ("multiple assignment", "{target} = {target} = bar"), 630 ("for loop", "for {target} in foo:\n pass"), 631 ("async for loop", "async for {target} in foo:\n pass") 632 ]: 633 for target in [ 634 "a", 635 "a,", 636 "a, b", 637 "a, *b, c", 638 "a, (b, c), d", 639 "a, (b, c, d), *e", 640 "a, (b, *c, d), e", 641 "a, (b, *c, (d, e), f), g", 642 "[a]", 643 "[a, b]", 644 "[a, *b, c]", 645 "[a, [b, c], d]", 646 "[a, [b, c, d], *e]", 647 "[a, [b, *c, d], e]", 648 "[a, [b, *c, [d, e], f], g]", 649 "a, [b, c], d", 650 "[a, b, (c, d), (e, f)]", 651 "a, b, [*c], d, e" 652 ]: 653 with self.subTest(source_type=source_type, target=target): 654 self.check_src_roundtrip(source.format(target=target)) 655 656 def test_star_expr_assign_target_multiple(self): 657 self.check_src_roundtrip("() = []") 658 self.check_src_roundtrip("[] = ()") 659 self.check_src_roundtrip("() = [a] = c, = [d] = e, f = () = g = h") 660 self.check_src_roundtrip("a = b = c = d") 661 self.check_src_roundtrip("a, b = c, d = e, f = g") 662 self.check_src_roundtrip("[a, b] = [c, d] = [e, f] = g") 663 self.check_src_roundtrip("a, b = [c, d] = e, f = g") 664 665 666 667class DirectoryTestCase(ASTTestCase): 668 """Test roundtrip behaviour on all files in Lib and Lib/test.""" 669 670 lib_dir = pathlib.Path(__file__).parent / ".." 671 test_directories = (lib_dir, lib_dir / "test") 672 run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py", 673 "test_ast.py", "test_asdl_parser.py", "test_fstring.py", 674 "test_patma.py"} 675 676 _files_to_test = None 677 678 @classmethod 679 def files_to_test(cls): 680 681 if cls._files_to_test is not None: 682 return cls._files_to_test 683 684 items = [ 685 item.resolve() 686 for directory in cls.test_directories 687 for item in directory.glob("*.py") 688 if not item.name.startswith("bad") 689 ] 690 691 # Test limited subset of files unless the 'cpu' resource is specified. 692 if not test.support.is_resource_enabled("cpu"): 693 694 tests_to_run_always = {item for item in items if 695 item.name in cls.run_always_files} 696 697 items = set(random.sample(items, 10)) 698 699 # Make sure that at least tests that heavily use grammar features are 700 # always considered in order to reduce the chance of missing something. 701 items = list(items | tests_to_run_always) 702 703 # bpo-31174: Store the names sample to always test the same files. 704 # It prevents false alarms when hunting reference leaks. 705 cls._files_to_test = items 706 707 return items 708 709 def test_files(self): 710 for item in self.files_to_test(): 711 if test.support.verbose: 712 print(f"Testing {item.absolute()}") 713 714 with self.subTest(filename=item): 715 source = read_pyfile(item) 716 self.check_ast_roundtrip(source) 717 718 719if __name__ == "__main__": 720 unittest.main() 721