blob: f7fcb2bffe891999d7d10e40442413ad9d4bcea8 [file] [log] [blame]
Zachary Ware2b0a6102014-07-16 14:26:09 -05001"""Tests for the unparse.py script in the Tools/parser directory."""
2
Mark Dickinsonae100052010-06-28 19:44:20 +00003import unittest
4import test.support
Pablo Galindo27fc3b62019-11-24 23:02:40 +00005import pathlib
Mark Dickinsonbe4fb692012-06-23 09:27:47 +01006import random
Mark Dickinsond751c2e2010-06-29 14:08:23 +00007import tokenize
Mark Dickinsonbe4fb692012-06-23 09:27:47 +01008import ast
Mark Dickinsonae100052010-06-28 19:44:20 +00009
Zachary Ware2b0a6102014-07-16 14:26:09 -050010
Mark Dickinsond751c2e2010-06-29 14:08:23 +000011def 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 open(filename, "rb") as pyfile:
15 encoding = tokenize.detect_encoding(pyfile.readline)[0]
16 with open(filename, "r", encoding=encoding) as pyfile:
17 source = pyfile.read()
18 return source
19
Pablo Galindo27fc3b62019-11-24 23:02:40 +000020
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +000021for_else = """\
Mark Dickinsonae100052010-06-28 19:44:20 +000022def f():
23 for x in range(10):
24 break
25 else:
26 y = 2
27 z = 3
28"""
29
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +000030while_else = """\
Mark Dickinsonae100052010-06-28 19:44:20 +000031def g():
32 while True:
33 break
34 else:
35 y = 2
36 z = 3
37"""
38
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +000039relative_import = """\
40from . import fred
41from .. import barney
42from .australia import shrimp as prawns
43"""
44
45nonlocal_ex = """\
46def f():
47 x = 1
48 def g():
49 nonlocal x
50 x = 2
51 y = 7
52 def h():
53 nonlocal x, y
54"""
55
56# also acts as test for 'except ... as ...'
57raise_from = """\
58try:
59 1 / 0
60except ZeroDivisionError as e:
61 raise ArithmeticError from e
62"""
63
64class_decorator = """\
65@f1(arg)
66@f2
67class Foo: pass
68"""
69
Mark Dickinson8d6d7602010-06-30 08:32:11 +000070elif1 = """\
71if cond1:
72 suite1
73elif cond2:
74 suite2
75else:
76 suite3
77"""
78
79elif2 = """\
80if cond1:
81 suite1
82elif cond2:
83 suite2
84"""
85
Mark Dickinson81ad8cc2010-06-30 08:46:53 +000086try_except_finally = """\
87try:
88 suite1
89except ex1:
90 suite2
91except ex2:
92 suite3
93else:
94 suite4
95finally:
96 suite5
97"""
Mark Dickinson8d6d7602010-06-30 08:32:11 +000098
Mark Dickinsonfe8440a2012-05-06 17:35:19 +010099with_simple = """\
100with f():
101 suite1
102"""
103
104with_as = """\
105with f() as x:
106 suite1
107"""
108
109with_two_items = """\
110with f() as x, g() as y:
111 suite1
112"""
113
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000114
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000115class ASTTestCase(unittest.TestCase):
116 def assertASTEqual(self, ast1, ast2):
117 self.assertEqual(ast.dump(ast1), ast.dump(ast2))
Mark Dickinsonae100052010-06-28 19:44:20 +0000118
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000119 def check_roundtrip(self, code1):
120 ast1 = ast.parse(code1)
121 code2 = ast.unparse(ast1)
122 ast2 = ast.parse(code2)
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000123 self.assertASTEqual(ast1, ast2)
124
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000125 def check_invalid(self, node, raises=ValueError):
126 self.assertRaises(raises, ast.unparse, node)
127
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300128 def check_src_roundtrip(self, code1, code2=None, strip=True):
129 code2 = code2 or code1
130 code1 = ast.unparse(ast.parse(code1))
131 if strip:
132 code1 = code1.strip()
133 self.assertEqual(code2, code1)
134
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000135
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000136class UnparseTestCase(ASTTestCase):
137 # Tests for specific bugs found in earlier versions of unparse
Mark Dickinsonae100052010-06-28 19:44:20 +0000138
Eric V. Smith608adf92015-09-20 15:09:15 -0400139 def test_fstrings(self):
140 # See issue 25180
141 self.check_roundtrip(r"""f'{f"{0}"*3}'""")
142 self.check_roundtrip(r"""f'{f"{y}"*3}'""")
Eric V. Smith608adf92015-09-20 15:09:15 -0400143
Chih-Hsuan Yenaaf47ca2019-05-27 01:08:20 +0800144 def test_strings(self):
145 self.check_roundtrip("u'foo'")
146 self.check_roundtrip("r'foo'")
147 self.check_roundtrip("b'foo'")
148
Mark Dickinsonae100052010-06-28 19:44:20 +0000149 def test_del_statement(self):
150 self.check_roundtrip("del x, y, z")
151
152 def test_shifts(self):
153 self.check_roundtrip("45 << 2")
154 self.check_roundtrip("13 >> 7")
155
156 def test_for_else(self):
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000157 self.check_roundtrip(for_else)
Mark Dickinsonae100052010-06-28 19:44:20 +0000158
159 def test_while_else(self):
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000160 self.check_roundtrip(while_else)
Mark Dickinsonae100052010-06-28 19:44:20 +0000161
162 def test_unary_parens(self):
163 self.check_roundtrip("(-1)**7")
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000164 self.check_roundtrip("(-1.)**8")
165 self.check_roundtrip("(-1j)**6")
Mark Dickinsonae100052010-06-28 19:44:20 +0000166 self.check_roundtrip("not True or False")
167 self.check_roundtrip("True or not False")
168
Mark Dickinson3eb02902010-06-29 08:52:36 +0000169 def test_integer_parens(self):
170 self.check_roundtrip("3 .__abs__()")
171
Mark Dickinson8042e282010-06-29 10:01:48 +0000172 def test_huge_float(self):
173 self.check_roundtrip("1e1000")
174 self.check_roundtrip("-1e1000")
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000175 self.check_roundtrip("1e1000j")
176 self.check_roundtrip("-1e1000j")
177
178 def test_min_int(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000179 self.check_roundtrip(str(-(2 ** 31)))
180 self.check_roundtrip(str(-(2 ** 63)))
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000181
182 def test_imaginary_literals(self):
183 self.check_roundtrip("7j")
184 self.check_roundtrip("-7j")
185 self.check_roundtrip("0j")
186 self.check_roundtrip("-0j")
Mark Dickinson8042e282010-06-29 10:01:48 +0000187
188 def test_lambda_parentheses(self):
189 self.check_roundtrip("(lambda: int)()")
190
Mark Dickinsonf5451e52010-06-28 20:09:18 +0000191 def test_chained_comparisons(self):
192 self.check_roundtrip("1 < 4 <= 5")
193 self.check_roundtrip("a is b is c is not d")
194
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000195 def test_function_arguments(self):
196 self.check_roundtrip("def f(): pass")
197 self.check_roundtrip("def f(a): pass")
198 self.check_roundtrip("def f(b = 2): pass")
199 self.check_roundtrip("def f(a, b): pass")
200 self.check_roundtrip("def f(a, b = 2): pass")
201 self.check_roundtrip("def f(a = 5, b = 2): pass")
202 self.check_roundtrip("def f(*, a = 1, b = 2): pass")
203 self.check_roundtrip("def f(*, a = 1, b): pass")
204 self.check_roundtrip("def f(*, a, b = 2): pass")
205 self.check_roundtrip("def f(a, b = None, *, c, **kwds): pass")
206 self.check_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass")
207 self.check_roundtrip("def f(*args, **kwargs): pass")
208
209 def test_relative_import(self):
210 self.check_roundtrip(relative_import)
211
212 def test_nonlocal(self):
213 self.check_roundtrip(nonlocal_ex)
214
215 def test_raise_from(self):
216 self.check_roundtrip(raise_from)
217
218 def test_bytes(self):
219 self.check_roundtrip("b'123'")
220
221 def test_annotations(self):
222 self.check_roundtrip("def f(a : int): pass")
223 self.check_roundtrip("def f(a: int = 5): pass")
224 self.check_roundtrip("def f(*args: [int]): pass")
225 self.check_roundtrip("def f(**kwargs: dict): pass")
226 self.check_roundtrip("def f() -> None: pass")
227
228 def test_set_literal(self):
229 self.check_roundtrip("{'a', 'b', 'c'}")
230
231 def test_set_comprehension(self):
232 self.check_roundtrip("{x for x in range(5)}")
233
234 def test_dict_comprehension(self):
235 self.check_roundtrip("{x: x*x for x in range(10)}")
236
237 def test_class_decorators(self):
238 self.check_roundtrip(class_decorator)
Mark Dickinsonae100052010-06-28 19:44:20 +0000239
Mark Dickinson578aa562010-06-29 18:38:59 +0000240 def test_class_definition(self):
241 self.check_roundtrip("class A(metaclass=type, *[], **{}): pass")
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000242
Mark Dickinson8d6d7602010-06-30 08:32:11 +0000243 def test_elifs(self):
244 self.check_roundtrip(elif1)
245 self.check_roundtrip(elif2)
246
Mark Dickinson81ad8cc2010-06-30 08:46:53 +0000247 def test_try_except_finally(self):
248 self.check_roundtrip(try_except_finally)
249
Mark Dickinson1b2e9442012-05-06 17:27:39 +0100250 def test_starred_assignment(self):
251 self.check_roundtrip("a, *b, c = seq")
252 self.check_roundtrip("a, (*b, c) = seq")
253 self.check_roundtrip("a, *b[0], c = seq")
254 self.check_roundtrip("a, *(b, c) = seq")
255
Mark Dickinsonfe8440a2012-05-06 17:35:19 +0100256 def test_with_simple(self):
257 self.check_roundtrip(with_simple)
258
259 def test_with_as(self):
260 self.check_roundtrip(with_as)
261
262 def test_with_two_items(self):
263 self.check_roundtrip(with_two_items)
264
Berker Peksagd66dd5c2016-03-06 16:50:15 +0200265 def test_dict_unpacking_in_dict(self):
266 # See issue 26489
267 self.check_roundtrip(r"""{**{'y': 2}, 'x': 1}""")
268 self.check_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""")
269
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000270 def test_invalid_raise(self):
271 self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X")))
272
273 def test_invalid_fstring_constant(self):
274 self.check_invalid(ast.JoinedStr(values=[ast.Constant(value=100)]))
275
276 def test_invalid_fstring_conversion(self):
277 self.check_invalid(
278 ast.FormattedValue(
279 value=ast.Constant(value="a", kind=None),
280 conversion=ord("Y"), # random character
281 format_spec=None,
282 )
283 )
284
285 def test_invalid_set(self):
286 self.check_invalid(ast.Set(elts=[]))
287
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300288 def test_invalid_yield_from(self):
289 self.check_invalid(ast.YieldFrom(value=None))
Mark Dickinson1b2e9442012-05-06 17:27:39 +0100290
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300291class CosmeticTestCase(ASTTestCase):
292 """Test if there are cosmetic issues caused by unnecesary additions"""
293
294 def test_simple_expressions_parens(self):
295 self.check_src_roundtrip("(a := b)")
296 self.check_src_roundtrip("await x")
297 self.check_src_roundtrip("x if x else y")
298 self.check_src_roundtrip("lambda x: x")
299 self.check_src_roundtrip("1 + 1")
300 self.check_src_roundtrip("1 + 2 / 3")
301 self.check_src_roundtrip("(1 + 2) / 3")
302 self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2)")
303 self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2) ** 2")
304 self.check_src_roundtrip("~ x")
305 self.check_src_roundtrip("x and y")
306 self.check_src_roundtrip("x and y and z")
307 self.check_src_roundtrip("x and (y and x)")
308 self.check_src_roundtrip("(x and y) and z")
309 self.check_src_roundtrip("(x ** y) ** z ** q")
310 self.check_src_roundtrip("x >> y")
311 self.check_src_roundtrip("x << y")
312 self.check_src_roundtrip("x >> y and x >> z")
313 self.check_src_roundtrip("x + y - z * q ^ t ** k")
314 self.check_src_roundtrip("P * V if P and V else n * R * T")
315 self.check_src_roundtrip("lambda P, V, n: P * V == n * R * T")
316 self.check_src_roundtrip("flag & (other | foo)")
317 self.check_src_roundtrip("not x == y")
318 self.check_src_roundtrip("x == (not y)")
319 self.check_src_roundtrip("yield x")
320 self.check_src_roundtrip("yield from x")
321 self.check_src_roundtrip("call((yield x))")
322 self.check_src_roundtrip("return x + (yield x)")
323
324
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000325class DirectoryTestCase(ASTTestCase):
326 """Test roundtrip behaviour on all files in Lib and Lib/test."""
327
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000328 lib_dir = pathlib.Path(__file__).parent / ".."
329 test_directories = (lib_dir, lib_dir / "test")
330 skip_files = {"test_fstring.py"}
Pablo Galindo23a226b2019-12-29 19:20:55 +0000331 run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py",
332 "test_ast.py", "test_asdl_parser.py"}
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000333
Pablo Galindoac229112019-12-09 17:57:50 +0000334 _files_to_test = None
335
336 @classmethod
337 def files_to_test(cls):
338
339 if cls._files_to_test is not None:
340 return cls._files_to_test
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000341
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000342 items = [
343 item.resolve()
Pablo Galindoac229112019-12-09 17:57:50 +0000344 for directory in cls.test_directories
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000345 for item in directory.glob("*.py")
346 if not item.name.startswith("bad")
347 ]
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000348
Mark Dickinsonbe4fb692012-06-23 09:27:47 +0100349 # Test limited subset of files unless the 'cpu' resource is specified.
350 if not test.support.is_resource_enabled("cpu"):
Pablo Galindobe287c32019-12-29 20:18:36 +0000351
352 tests_to_run_always = {item for item in items if
353 item.name in cls.run_always_files}
354
Pablo Galindo23a226b2019-12-29 19:20:55 +0000355 items = set(random.sample(items, 10))
356
Pablo Galindobe287c32019-12-29 20:18:36 +0000357 # Make sure that at least tests that heavily use grammar features are
358 # always considered in order to reduce the chance of missing something.
359 items = list(items | tests_to_run_always)
Pablo Galindoac229112019-12-09 17:57:50 +0000360
361 # bpo-31174: Store the names sample to always test the same files.
362 # It prevents false alarms when hunting reference leaks.
363 cls._files_to_test = items
364
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000365 return items
Victor Stinner8e482be2017-10-24 03:33:36 -0700366
367 def test_files(self):
Pablo Galindoac229112019-12-09 17:57:50 +0000368 for item in self.files_to_test():
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000369 if test.support.verbose:
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000370 print(f"Testing {item.absolute()}")
Eric V. Smith06cf6012016-09-03 12:33:38 -0400371
Eric V. Smith451d0e32016-09-09 21:56:20 -0400372 # Some f-strings are not correctly round-tripped by
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000373 # Tools/parser/unparse.py. See issue 28002 for details.
374 # We need to skip files that contain such f-strings.
375 if item.name in self.skip_files:
Eric V. Smith06cf6012016-09-03 12:33:38 -0400376 if test.support.verbose:
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000377 print(f"Skipping {item.absolute()}: see issue 28002")
Eric V. Smith06cf6012016-09-03 12:33:38 -0400378 continue
379
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000380 with self.subTest(filename=item):
381 source = read_pyfile(item)
Yury Selivanovd04e4172016-09-09 11:14:59 -0700382 self.check_roundtrip(source)
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000383
384
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000385if __name__ == "__main__":
Zachary Ware2b0a6102014-07-16 14:26:09 -0500386 unittest.main()