blob: e8b0d4b06f9e97bc815b896f8084b60a28deef31 [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
128
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000129class UnparseTestCase(ASTTestCase):
130 # Tests for specific bugs found in earlier versions of unparse
Mark Dickinsonae100052010-06-28 19:44:20 +0000131
Eric V. Smith608adf92015-09-20 15:09:15 -0400132 def test_fstrings(self):
133 # See issue 25180
134 self.check_roundtrip(r"""f'{f"{0}"*3}'""")
135 self.check_roundtrip(r"""f'{f"{y}"*3}'""")
Eric V. Smith608adf92015-09-20 15:09:15 -0400136
Chih-Hsuan Yenaaf47ca2019-05-27 01:08:20 +0800137 def test_strings(self):
138 self.check_roundtrip("u'foo'")
139 self.check_roundtrip("r'foo'")
140 self.check_roundtrip("b'foo'")
141
Mark Dickinsonae100052010-06-28 19:44:20 +0000142 def test_del_statement(self):
143 self.check_roundtrip("del x, y, z")
144
145 def test_shifts(self):
146 self.check_roundtrip("45 << 2")
147 self.check_roundtrip("13 >> 7")
148
149 def test_for_else(self):
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000150 self.check_roundtrip(for_else)
Mark Dickinsonae100052010-06-28 19:44:20 +0000151
152 def test_while_else(self):
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000153 self.check_roundtrip(while_else)
Mark Dickinsonae100052010-06-28 19:44:20 +0000154
155 def test_unary_parens(self):
156 self.check_roundtrip("(-1)**7")
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000157 self.check_roundtrip("(-1.)**8")
158 self.check_roundtrip("(-1j)**6")
Mark Dickinsonae100052010-06-28 19:44:20 +0000159 self.check_roundtrip("not True or False")
160 self.check_roundtrip("True or not False")
161
Mark Dickinson3eb02902010-06-29 08:52:36 +0000162 def test_integer_parens(self):
163 self.check_roundtrip("3 .__abs__()")
164
Mark Dickinson8042e282010-06-29 10:01:48 +0000165 def test_huge_float(self):
166 self.check_roundtrip("1e1000")
167 self.check_roundtrip("-1e1000")
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000168 self.check_roundtrip("1e1000j")
169 self.check_roundtrip("-1e1000j")
170
171 def test_min_int(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000172 self.check_roundtrip(str(-(2 ** 31)))
173 self.check_roundtrip(str(-(2 ** 63)))
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000174
175 def test_imaginary_literals(self):
176 self.check_roundtrip("7j")
177 self.check_roundtrip("-7j")
178 self.check_roundtrip("0j")
179 self.check_roundtrip("-0j")
Mark Dickinson8042e282010-06-29 10:01:48 +0000180
181 def test_lambda_parentheses(self):
182 self.check_roundtrip("(lambda: int)()")
183
Mark Dickinsonf5451e52010-06-28 20:09:18 +0000184 def test_chained_comparisons(self):
185 self.check_roundtrip("1 < 4 <= 5")
186 self.check_roundtrip("a is b is c is not d")
187
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000188 def test_function_arguments(self):
189 self.check_roundtrip("def f(): pass")
190 self.check_roundtrip("def f(a): pass")
191 self.check_roundtrip("def f(b = 2): pass")
192 self.check_roundtrip("def f(a, b): pass")
193 self.check_roundtrip("def f(a, b = 2): pass")
194 self.check_roundtrip("def f(a = 5, b = 2): pass")
195 self.check_roundtrip("def f(*, a = 1, b = 2): pass")
196 self.check_roundtrip("def f(*, a = 1, b): pass")
197 self.check_roundtrip("def f(*, a, b = 2): pass")
198 self.check_roundtrip("def f(a, b = None, *, c, **kwds): pass")
199 self.check_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass")
200 self.check_roundtrip("def f(*args, **kwargs): pass")
201
202 def test_relative_import(self):
203 self.check_roundtrip(relative_import)
204
205 def test_nonlocal(self):
206 self.check_roundtrip(nonlocal_ex)
207
208 def test_raise_from(self):
209 self.check_roundtrip(raise_from)
210
211 def test_bytes(self):
212 self.check_roundtrip("b'123'")
213
214 def test_annotations(self):
215 self.check_roundtrip("def f(a : int): pass")
216 self.check_roundtrip("def f(a: int = 5): pass")
217 self.check_roundtrip("def f(*args: [int]): pass")
218 self.check_roundtrip("def f(**kwargs: dict): pass")
219 self.check_roundtrip("def f() -> None: pass")
220
221 def test_set_literal(self):
222 self.check_roundtrip("{'a', 'b', 'c'}")
223
224 def test_set_comprehension(self):
225 self.check_roundtrip("{x for x in range(5)}")
226
227 def test_dict_comprehension(self):
228 self.check_roundtrip("{x: x*x for x in range(10)}")
229
230 def test_class_decorators(self):
231 self.check_roundtrip(class_decorator)
Mark Dickinsonae100052010-06-28 19:44:20 +0000232
Mark Dickinson578aa562010-06-29 18:38:59 +0000233 def test_class_definition(self):
234 self.check_roundtrip("class A(metaclass=type, *[], **{}): pass")
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000235
Mark Dickinson8d6d7602010-06-30 08:32:11 +0000236 def test_elifs(self):
237 self.check_roundtrip(elif1)
238 self.check_roundtrip(elif2)
239
Mark Dickinson81ad8cc2010-06-30 08:46:53 +0000240 def test_try_except_finally(self):
241 self.check_roundtrip(try_except_finally)
242
Mark Dickinson1b2e9442012-05-06 17:27:39 +0100243 def test_starred_assignment(self):
244 self.check_roundtrip("a, *b, c = seq")
245 self.check_roundtrip("a, (*b, c) = seq")
246 self.check_roundtrip("a, *b[0], c = seq")
247 self.check_roundtrip("a, *(b, c) = seq")
248
Mark Dickinsonfe8440a2012-05-06 17:35:19 +0100249 def test_with_simple(self):
250 self.check_roundtrip(with_simple)
251
252 def test_with_as(self):
253 self.check_roundtrip(with_as)
254
255 def test_with_two_items(self):
256 self.check_roundtrip(with_two_items)
257
Berker Peksagd66dd5c2016-03-06 16:50:15 +0200258 def test_dict_unpacking_in_dict(self):
259 # See issue 26489
260 self.check_roundtrip(r"""{**{'y': 2}, 'x': 1}""")
261 self.check_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""")
262
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000263 def test_invalid_raise(self):
264 self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X")))
265
266 def test_invalid_fstring_constant(self):
267 self.check_invalid(ast.JoinedStr(values=[ast.Constant(value=100)]))
268
269 def test_invalid_fstring_conversion(self):
270 self.check_invalid(
271 ast.FormattedValue(
272 value=ast.Constant(value="a", kind=None),
273 conversion=ord("Y"), # random character
274 format_spec=None,
275 )
276 )
277
278 def test_invalid_set(self):
279 self.check_invalid(ast.Set(elts=[]))
280
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300281 def test_invalid_yield_from(self):
282 self.check_invalid(ast.YieldFrom(value=None))
Mark Dickinson1b2e9442012-05-06 17:27:39 +0100283
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000284class DirectoryTestCase(ASTTestCase):
285 """Test roundtrip behaviour on all files in Lib and Lib/test."""
286
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000287 lib_dir = pathlib.Path(__file__).parent / ".."
288 test_directories = (lib_dir, lib_dir / "test")
289 skip_files = {"test_fstring.py"}
Pablo Galindo23a226b2019-12-29 19:20:55 +0000290 run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py",
291 "test_ast.py", "test_asdl_parser.py"}
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000292
Pablo Galindoac229112019-12-09 17:57:50 +0000293 _files_to_test = None
294
295 @classmethod
296 def files_to_test(cls):
297
298 if cls._files_to_test is not None:
299 return cls._files_to_test
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000300
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000301 items = [
302 item.resolve()
Pablo Galindoac229112019-12-09 17:57:50 +0000303 for directory in cls.test_directories
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000304 for item in directory.glob("*.py")
305 if not item.name.startswith("bad")
306 ]
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000307
Mark Dickinsonbe4fb692012-06-23 09:27:47 +0100308 # Test limited subset of files unless the 'cpu' resource is specified.
309 if not test.support.is_resource_enabled("cpu"):
Pablo Galindobe287c32019-12-29 20:18:36 +0000310
311 tests_to_run_always = {item for item in items if
312 item.name in cls.run_always_files}
313
Pablo Galindo23a226b2019-12-29 19:20:55 +0000314 items = set(random.sample(items, 10))
315
Pablo Galindobe287c32019-12-29 20:18:36 +0000316 # Make sure that at least tests that heavily use grammar features are
317 # always considered in order to reduce the chance of missing something.
318 items = list(items | tests_to_run_always)
Pablo Galindoac229112019-12-09 17:57:50 +0000319
320 # bpo-31174: Store the names sample to always test the same files.
321 # It prevents false alarms when hunting reference leaks.
322 cls._files_to_test = items
323
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000324 return items
Victor Stinner8e482be2017-10-24 03:33:36 -0700325
326 def test_files(self):
Pablo Galindoac229112019-12-09 17:57:50 +0000327 for item in self.files_to_test():
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000328 if test.support.verbose:
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000329 print(f"Testing {item.absolute()}")
Eric V. Smith06cf6012016-09-03 12:33:38 -0400330
Eric V. Smith451d0e32016-09-09 21:56:20 -0400331 # Some f-strings are not correctly round-tripped by
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000332 # Tools/parser/unparse.py. See issue 28002 for details.
333 # We need to skip files that contain such f-strings.
334 if item.name in self.skip_files:
Eric V. Smith06cf6012016-09-03 12:33:38 -0400335 if test.support.verbose:
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000336 print(f"Skipping {item.absolute()}: see issue 28002")
Eric V. Smith06cf6012016-09-03 12:33:38 -0400337 continue
338
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000339 with self.subTest(filename=item):
340 source = read_pyfile(item)
Yury Selivanovd04e4172016-09-09 11:14:59 -0700341 self.check_roundtrip(source)
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000342
343
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000344if __name__ == "__main__":
Zachary Ware2b0a6102014-07-16 14:26:09 -0500345 unittest.main()