| 
									
										
										
										
											2014-07-16 14:26:09 -05:00
										 |  |  | """Tests for the unparse.py script in the Tools/parser directory.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | import unittest | 
					
						
							|  |  |  | import test.support | 
					
						
							|  |  |  | import io | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2012-06-23 09:27:47 +01:00
										 |  |  | import random | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | import tokenize | 
					
						
							| 
									
										
										
										
											2012-06-23 09:27:47 +01:00
										 |  |  | import ast | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 14:26:09 -05:00
										 |  |  | from test.test_tools import basepath, toolsdir, skip_if_missing | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | skip_if_missing() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | parser_path = os.path.join(toolsdir, "parser") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with test.support.DirsOnSysPath(parser_path): | 
					
						
							|  |  |  |     import unparse | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | def read_pyfile(filename): | 
					
						
							|  |  |  |     """Read and return the contents of a Python source file (as a
 | 
					
						
							|  |  |  |     string), taking into account the file encoding."""
 | 
					
						
							|  |  |  |     with open(filename, "rb") as pyfile: | 
					
						
							|  |  |  |         encoding = tokenize.detect_encoding(pyfile.readline)[0] | 
					
						
							|  |  |  |     with open(filename, "r", encoding=encoding) as pyfile: | 
					
						
							|  |  |  |         source = pyfile.read() | 
					
						
							|  |  |  |     return source | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 21:14:17 +00:00
										 |  |  | for_else = """\
 | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | def f(): | 
					
						
							|  |  |  |     for x in range(10): | 
					
						
							|  |  |  |         break | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         y = 2 | 
					
						
							|  |  |  |     z = 3 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 21:14:17 +00:00
										 |  |  | while_else = """\
 | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | def g(): | 
					
						
							|  |  |  |     while True: | 
					
						
							|  |  |  |         break | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         y = 2 | 
					
						
							|  |  |  |     z = 3 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 21:14:17 +00:00
										 |  |  | relative_import = """\
 | 
					
						
							|  |  |  | from . import fred | 
					
						
							|  |  |  | from .. import barney | 
					
						
							|  |  |  | from .australia import shrimp as prawns | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | nonlocal_ex = """\
 | 
					
						
							|  |  |  | def f(): | 
					
						
							|  |  |  |     x = 1 | 
					
						
							|  |  |  |     def g(): | 
					
						
							|  |  |  |         nonlocal x | 
					
						
							|  |  |  |         x = 2 | 
					
						
							|  |  |  |         y = 7 | 
					
						
							|  |  |  |         def h(): | 
					
						
							|  |  |  |             nonlocal x, y | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # also acts as test for 'except ... as ...' | 
					
						
							|  |  |  | raise_from = """\
 | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     1 / 0 | 
					
						
							|  |  |  | except ZeroDivisionError as e: | 
					
						
							|  |  |  |     raise ArithmeticError from e | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class_decorator = """\
 | 
					
						
							|  |  |  | @f1(arg) | 
					
						
							|  |  |  | @f2 | 
					
						
							|  |  |  | class Foo: pass | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-30 08:32:11 +00:00
										 |  |  | elif1 = """\
 | 
					
						
							|  |  |  | if cond1: | 
					
						
							|  |  |  |     suite1 | 
					
						
							|  |  |  | elif cond2: | 
					
						
							|  |  |  |     suite2 | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     suite3 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | elif2 = """\
 | 
					
						
							|  |  |  | if cond1: | 
					
						
							|  |  |  |     suite1 | 
					
						
							|  |  |  | elif cond2: | 
					
						
							|  |  |  |     suite2 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-30 08:46:53 +00:00
										 |  |  | try_except_finally = """\
 | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     suite1 | 
					
						
							|  |  |  | except ex1: | 
					
						
							|  |  |  |     suite2 | 
					
						
							|  |  |  | except ex2: | 
					
						
							|  |  |  |     suite3 | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     suite4 | 
					
						
							|  |  |  | finally: | 
					
						
							|  |  |  |     suite5 | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2010-06-30 08:32:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 17:35:19 +01:00
										 |  |  | with_simple = """\
 | 
					
						
							|  |  |  | with f(): | 
					
						
							|  |  |  |     suite1 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with_as = """\
 | 
					
						
							|  |  |  | with f() as x: | 
					
						
							|  |  |  |     suite1 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with_two_items = """\
 | 
					
						
							|  |  |  | with f() as x, g() as y: | 
					
						
							|  |  |  |     suite1 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | class ASTTestCase(unittest.TestCase): | 
					
						
							|  |  |  |     def assertASTEqual(self, ast1, ast2): | 
					
						
							|  |  |  |         self.assertEqual(ast.dump(ast1), ast.dump(ast2)) | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def check_roundtrip(self, code1, filename="internal"): | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  |         ast1 = compile(code1, filename, "exec", ast.PyCF_ONLY_AST) | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  |         unparse_buffer = io.StringIO() | 
					
						
							|  |  |  |         unparse.Unparser(ast1, unparse_buffer) | 
					
						
							|  |  |  |         code2 = unparse_buffer.getvalue() | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  |         ast2 = compile(code2, filename, "exec", ast.PyCF_ONLY_AST) | 
					
						
							|  |  |  |         self.assertASTEqual(ast1, ast2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class UnparseTestCase(ASTTestCase): | 
					
						
							|  |  |  |     # Tests for specific bugs found in earlier versions of unparse | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-20 15:09:15 -04:00
										 |  |  |     def test_fstrings(self): | 
					
						
							|  |  |  |         # See issue 25180 | 
					
						
							|  |  |  |         self.check_roundtrip(r"""f'{f"{0}"*3}'""") | 
					
						
							|  |  |  |         self.check_roundtrip(r"""f'{f"{y}"*3}'""") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  |     def test_del_statement(self): | 
					
						
							|  |  |  |         self.check_roundtrip("del x, y, z") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_shifts(self): | 
					
						
							|  |  |  |         self.check_roundtrip("45 << 2") | 
					
						
							|  |  |  |         self.check_roundtrip("13 >> 7") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_for_else(self): | 
					
						
							| 
									
										
										
										
											2010-06-28 21:14:17 +00:00
										 |  |  |         self.check_roundtrip(for_else) | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_while_else(self): | 
					
						
							| 
									
										
										
										
											2010-06-28 21:14:17 +00:00
										 |  |  |         self.check_roundtrip(while_else) | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_unary_parens(self): | 
					
						
							|  |  |  |         self.check_roundtrip("(-1)**7") | 
					
						
							| 
									
										
										
										
											2010-06-30 11:45:53 +00:00
										 |  |  |         self.check_roundtrip("(-1.)**8") | 
					
						
							|  |  |  |         self.check_roundtrip("(-1j)**6") | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  |         self.check_roundtrip("not True or False") | 
					
						
							|  |  |  |         self.check_roundtrip("True or not False") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 08:52:36 +00:00
										 |  |  |     def test_integer_parens(self): | 
					
						
							|  |  |  |         self.check_roundtrip("3 .__abs__()") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 10:01:48 +00:00
										 |  |  |     def test_huge_float(self): | 
					
						
							|  |  |  |         self.check_roundtrip("1e1000") | 
					
						
							|  |  |  |         self.check_roundtrip("-1e1000") | 
					
						
							| 
									
										
										
										
											2010-06-30 11:45:53 +00:00
										 |  |  |         self.check_roundtrip("1e1000j") | 
					
						
							|  |  |  |         self.check_roundtrip("-1e1000j") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_min_int(self): | 
					
						
							|  |  |  |         self.check_roundtrip(str(-2**31)) | 
					
						
							|  |  |  |         self.check_roundtrip(str(-2**63)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_imaginary_literals(self): | 
					
						
							|  |  |  |         self.check_roundtrip("7j") | 
					
						
							|  |  |  |         self.check_roundtrip("-7j") | 
					
						
							|  |  |  |         self.check_roundtrip("0j") | 
					
						
							|  |  |  |         self.check_roundtrip("-0j") | 
					
						
							| 
									
										
										
										
											2010-06-29 10:01:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_lambda_parentheses(self): | 
					
						
							|  |  |  |         self.check_roundtrip("(lambda: int)()") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 20:09:18 +00:00
										 |  |  |     def test_chained_comparisons(self): | 
					
						
							|  |  |  |         self.check_roundtrip("1 < 4 <= 5") | 
					
						
							|  |  |  |         self.check_roundtrip("a is b is c is not d") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 21:14:17 +00:00
										 |  |  |     def test_function_arguments(self): | 
					
						
							|  |  |  |         self.check_roundtrip("def f(): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(b = 2): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a, b): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a, b = 2): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a = 5, b = 2): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(*, a = 1, b = 2): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(*, a = 1, b): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(*, a, b = 2): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a, b = None, *, c, **kwds): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(*args, **kwargs): pass") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_relative_import(self): | 
					
						
							|  |  |  |         self.check_roundtrip(relative_import) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_nonlocal(self): | 
					
						
							|  |  |  |         self.check_roundtrip(nonlocal_ex) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_raise_from(self): | 
					
						
							|  |  |  |         self.check_roundtrip(raise_from) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_bytes(self): | 
					
						
							|  |  |  |         self.check_roundtrip("b'123'") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_annotations(self): | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a : int): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(a: int = 5): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(*args: [int]): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f(**kwargs: dict): pass") | 
					
						
							|  |  |  |         self.check_roundtrip("def f() -> None: pass") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_set_literal(self): | 
					
						
							|  |  |  |         self.check_roundtrip("{'a', 'b', 'c'}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_set_comprehension(self): | 
					
						
							|  |  |  |         self.check_roundtrip("{x for x in range(5)}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_dict_comprehension(self): | 
					
						
							|  |  |  |         self.check_roundtrip("{x: x*x for x in range(10)}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_class_decorators(self): | 
					
						
							|  |  |  |         self.check_roundtrip(class_decorator) | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 18:38:59 +00:00
										 |  |  |     def test_class_definition(self): | 
					
						
							|  |  |  |         self.check_roundtrip("class A(metaclass=type, *[], **{}): pass") | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-30 08:32:11 +00:00
										 |  |  |     def test_elifs(self): | 
					
						
							|  |  |  |         self.check_roundtrip(elif1) | 
					
						
							|  |  |  |         self.check_roundtrip(elif2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-30 08:46:53 +00:00
										 |  |  |     def test_try_except_finally(self): | 
					
						
							|  |  |  |         self.check_roundtrip(try_except_finally) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 17:27:39 +01:00
										 |  |  |     def test_starred_assignment(self): | 
					
						
							|  |  |  |         self.check_roundtrip("a, *b, c = seq") | 
					
						
							|  |  |  |         self.check_roundtrip("a, (*b, c) = seq") | 
					
						
							|  |  |  |         self.check_roundtrip("a, *b[0], c = seq") | 
					
						
							|  |  |  |         self.check_roundtrip("a, *(b, c) = seq") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 17:35:19 +01:00
										 |  |  |     def test_with_simple(self): | 
					
						
							|  |  |  |         self.check_roundtrip(with_simple) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_with_as(self): | 
					
						
							|  |  |  |         self.check_roundtrip(with_as) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_with_two_items(self): | 
					
						
							|  |  |  |         self.check_roundtrip(with_two_items) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 16:50:15 +02:00
										 |  |  |     def test_dict_unpacking_in_dict(self): | 
					
						
							|  |  |  |         # See issue 26489 | 
					
						
							|  |  |  |         self.check_roundtrip(r"""{**{'y': 2}, 'x': 1}""") | 
					
						
							|  |  |  |         self.check_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 17:27:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | class DirectoryTestCase(ASTTestCase): | 
					
						
							|  |  |  |     """Test roundtrip behaviour on all files in Lib and Lib/test.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # test directories, relative to the root of the distribution | 
					
						
							|  |  |  |     test_directories = 'Lib', os.path.join('Lib', 'test') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-30 11:14:30 +00:00
										 |  |  |     def test_files(self): | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  |         # get names of files to test | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         names = [] | 
					
						
							|  |  |  |         for d in self.test_directories: | 
					
						
							| 
									
										
										
										
											2014-07-16 14:26:09 -05:00
										 |  |  |             test_dir = os.path.join(basepath, d) | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  |             for n in os.listdir(test_dir): | 
					
						
							|  |  |  |                 if n.endswith('.py') and not n.startswith('bad'): | 
					
						
							|  |  |  |                     names.append(os.path.join(test_dir, n)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-23 09:27:47 +01:00
										 |  |  |         # Test limited subset of files unless the 'cpu' resource is specified. | 
					
						
							|  |  |  |         if not test.support.is_resource_enabled("cpu"): | 
					
						
							|  |  |  |             names = random.sample(names, 10) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  |         for filename in names: | 
					
						
							|  |  |  |             if test.support.verbose: | 
					
						
							|  |  |  |                 print('Testing %s' % filename) | 
					
						
							| 
									
										
										
										
											2016-09-03 12:33:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 21:56:20 -04:00
										 |  |  |             # Some f-strings are not correctly round-tripped by | 
					
						
							|  |  |  |             #  Tools/parser/unparse.py.  See issue 28002 for details. | 
					
						
							|  |  |  |             #  We need to skip files that contain such f-strings. | 
					
						
							|  |  |  |             if os.path.basename(filename) in ('test_fstring.py', ): | 
					
						
							| 
									
										
										
										
											2016-09-03 12:33:38 -04:00
										 |  |  |                 if test.support.verbose: | 
					
						
							| 
									
										
										
										
											2016-09-09 21:56:20 -04:00
										 |  |  |                     print(f'Skipping {filename}: see issue 28002') | 
					
						
							| 
									
										
										
										
											2016-09-03 12:33:38 -04:00
										 |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 11:14:59 -07:00
										 |  |  |             with self.subTest(filename=filename): | 
					
						
							|  |  |  |                 source = read_pyfile(filename) | 
					
						
							|  |  |  |                 self.check_roundtrip(source) | 
					
						
							| 
									
										
										
										
											2010-06-29 14:08:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-28 19:44:20 +00:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2014-07-16 14:26:09 -05:00
										 |  |  |     unittest.main() |