blob: 96110260f550a8de5c39be226520fb95a10d90a4 [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
Mark Dickinsonae100052010-06-28 19:44:20 +00005import io
Pablo Galindo27fc3b62019-11-24 23:02:40 +00006import pathlib
Mark Dickinsonbe4fb692012-06-23 09:27:47 +01007import random
Mark Dickinsond751c2e2010-06-29 14:08:23 +00008import tokenize
Mark Dickinsonbe4fb692012-06-23 09:27:47 +01009import ast
Pablo Galindo27fc3b62019-11-24 23:02:40 +000010import functools
Mark Dickinsonae100052010-06-28 19:44:20 +000011
Zachary Ware2b0a6102014-07-16 14:26:09 -050012
Mark Dickinsond751c2e2010-06-29 14:08:23 +000013def read_pyfile(filename):
14 """Read and return the contents of a Python source file (as a
15 string), taking into account the file encoding."""
16 with open(filename, "rb") as pyfile:
17 encoding = tokenize.detect_encoding(pyfile.readline)[0]
18 with open(filename, "r", encoding=encoding) as pyfile:
19 source = pyfile.read()
20 return source
21
Pablo Galindo27fc3b62019-11-24 23:02:40 +000022
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +000023for_else = """\
Mark Dickinsonae100052010-06-28 19:44:20 +000024def f():
25 for x in range(10):
26 break
27 else:
28 y = 2
29 z = 3
30"""
31
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +000032while_else = """\
Mark Dickinsonae100052010-06-28 19:44:20 +000033def g():
34 while True:
35 break
36 else:
37 y = 2
38 z = 3
39"""
40
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +000041relative_import = """\
42from . import fred
43from .. import barney
44from .australia import shrimp as prawns
45"""
46
47nonlocal_ex = """\
48def f():
49 x = 1
50 def g():
51 nonlocal x
52 x = 2
53 y = 7
54 def h():
55 nonlocal x, y
56"""
57
58# also acts as test for 'except ... as ...'
59raise_from = """\
60try:
61 1 / 0
62except ZeroDivisionError as e:
63 raise ArithmeticError from e
64"""
65
66class_decorator = """\
67@f1(arg)
68@f2
69class Foo: pass
70"""
71
Mark Dickinson8d6d7602010-06-30 08:32:11 +000072elif1 = """\
73if cond1:
74 suite1
75elif cond2:
76 suite2
77else:
78 suite3
79"""
80
81elif2 = """\
82if cond1:
83 suite1
84elif cond2:
85 suite2
86"""
87
Mark Dickinson81ad8cc2010-06-30 08:46:53 +000088try_except_finally = """\
89try:
90 suite1
91except ex1:
92 suite2
93except ex2:
94 suite3
95else:
96 suite4
97finally:
98 suite5
99"""
Mark Dickinson8d6d7602010-06-30 08:32:11 +0000100
Mark Dickinsonfe8440a2012-05-06 17:35:19 +0100101with_simple = """\
102with f():
103 suite1
104"""
105
106with_as = """\
107with f() as x:
108 suite1
109"""
110
111with_two_items = """\
112with f() as x, g() as y:
113 suite1
114"""
115
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000116
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000117class ASTTestCase(unittest.TestCase):
118 def assertASTEqual(self, ast1, ast2):
119 self.assertEqual(ast.dump(ast1), ast.dump(ast2))
Mark Dickinsonae100052010-06-28 19:44:20 +0000120
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000121 def check_roundtrip(self, code1):
122 ast1 = ast.parse(code1)
123 code2 = ast.unparse(ast1)
124 ast2 = ast.parse(code2)
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000125 self.assertASTEqual(ast1, ast2)
126
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000127 def check_invalid(self, node, raises=ValueError):
128 self.assertRaises(raises, ast.unparse, node)
129
130
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000131class UnparseTestCase(ASTTestCase):
132 # Tests for specific bugs found in earlier versions of unparse
Mark Dickinsonae100052010-06-28 19:44:20 +0000133
Eric V. Smith608adf92015-09-20 15:09:15 -0400134 def test_fstrings(self):
135 # See issue 25180
136 self.check_roundtrip(r"""f'{f"{0}"*3}'""")
137 self.check_roundtrip(r"""f'{f"{y}"*3}'""")
Eric V. Smith608adf92015-09-20 15:09:15 -0400138
Chih-Hsuan Yenaaf47ca2019-05-27 01:08:20 +0800139 def test_strings(self):
140 self.check_roundtrip("u'foo'")
141 self.check_roundtrip("r'foo'")
142 self.check_roundtrip("b'foo'")
143
Mark Dickinsonae100052010-06-28 19:44:20 +0000144 def test_del_statement(self):
145 self.check_roundtrip("del x, y, z")
146
147 def test_shifts(self):
148 self.check_roundtrip("45 << 2")
149 self.check_roundtrip("13 >> 7")
150
151 def test_for_else(self):
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000152 self.check_roundtrip(for_else)
Mark Dickinsonae100052010-06-28 19:44:20 +0000153
154 def test_while_else(self):
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000155 self.check_roundtrip(while_else)
Mark Dickinsonae100052010-06-28 19:44:20 +0000156
157 def test_unary_parens(self):
158 self.check_roundtrip("(-1)**7")
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000159 self.check_roundtrip("(-1.)**8")
160 self.check_roundtrip("(-1j)**6")
Mark Dickinsonae100052010-06-28 19:44:20 +0000161 self.check_roundtrip("not True or False")
162 self.check_roundtrip("True or not False")
163
Mark Dickinson3eb02902010-06-29 08:52:36 +0000164 def test_integer_parens(self):
165 self.check_roundtrip("3 .__abs__()")
166
Mark Dickinson8042e282010-06-29 10:01:48 +0000167 def test_huge_float(self):
168 self.check_roundtrip("1e1000")
169 self.check_roundtrip("-1e1000")
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000170 self.check_roundtrip("1e1000j")
171 self.check_roundtrip("-1e1000j")
172
173 def test_min_int(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000174 self.check_roundtrip(str(-(2 ** 31)))
175 self.check_roundtrip(str(-(2 ** 63)))
Mark Dickinsoncba8c102010-06-30 11:45:53 +0000176
177 def test_imaginary_literals(self):
178 self.check_roundtrip("7j")
179 self.check_roundtrip("-7j")
180 self.check_roundtrip("0j")
181 self.check_roundtrip("-0j")
Mark Dickinson8042e282010-06-29 10:01:48 +0000182
183 def test_lambda_parentheses(self):
184 self.check_roundtrip("(lambda: int)()")
185
Mark Dickinsonf5451e52010-06-28 20:09:18 +0000186 def test_chained_comparisons(self):
187 self.check_roundtrip("1 < 4 <= 5")
188 self.check_roundtrip("a is b is c is not d")
189
Mark Dickinsonfa2e4e92010-06-28 21:14:17 +0000190 def test_function_arguments(self):
191 self.check_roundtrip("def f(): pass")
192 self.check_roundtrip("def f(a): pass")
193 self.check_roundtrip("def f(b = 2): pass")
194 self.check_roundtrip("def f(a, b): pass")
195 self.check_roundtrip("def f(a, b = 2): pass")
196 self.check_roundtrip("def f(a = 5, b = 2): pass")
197 self.check_roundtrip("def f(*, a = 1, b = 2): pass")
198 self.check_roundtrip("def f(*, a = 1, b): pass")
199 self.check_roundtrip("def f(*, a, b = 2): pass")
200 self.check_roundtrip("def f(a, b = None, *, c, **kwds): pass")
201 self.check_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass")
202 self.check_roundtrip("def f(*args, **kwargs): pass")
203
204 def test_relative_import(self):
205 self.check_roundtrip(relative_import)
206
207 def test_nonlocal(self):
208 self.check_roundtrip(nonlocal_ex)
209
210 def test_raise_from(self):
211 self.check_roundtrip(raise_from)
212
213 def test_bytes(self):
214 self.check_roundtrip("b'123'")
215
216 def test_annotations(self):
217 self.check_roundtrip("def f(a : int): pass")
218 self.check_roundtrip("def f(a: int = 5): pass")
219 self.check_roundtrip("def f(*args: [int]): pass")
220 self.check_roundtrip("def f(**kwargs: dict): pass")
221 self.check_roundtrip("def f() -> None: pass")
222
223 def test_set_literal(self):
224 self.check_roundtrip("{'a', 'b', 'c'}")
225
226 def test_set_comprehension(self):
227 self.check_roundtrip("{x for x in range(5)}")
228
229 def test_dict_comprehension(self):
230 self.check_roundtrip("{x: x*x for x in range(10)}")
231
232 def test_class_decorators(self):
233 self.check_roundtrip(class_decorator)
Mark Dickinsonae100052010-06-28 19:44:20 +0000234
Mark Dickinson578aa562010-06-29 18:38:59 +0000235 def test_class_definition(self):
236 self.check_roundtrip("class A(metaclass=type, *[], **{}): pass")
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000237
Mark Dickinson8d6d7602010-06-30 08:32:11 +0000238 def test_elifs(self):
239 self.check_roundtrip(elif1)
240 self.check_roundtrip(elif2)
241
Mark Dickinson81ad8cc2010-06-30 08:46:53 +0000242 def test_try_except_finally(self):
243 self.check_roundtrip(try_except_finally)
244
Mark Dickinson1b2e9442012-05-06 17:27:39 +0100245 def test_starred_assignment(self):
246 self.check_roundtrip("a, *b, c = seq")
247 self.check_roundtrip("a, (*b, c) = seq")
248 self.check_roundtrip("a, *b[0], c = seq")
249 self.check_roundtrip("a, *(b, c) = seq")
250
Mark Dickinsonfe8440a2012-05-06 17:35:19 +0100251 def test_with_simple(self):
252 self.check_roundtrip(with_simple)
253
254 def test_with_as(self):
255 self.check_roundtrip(with_as)
256
257 def test_with_two_items(self):
258 self.check_roundtrip(with_two_items)
259
Berker Peksagd66dd5c2016-03-06 16:50:15 +0200260 def test_dict_unpacking_in_dict(self):
261 # See issue 26489
262 self.check_roundtrip(r"""{**{'y': 2}, 'x': 1}""")
263 self.check_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""")
264
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000265 def test_invalid_raise(self):
266 self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X")))
267
268 def test_invalid_fstring_constant(self):
269 self.check_invalid(ast.JoinedStr(values=[ast.Constant(value=100)]))
270
271 def test_invalid_fstring_conversion(self):
272 self.check_invalid(
273 ast.FormattedValue(
274 value=ast.Constant(value="a", kind=None),
275 conversion=ord("Y"), # random character
276 format_spec=None,
277 )
278 )
279
280 def test_invalid_set(self):
281 self.check_invalid(ast.Set(elts=[]))
282
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"}
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000290
Pablo Galindoac229112019-12-09 17:57:50 +0000291 _files_to_test = None
292
293 @classmethod
294 def files_to_test(cls):
295
296 if cls._files_to_test is not None:
297 return cls._files_to_test
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000298
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000299 items = [
300 item.resolve()
Pablo Galindoac229112019-12-09 17:57:50 +0000301 for directory in cls.test_directories
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000302 for item in directory.glob("*.py")
303 if not item.name.startswith("bad")
304 ]
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000305
Mark Dickinsonbe4fb692012-06-23 09:27:47 +0100306 # Test limited subset of files unless the 'cpu' resource is specified.
307 if not test.support.is_resource_enabled("cpu"):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000308 items = random.sample(items, 10)
Pablo Galindoac229112019-12-09 17:57:50 +0000309
310 # bpo-31174: Store the names sample to always test the same files.
311 # It prevents false alarms when hunting reference leaks.
312 cls._files_to_test = items
313
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000314 return items
Victor Stinner8e482be2017-10-24 03:33:36 -0700315
316 def test_files(self):
Pablo Galindoac229112019-12-09 17:57:50 +0000317 for item in self.files_to_test():
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000318 if test.support.verbose:
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000319 print(f"Testing {item.absolute()}")
Eric V. Smith06cf6012016-09-03 12:33:38 -0400320
Eric V. Smith451d0e32016-09-09 21:56:20 -0400321 # Some f-strings are not correctly round-tripped by
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000322 # Tools/parser/unparse.py. See issue 28002 for details.
323 # We need to skip files that contain such f-strings.
324 if item.name in self.skip_files:
Eric V. Smith06cf6012016-09-03 12:33:38 -0400325 if test.support.verbose:
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000326 print(f"Skipping {item.absolute()}: see issue 28002")
Eric V. Smith06cf6012016-09-03 12:33:38 -0400327 continue
328
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000329 with self.subTest(filename=item):
330 source = read_pyfile(item)
Yury Selivanovd04e4172016-09-09 11:14:59 -0700331 self.check_roundtrip(source)
Mark Dickinsond751c2e2010-06-29 14:08:23 +0000332
333
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000334if __name__ == "__main__":
Zachary Ware2b0a6102014-07-16 14:26:09 -0500335 unittest.main()