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