Zachary Ware | 2b0a610 | 2014-07-16 14:26:09 -0500 | [diff] [blame] | 1 | """Tests for the unparse.py script in the Tools/parser directory.""" |
| 2 | |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 3 | import unittest |
| 4 | import test.support |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 5 | import pathlib |
Mark Dickinson | be4fb69 | 2012-06-23 09:27:47 +0100 | [diff] [blame] | 6 | import random |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 7 | import tokenize |
Mark Dickinson | be4fb69 | 2012-06-23 09:27:47 +0100 | [diff] [blame] | 8 | import ast |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 9 | |
Zachary Ware | 2b0a610 | 2014-07-16 14:26:09 -0500 | [diff] [blame] | 10 | |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 11 | def read_pyfile(filename): |
| 12 | """Read and return the contents of a Python source file (as a |
| 13 | string), taking into account the file encoding.""" |
Hakan Çelik | 6a5bf15 | 2020-04-16 13:11:55 +0300 | [diff] [blame] | 14 | with tokenize.open(filename) as stream: |
| 15 | return stream.read() |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 16 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 17 | |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 18 | for_else = """\ |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 19 | def f(): |
| 20 | for x in range(10): |
| 21 | break |
| 22 | else: |
| 23 | y = 2 |
| 24 | z = 3 |
| 25 | """ |
| 26 | |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 27 | while_else = """\ |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 28 | def g(): |
| 29 | while True: |
| 30 | break |
| 31 | else: |
| 32 | y = 2 |
| 33 | z = 3 |
| 34 | """ |
| 35 | |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 36 | relative_import = """\ |
| 37 | from . import fred |
| 38 | from .. import barney |
| 39 | from .australia import shrimp as prawns |
| 40 | """ |
| 41 | |
| 42 | nonlocal_ex = """\ |
| 43 | def 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 ...' |
| 54 | raise_from = """\ |
| 55 | try: |
| 56 | 1 / 0 |
| 57 | except ZeroDivisionError as e: |
| 58 | raise ArithmeticError from e |
| 59 | """ |
| 60 | |
| 61 | class_decorator = """\ |
| 62 | @f1(arg) |
| 63 | @f2 |
| 64 | class Foo: pass |
| 65 | """ |
| 66 | |
Mark Dickinson | 8d6d760 | 2010-06-30 08:32:11 +0000 | [diff] [blame] | 67 | elif1 = """\ |
| 68 | if cond1: |
| 69 | suite1 |
| 70 | elif cond2: |
| 71 | suite2 |
| 72 | else: |
| 73 | suite3 |
| 74 | """ |
| 75 | |
| 76 | elif2 = """\ |
| 77 | if cond1: |
| 78 | suite1 |
| 79 | elif cond2: |
| 80 | suite2 |
| 81 | """ |
| 82 | |
Mark Dickinson | 81ad8cc | 2010-06-30 08:46:53 +0000 | [diff] [blame] | 83 | try_except_finally = """\ |
| 84 | try: |
| 85 | suite1 |
| 86 | except ex1: |
| 87 | suite2 |
| 88 | except ex2: |
| 89 | suite3 |
| 90 | else: |
| 91 | suite4 |
| 92 | finally: |
| 93 | suite5 |
| 94 | """ |
Mark Dickinson | 8d6d760 | 2010-06-30 08:32:11 +0000 | [diff] [blame] | 95 | |
Mark Dickinson | fe8440a | 2012-05-06 17:35:19 +0100 | [diff] [blame] | 96 | with_simple = """\ |
| 97 | with f(): |
| 98 | suite1 |
| 99 | """ |
| 100 | |
| 101 | with_as = """\ |
| 102 | with f() as x: |
| 103 | suite1 |
| 104 | """ |
| 105 | |
| 106 | with_two_items = """\ |
| 107 | with f() as x, g() as y: |
| 108 | suite1 |
| 109 | """ |
| 110 | |
Batuhan Taskaya | dff92bb | 2020-05-17 02:04:12 +0300 | [diff] [blame] | 111 | docstring_prefixes = ( |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 112 | "", |
Batuhan Taskaya | 25160cd | 2020-05-17 00:53:25 +0300 | [diff] [blame] | 113 | "class foo:\n ", |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 114 | "def foo():\n ", |
| 115 | "async def foo():\n ", |
Batuhan Taskaya | dff92bb | 2020-05-17 02:04:12 +0300 | [diff] [blame] | 116 | ) |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 117 | |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 118 | class ASTTestCase(unittest.TestCase): |
| 119 | def assertASTEqual(self, ast1, ast2): |
| 120 | self.assertEqual(ast.dump(ast1), ast.dump(ast2)) |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 121 | |
Batuhan Taşkaya | 5b66ec1 | 2020-03-15 22:56:57 +0300 | [diff] [blame] | 122 | def check_ast_roundtrip(self, code1, **kwargs): |
Pablo Galindo | 6341fc7 | 2020-05-17 03:53:57 +0100 | [diff] [blame] | 123 | with self.subTest(code1=code1, ast_parse_kwargs=kwargs): |
| 124 | ast1 = ast.parse(code1, **kwargs) |
| 125 | code2 = ast.unparse(ast1) |
| 126 | ast2 = ast.parse(code2, **kwargs) |
| 127 | self.assertASTEqual(ast1, ast2) |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 128 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 129 | def check_invalid(self, node, raises=ValueError): |
Pablo Galindo | 6341fc7 | 2020-05-17 03:53:57 +0100 | [diff] [blame] | 130 | with self.subTest(node=node): |
| 131 | self.assertRaises(raises, ast.unparse, node) |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 132 | |
Batuhan Taskaya | 493bf1c | 2020-05-03 20:11:51 +0300 | [diff] [blame] | 133 | def get_source(self, code1, code2=None): |
Batuhan Taşkaya | 397b96f | 2020-03-01 23:12:17 +0300 | [diff] [blame] | 134 | code2 = code2 or code1 |
| 135 | code1 = ast.unparse(ast.parse(code1)) |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 136 | return code1, code2 |
| 137 | |
Batuhan Taskaya | 493bf1c | 2020-05-03 20:11:51 +0300 | [diff] [blame] | 138 | def check_src_roundtrip(self, code1, code2=None): |
| 139 | code1, code2 = self.get_source(code1, code2) |
Pablo Galindo | 6341fc7 | 2020-05-17 03:53:57 +0100 | [diff] [blame] | 140 | with self.subTest(code1=code1, code2=code2): |
| 141 | self.assertEqual(code2, code1) |
Batuhan Taşkaya | 397b96f | 2020-03-01 23:12:17 +0300 | [diff] [blame] | 142 | |
Batuhan Taskaya | 493bf1c | 2020-05-03 20:11:51 +0300 | [diff] [blame] | 143 | def check_src_dont_roundtrip(self, code1, code2=None): |
| 144 | code1, code2 = self.get_source(code1, code2) |
Pablo Galindo | 6341fc7 | 2020-05-17 03:53:57 +0100 | [diff] [blame] | 145 | with self.subTest(code1=code1, code2=code2): |
| 146 | self.assertNotEqual(code2, code1) |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 147 | |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 148 | class UnparseTestCase(ASTTestCase): |
| 149 | # Tests for specific bugs found in earlier versions of unparse |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 150 | |
Eric V. Smith | 608adf9 | 2015-09-20 15:09:15 -0400 | [diff] [blame] | 151 | def test_fstrings(self): |
| 152 | # See issue 25180 |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 153 | self.check_ast_roundtrip(r"""f'{f"{0}"*3}'""") |
| 154 | self.check_ast_roundtrip(r"""f'{f"{y}"*3}'""") |
Shantanu | a993e90 | 2020-11-20 13:16:42 -0800 | [diff] [blame] | 155 | self.check_ast_roundtrip("""f''""") |
| 156 | self.check_ast_roundtrip('''f"""'end' "quote\\""""''') |
| 157 | |
| 158 | def test_fstrings_complicated(self): |
| 159 | # See issue 28002 |
| 160 | self.check_ast_roundtrip("""f'''{"'"}'''""") |
| 161 | self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') |
| 162 | self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-'single quote\\'\'\'\'''') |
| 163 | self.check_ast_roundtrip('f"""{\'\'\'\n\'\'\'}"""') |
| 164 | self.check_ast_roundtrip('f"""{g(\'\'\'\n\'\'\')}"""') |
| 165 | self.check_ast_roundtrip('''f"a\\r\\nb"''') |
| 166 | self.check_ast_roundtrip('''f"\\u2028{'x'}"''') |
Eric V. Smith | 608adf9 | 2015-09-20 15:09:15 -0400 | [diff] [blame] | 167 | |
Chih-Hsuan Yen | aaf47ca | 2019-05-27 01:08:20 +0800 | [diff] [blame] | 168 | def test_strings(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 169 | self.check_ast_roundtrip("u'foo'") |
| 170 | self.check_ast_roundtrip("r'foo'") |
| 171 | self.check_ast_roundtrip("b'foo'") |
Chih-Hsuan Yen | aaf47ca | 2019-05-27 01:08:20 +0800 | [diff] [blame] | 172 | |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 173 | def test_del_statement(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 174 | self.check_ast_roundtrip("del x, y, z") |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 175 | |
| 176 | def test_shifts(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 177 | self.check_ast_roundtrip("45 << 2") |
| 178 | self.check_ast_roundtrip("13 >> 7") |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 179 | |
| 180 | def test_for_else(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 181 | self.check_ast_roundtrip(for_else) |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 182 | |
| 183 | def test_while_else(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 184 | self.check_ast_roundtrip(while_else) |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 185 | |
| 186 | def test_unary_parens(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 187 | self.check_ast_roundtrip("(-1)**7") |
| 188 | self.check_ast_roundtrip("(-1.)**8") |
| 189 | self.check_ast_roundtrip("(-1j)**6") |
| 190 | self.check_ast_roundtrip("not True or False") |
| 191 | self.check_ast_roundtrip("True or not False") |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 192 | |
Mark Dickinson | 3eb0290 | 2010-06-29 08:52:36 +0000 | [diff] [blame] | 193 | def test_integer_parens(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 194 | self.check_ast_roundtrip("3 .__abs__()") |
Mark Dickinson | 3eb0290 | 2010-06-29 08:52:36 +0000 | [diff] [blame] | 195 | |
Mark Dickinson | 8042e28 | 2010-06-29 10:01:48 +0000 | [diff] [blame] | 196 | def test_huge_float(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 197 | self.check_ast_roundtrip("1e1000") |
| 198 | self.check_ast_roundtrip("-1e1000") |
| 199 | self.check_ast_roundtrip("1e1000j") |
| 200 | self.check_ast_roundtrip("-1e1000j") |
Mark Dickinson | cba8c10 | 2010-06-30 11:45:53 +0000 | [diff] [blame] | 201 | |
Kodi Arfer | 08ff436 | 2021-03-18 13:36:06 -0400 | [diff] [blame] | 202 | def test_nan(self): |
| 203 | self.assertASTEqual( |
| 204 | ast.parse(ast.unparse(ast.Constant(value=float('nan')))), |
| 205 | ast.parse('1e1000 - 1e1000') |
| 206 | ) |
| 207 | |
Mark Dickinson | cba8c10 | 2010-06-30 11:45:53 +0000 | [diff] [blame] | 208 | def test_min_int(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 209 | self.check_ast_roundtrip(str(-(2 ** 31))) |
| 210 | self.check_ast_roundtrip(str(-(2 ** 63))) |
Mark Dickinson | cba8c10 | 2010-06-30 11:45:53 +0000 | [diff] [blame] | 211 | |
| 212 | def test_imaginary_literals(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 213 | self.check_ast_roundtrip("7j") |
| 214 | self.check_ast_roundtrip("-7j") |
| 215 | self.check_ast_roundtrip("0j") |
| 216 | self.check_ast_roundtrip("-0j") |
Mark Dickinson | 8042e28 | 2010-06-29 10:01:48 +0000 | [diff] [blame] | 217 | |
| 218 | def test_lambda_parentheses(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 219 | self.check_ast_roundtrip("(lambda: int)()") |
Mark Dickinson | 8042e28 | 2010-06-29 10:01:48 +0000 | [diff] [blame] | 220 | |
Mark Dickinson | f5451e5 | 2010-06-28 20:09:18 +0000 | [diff] [blame] | 221 | def test_chained_comparisons(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 222 | self.check_ast_roundtrip("1 < 4 <= 5") |
| 223 | self.check_ast_roundtrip("a is b is c is not d") |
Mark Dickinson | f5451e5 | 2010-06-28 20:09:18 +0000 | [diff] [blame] | 224 | |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 225 | def test_function_arguments(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 226 | self.check_ast_roundtrip("def f(): pass") |
| 227 | self.check_ast_roundtrip("def f(a): pass") |
| 228 | self.check_ast_roundtrip("def f(b = 2): pass") |
| 229 | self.check_ast_roundtrip("def f(a, b): pass") |
| 230 | self.check_ast_roundtrip("def f(a, b = 2): pass") |
| 231 | self.check_ast_roundtrip("def f(a = 5, b = 2): pass") |
| 232 | self.check_ast_roundtrip("def f(*, a = 1, b = 2): pass") |
| 233 | self.check_ast_roundtrip("def f(*, a = 1, b): pass") |
| 234 | self.check_ast_roundtrip("def f(*, a, b = 2): pass") |
| 235 | self.check_ast_roundtrip("def f(a, b = None, *, c, **kwds): pass") |
| 236 | self.check_ast_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass") |
| 237 | self.check_ast_roundtrip("def f(*args, **kwargs): pass") |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 238 | |
| 239 | def test_relative_import(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 240 | self.check_ast_roundtrip(relative_import) |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 241 | |
| 242 | def test_nonlocal(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 243 | self.check_ast_roundtrip(nonlocal_ex) |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 244 | |
| 245 | def test_raise_from(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 246 | self.check_ast_roundtrip(raise_from) |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 247 | |
| 248 | def test_bytes(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 249 | self.check_ast_roundtrip("b'123'") |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 250 | |
| 251 | def test_annotations(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 252 | self.check_ast_roundtrip("def f(a : int): pass") |
| 253 | self.check_ast_roundtrip("def f(a: int = 5): pass") |
| 254 | self.check_ast_roundtrip("def f(*args: [int]): pass") |
| 255 | self.check_ast_roundtrip("def f(**kwargs: dict): pass") |
| 256 | self.check_ast_roundtrip("def f() -> None: pass") |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 257 | |
| 258 | def test_set_literal(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 259 | self.check_ast_roundtrip("{'a', 'b', 'c'}") |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 260 | |
Kodi Arfer | 08ff436 | 2021-03-18 13:36:06 -0400 | [diff] [blame] | 261 | def test_empty_set(self): |
| 262 | self.assertASTEqual( |
| 263 | ast.parse(ast.unparse(ast.Set(elts=[]))), |
| 264 | ast.parse('{*()}') |
| 265 | ) |
| 266 | |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 267 | def test_set_comprehension(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 268 | self.check_ast_roundtrip("{x for x in range(5)}") |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 269 | |
| 270 | def test_dict_comprehension(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 271 | self.check_ast_roundtrip("{x: x*x for x in range(10)}") |
Mark Dickinson | fa2e4e9 | 2010-06-28 21:14:17 +0000 | [diff] [blame] | 272 | |
| 273 | def test_class_decorators(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 274 | self.check_ast_roundtrip(class_decorator) |
Mark Dickinson | ae10005 | 2010-06-28 19:44:20 +0000 | [diff] [blame] | 275 | |
Mark Dickinson | 578aa56 | 2010-06-29 18:38:59 +0000 | [diff] [blame] | 276 | def test_class_definition(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 277 | self.check_ast_roundtrip("class A(metaclass=type, *[], **{}): pass") |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 278 | |
Mark Dickinson | 8d6d760 | 2010-06-30 08:32:11 +0000 | [diff] [blame] | 279 | def test_elifs(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 280 | self.check_ast_roundtrip(elif1) |
| 281 | self.check_ast_roundtrip(elif2) |
Mark Dickinson | 8d6d760 | 2010-06-30 08:32:11 +0000 | [diff] [blame] | 282 | |
Mark Dickinson | 81ad8cc | 2010-06-30 08:46:53 +0000 | [diff] [blame] | 283 | def test_try_except_finally(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 284 | self.check_ast_roundtrip(try_except_finally) |
Mark Dickinson | 81ad8cc | 2010-06-30 08:46:53 +0000 | [diff] [blame] | 285 | |
Mark Dickinson | 1b2e944 | 2012-05-06 17:27:39 +0100 | [diff] [blame] | 286 | def test_starred_assignment(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 287 | self.check_ast_roundtrip("a, *b, c = seq") |
| 288 | self.check_ast_roundtrip("a, (*b, c) = seq") |
| 289 | self.check_ast_roundtrip("a, *b[0], c = seq") |
| 290 | self.check_ast_roundtrip("a, *(b, c) = seq") |
Mark Dickinson | 1b2e944 | 2012-05-06 17:27:39 +0100 | [diff] [blame] | 291 | |
Mark Dickinson | fe8440a | 2012-05-06 17:35:19 +0100 | [diff] [blame] | 292 | def test_with_simple(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 293 | self.check_ast_roundtrip(with_simple) |
Mark Dickinson | fe8440a | 2012-05-06 17:35:19 +0100 | [diff] [blame] | 294 | |
| 295 | def test_with_as(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 296 | self.check_ast_roundtrip(with_as) |
Mark Dickinson | fe8440a | 2012-05-06 17:35:19 +0100 | [diff] [blame] | 297 | |
| 298 | def test_with_two_items(self): |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 299 | self.check_ast_roundtrip(with_two_items) |
Mark Dickinson | fe8440a | 2012-05-06 17:35:19 +0100 | [diff] [blame] | 300 | |
Berker Peksag | d66dd5c | 2016-03-06 16:50:15 +0200 | [diff] [blame] | 301 | def test_dict_unpacking_in_dict(self): |
| 302 | # See issue 26489 |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 303 | self.check_ast_roundtrip(r"""{**{'y': 2}, 'x': 1}""") |
| 304 | self.check_ast_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""") |
Berker Peksag | d66dd5c | 2016-03-06 16:50:15 +0200 | [diff] [blame] | 305 | |
Batuhan Taskaya | c102a14 | 2020-05-18 23:48:49 +0300 | [diff] [blame] | 306 | def test_slices(self): |
Batuhan Taşkaya | e7cab7f | 2020-03-09 23:27:03 +0300 | [diff] [blame] | 307 | self.check_ast_roundtrip("a[i]") |
| 308 | self.check_ast_roundtrip("a[i,]") |
| 309 | self.check_ast_roundtrip("a[i, j]") |
Batuhan Taskaya | c102a14 | 2020-05-18 23:48:49 +0300 | [diff] [blame] | 310 | self.check_ast_roundtrip("a[(*a,)]") |
| 311 | self.check_ast_roundtrip("a[(a:=b)]") |
| 312 | self.check_ast_roundtrip("a[(a:=b,c)]") |
Batuhan Taşkaya | e7cab7f | 2020-03-09 23:27:03 +0300 | [diff] [blame] | 313 | self.check_ast_roundtrip("a[()]") |
| 314 | self.check_ast_roundtrip("a[i:j]") |
| 315 | self.check_ast_roundtrip("a[:j]") |
| 316 | self.check_ast_roundtrip("a[i:]") |
| 317 | self.check_ast_roundtrip("a[i:j:k]") |
| 318 | self.check_ast_roundtrip("a[:j:k]") |
| 319 | self.check_ast_roundtrip("a[i::k]") |
| 320 | self.check_ast_roundtrip("a[i:j,]") |
| 321 | self.check_ast_roundtrip("a[i:j, k]") |
| 322 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 323 | def test_invalid_raise(self): |
| 324 | self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X"))) |
| 325 | |
| 326 | def test_invalid_fstring_constant(self): |
| 327 | self.check_invalid(ast.JoinedStr(values=[ast.Constant(value=100)])) |
| 328 | |
| 329 | def test_invalid_fstring_conversion(self): |
| 330 | self.check_invalid( |
| 331 | ast.FormattedValue( |
| 332 | value=ast.Constant(value="a", kind=None), |
| 333 | conversion=ord("Y"), # random character |
| 334 | format_spec=None, |
| 335 | ) |
| 336 | ) |
| 337 | |
Shantanu | a993e90 | 2020-11-20 13:16:42 -0800 | [diff] [blame] | 338 | def test_invalid_fstring_backslash(self): |
| 339 | self.check_invalid(ast.FormattedValue(value=ast.Constant(value="\\\\"))) |
| 340 | |
Batuhan Taşkaya | 7b35bef | 2020-01-02 21:20:04 +0300 | [diff] [blame] | 341 | def test_invalid_yield_from(self): |
| 342 | self.check_invalid(ast.YieldFrom(value=None)) |
Mark Dickinson | 1b2e944 | 2012-05-06 17:27:39 +0100 | [diff] [blame] | 343 | |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 344 | def test_docstrings(self): |
| 345 | docstrings = ( |
| 346 | 'this ends with double quote"', |
Batuhan Taskaya | e966af7 | 2020-05-17 01:49:07 +0300 | [diff] [blame] | 347 | 'this includes a """triple quote"""', |
| 348 | '\r', |
| 349 | '\\r', |
| 350 | '\t', |
| 351 | '\\t', |
| 352 | '\n', |
| 353 | '\\n', |
CyberSaxosTiGER | d71a649 | 2020-05-18 21:41:35 +0300 | [diff] [blame] | 354 | '\r\\r\t\\t\n\\n', |
| 355 | '""">>> content = \"\"\"blabla\"\"\" <<<"""', |
| 356 | r'foo\n\x00', |
Shantanu | a993e90 | 2020-11-20 13:16:42 -0800 | [diff] [blame] | 357 | "' \\'\\'\\'\"\"\" \"\"\\'\\' \\'", |
| 358 | '🐍⛎𩸽üéş^\\\\X\\\\BB\N{LONG RIGHTWARDS SQUIGGLE ARROW}' |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 359 | ) |
| 360 | for docstring in docstrings: |
| 361 | # check as Module docstrings for easy testing |
Batuhan Taskaya | e966af7 | 2020-05-17 01:49:07 +0300 | [diff] [blame] | 362 | self.check_ast_roundtrip(f"'''{docstring}'''") |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 363 | |
Batuhan Taşkaya | e7cab7f | 2020-03-09 23:27:03 +0300 | [diff] [blame] | 364 | def test_constant_tuples(self): |
| 365 | self.check_src_roundtrip(ast.Constant(value=(1,), kind=None), "(1,)") |
| 366 | self.check_src_roundtrip( |
| 367 | ast.Constant(value=(1, 2, 3), kind=None), "(1, 2, 3)" |
| 368 | ) |
| 369 | |
Batuhan Taşkaya | 5b66ec1 | 2020-03-15 22:56:57 +0300 | [diff] [blame] | 370 | def test_function_type(self): |
| 371 | for function_type in ( |
| 372 | "() -> int", |
| 373 | "(int, int) -> int", |
| 374 | "(Callable[complex], More[Complex(call.to_typevar())]) -> None" |
| 375 | ): |
| 376 | self.check_ast_roundtrip(function_type, mode="func_type") |
| 377 | |
Batuhan Taskaya | dff92bb | 2020-05-17 02:04:12 +0300 | [diff] [blame] | 378 | def test_type_comments(self): |
| 379 | for statement in ( |
| 380 | "a = 5 # type:", |
| 381 | "a = 5 # type: int", |
| 382 | "a = 5 # type: int and more", |
| 383 | "def x(): # type: () -> None\n\tpass", |
| 384 | "def x(y): # type: (int) -> None and more\n\tpass", |
| 385 | "async def x(): # type: () -> None\n\tpass", |
| 386 | "async def x(y): # type: (int) -> None and more\n\tpass", |
| 387 | "for x in y: # type: int\n\tpass", |
| 388 | "async for x in y: # type: int\n\tpass", |
| 389 | "with x(): # type: int\n\tpass", |
| 390 | "async with x(): # type: int\n\tpass" |
| 391 | ): |
| 392 | self.check_ast_roundtrip(statement, type_comments=True) |
| 393 | |
| 394 | def test_type_ignore(self): |
| 395 | for statement in ( |
| 396 | "a = 5 # type: ignore", |
| 397 | "a = 5 # type: ignore and more", |
| 398 | "def x(): # type: ignore\n\tpass", |
| 399 | "def x(y): # type: ignore and more\n\tpass", |
| 400 | "async def x(): # type: ignore\n\tpass", |
| 401 | "async def x(y): # type: ignore and more\n\tpass", |
| 402 | "for x in y: # type: ignore\n\tpass", |
| 403 | "async for x in y: # type: ignore\n\tpass", |
| 404 | "with x(): # type: ignore\n\tpass", |
| 405 | "async with x(): # type: ignore\n\tpass" |
| 406 | ): |
| 407 | self.check_ast_roundtrip(statement, type_comments=True) |
| 408 | |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 409 | |
Batuhan Taşkaya | 397b96f | 2020-03-01 23:12:17 +0300 | [diff] [blame] | 410 | class CosmeticTestCase(ASTTestCase): |
| 411 | """Test if there are cosmetic issues caused by unnecesary additions""" |
| 412 | |
| 413 | def test_simple_expressions_parens(self): |
| 414 | self.check_src_roundtrip("(a := b)") |
| 415 | self.check_src_roundtrip("await x") |
| 416 | self.check_src_roundtrip("x if x else y") |
| 417 | self.check_src_roundtrip("lambda x: x") |
| 418 | self.check_src_roundtrip("1 + 1") |
| 419 | self.check_src_roundtrip("1 + 2 / 3") |
| 420 | self.check_src_roundtrip("(1 + 2) / 3") |
| 421 | self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2)") |
| 422 | self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2) ** 2") |
Batuhan Taskaya | ce4a753 | 2020-05-17 00:46:11 +0300 | [diff] [blame] | 423 | self.check_src_roundtrip("~x") |
Batuhan Taşkaya | 397b96f | 2020-03-01 23:12:17 +0300 | [diff] [blame] | 424 | self.check_src_roundtrip("x and y") |
| 425 | self.check_src_roundtrip("x and y and z") |
| 426 | self.check_src_roundtrip("x and (y and x)") |
| 427 | self.check_src_roundtrip("(x and y) and z") |
| 428 | self.check_src_roundtrip("(x ** y) ** z ** q") |
| 429 | self.check_src_roundtrip("x >> y") |
| 430 | self.check_src_roundtrip("x << y") |
| 431 | self.check_src_roundtrip("x >> y and x >> z") |
| 432 | self.check_src_roundtrip("x + y - z * q ^ t ** k") |
| 433 | self.check_src_roundtrip("P * V if P and V else n * R * T") |
| 434 | self.check_src_roundtrip("lambda P, V, n: P * V == n * R * T") |
| 435 | self.check_src_roundtrip("flag & (other | foo)") |
| 436 | self.check_src_roundtrip("not x == y") |
| 437 | self.check_src_roundtrip("x == (not y)") |
| 438 | self.check_src_roundtrip("yield x") |
| 439 | self.check_src_roundtrip("yield from x") |
| 440 | self.check_src_roundtrip("call((yield x))") |
| 441 | self.check_src_roundtrip("return x + (yield x)") |
| 442 | |
Batuhan Taskaya | 25160cd | 2020-05-17 00:53:25 +0300 | [diff] [blame] | 443 | def test_class_bases_and_keywords(self): |
| 444 | self.check_src_roundtrip("class X:\n pass") |
| 445 | self.check_src_roundtrip("class X(A):\n pass") |
| 446 | self.check_src_roundtrip("class X(A, B, C, D):\n pass") |
| 447 | self.check_src_roundtrip("class X(x=y):\n pass") |
| 448 | self.check_src_roundtrip("class X(metaclass=z):\n pass") |
| 449 | self.check_src_roundtrip("class X(x=y, z=d):\n pass") |
| 450 | self.check_src_roundtrip("class X(A, x=y):\n pass") |
| 451 | self.check_src_roundtrip("class X(A, **kw):\n pass") |
| 452 | self.check_src_roundtrip("class X(*args):\n pass") |
| 453 | self.check_src_roundtrip("class X(*args, **kwargs):\n pass") |
| 454 | |
Shantanu | a993e90 | 2020-11-20 13:16:42 -0800 | [diff] [blame] | 455 | def test_fstrings(self): |
| 456 | self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') |
| 457 | self.check_src_roundtrip('''f"\\u2028{'x'}"''') |
| 458 | self.check_src_roundtrip(r"f'{x}\n'") |
| 459 | self.check_src_roundtrip('''f''\'{"""\n"""}\\n''\'''') |
| 460 | self.check_src_roundtrip('''f''\'{f"""{x}\n"""}\\n''\'''') |
| 461 | |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 462 | def test_docstrings(self): |
| 463 | docstrings = ( |
| 464 | '"""simple doc string"""', |
| 465 | '''"""A more complex one |
| 466 | with some newlines"""''', |
| 467 | '''"""Foo bar baz |
| 468 | |
| 469 | empty newline"""''', |
| 470 | '"""With some \t"""', |
| 471 | '"""Foo "bar" baz """', |
Batuhan Taskaya | e966af7 | 2020-05-17 01:49:07 +0300 | [diff] [blame] | 472 | '"""\\r"""', |
| 473 | '""""""', |
| 474 | '"""\'\'\'"""', |
| 475 | '"""\'\'\'\'\'\'"""', |
Shantanu | a993e90 | 2020-11-20 13:16:42 -0800 | [diff] [blame] | 476 | '"""🐍⛎𩸽üéş^\\\\X\\\\BB⟿"""', |
| 477 | '"""end in single \'quote\'"""', |
| 478 | "'''end in double \"quote\"'''", |
| 479 | '"""almost end in double "quote"."""', |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 480 | ) |
| 481 | |
| 482 | for prefix in docstring_prefixes: |
| 483 | for docstring in docstrings: |
| 484 | self.check_src_roundtrip(f"{prefix}{docstring}") |
| 485 | |
| 486 | def test_docstrings_negative_cases(self): |
| 487 | # Test some cases that involve strings in the children of the |
| 488 | # first node but aren't docstrings to make sure we don't have |
| 489 | # False positives. |
| 490 | docstrings_negative = ( |
| 491 | 'a = """false"""', |
| 492 | '"""false""" + """unless its optimized"""', |
| 493 | '1 + 1\n"""false"""', |
| 494 | 'f"""no, top level but f-fstring"""' |
| 495 | ) |
| 496 | for prefix in docstring_prefixes: |
| 497 | for negative in docstrings_negative: |
| 498 | # this cases should be result with single quote |
| 499 | # rather then triple quoted docstring |
| 500 | src = f"{prefix}{negative}" |
| 501 | self.check_ast_roundtrip(src) |
| 502 | self.check_src_dont_roundtrip(src) |
Batuhan Taşkaya | 397b96f | 2020-03-01 23:12:17 +0300 | [diff] [blame] | 503 | |
Batuhan Taskaya | ce4a753 | 2020-05-17 00:46:11 +0300 | [diff] [blame] | 504 | def test_unary_op_factor(self): |
| 505 | for prefix in ("+", "-", "~"): |
| 506 | self.check_src_roundtrip(f"{prefix}1") |
| 507 | for prefix in ("not",): |
| 508 | self.check_src_roundtrip(f"{prefix} 1") |
| 509 | |
Batuhan Taskaya | c102a14 | 2020-05-18 23:48:49 +0300 | [diff] [blame] | 510 | def test_slices(self): |
| 511 | self.check_src_roundtrip("a[1]") |
| 512 | self.check_src_roundtrip("a[1, 2]") |
| 513 | self.check_src_roundtrip("a[(1, *a)]") |
| 514 | |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 515 | class DirectoryTestCase(ASTTestCase): |
| 516 | """Test roundtrip behaviour on all files in Lib and Lib/test.""" |
| 517 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 518 | lib_dir = pathlib.Path(__file__).parent / ".." |
| 519 | test_directories = (lib_dir, lib_dir / "test") |
Pablo Galindo | 23a226b | 2019-12-29 19:20:55 +0000 | [diff] [blame] | 520 | run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py", |
Nick Coghlan | 1e7b858 | 2021-04-29 15:58:44 +1000 | [diff] [blame^] | 521 | "test_ast.py", "test_asdl_parser.py", "test_fstring.py", |
| 522 | "test_patma.py"} |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 523 | |
Pablo Galindo | ac22911 | 2019-12-09 17:57:50 +0000 | [diff] [blame] | 524 | _files_to_test = None |
| 525 | |
| 526 | @classmethod |
| 527 | def files_to_test(cls): |
| 528 | |
| 529 | if cls._files_to_test is not None: |
| 530 | return cls._files_to_test |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 531 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 532 | items = [ |
| 533 | item.resolve() |
Pablo Galindo | ac22911 | 2019-12-09 17:57:50 +0000 | [diff] [blame] | 534 | for directory in cls.test_directories |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 535 | for item in directory.glob("*.py") |
| 536 | if not item.name.startswith("bad") |
| 537 | ] |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 538 | |
Mark Dickinson | be4fb69 | 2012-06-23 09:27:47 +0100 | [diff] [blame] | 539 | # Test limited subset of files unless the 'cpu' resource is specified. |
| 540 | if not test.support.is_resource_enabled("cpu"): |
Pablo Galindo | be287c3 | 2019-12-29 20:18:36 +0000 | [diff] [blame] | 541 | |
| 542 | tests_to_run_always = {item for item in items if |
| 543 | item.name in cls.run_always_files} |
| 544 | |
Pablo Galindo | 23a226b | 2019-12-29 19:20:55 +0000 | [diff] [blame] | 545 | items = set(random.sample(items, 10)) |
| 546 | |
Pablo Galindo | be287c3 | 2019-12-29 20:18:36 +0000 | [diff] [blame] | 547 | # Make sure that at least tests that heavily use grammar features are |
| 548 | # always considered in order to reduce the chance of missing something. |
| 549 | items = list(items | tests_to_run_always) |
Pablo Galindo | ac22911 | 2019-12-09 17:57:50 +0000 | [diff] [blame] | 550 | |
| 551 | # bpo-31174: Store the names sample to always test the same files. |
| 552 | # It prevents false alarms when hunting reference leaks. |
| 553 | cls._files_to_test = items |
| 554 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 555 | return items |
Victor Stinner | 8e482be | 2017-10-24 03:33:36 -0700 | [diff] [blame] | 556 | |
| 557 | def test_files(self): |
Pablo Galindo | ac22911 | 2019-12-09 17:57:50 +0000 | [diff] [blame] | 558 | for item in self.files_to_test(): |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 559 | if test.support.verbose: |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 560 | print(f"Testing {item.absolute()}") |
Eric V. Smith | 06cf601 | 2016-09-03 12:33:38 -0400 | [diff] [blame] | 561 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 562 | with self.subTest(filename=item): |
| 563 | source = read_pyfile(item) |
Batuhan Taşkaya | 89aa469 | 2020-03-02 21:59:01 +0300 | [diff] [blame] | 564 | self.check_ast_roundtrip(source) |
Mark Dickinson | d751c2e | 2010-06-29 14:08:23 +0000 | [diff] [blame] | 565 | |
| 566 | |
Pablo Galindo | 27fc3b6 | 2019-11-24 23:02:40 +0000 | [diff] [blame] | 567 | if __name__ == "__main__": |
Zachary Ware | 2b0a610 | 2014-07-16 14:26:09 -0500 | [diff] [blame] | 568 | unittest.main() |