diff options
author | Pablo Galindo <Pablogsal@gmail.com> | 2019-11-24 23:02:40 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-24 23:02:40 (GMT) |
commit | 27fc3b6f3fc49a36d3f962caac5c5495696d12ed (patch) | |
tree | a95472c5bd4a5a0588a3725b4b8b1a94547c9999 /Lib/test | |
parent | 6bf644ec82f14cceae68278dc35bafb00875efae (diff) | |
download | cpython-27fc3b6f3fc49a36d3f962caac5c5495696d12ed.zip cpython-27fc3b6f3fc49a36d3f962caac5c5495696d12ed.tar.gz cpython-27fc3b6f3fc49a36d3f962caac5c5495696d12ed.tar.bz2 |
bpo-38870: Expose a function to unparse an ast object in the ast module (GH-17302)
Add ast.unparse() as a function in the ast module that can be used to unparse an
ast.AST object and produce a string with code that would produce an equivalent ast.AST
object when parsed.
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_unparse.py (renamed from Lib/test/test_tools/test_unparse.py) | 104 |
1 files changed, 57 insertions, 47 deletions
diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_unparse.py index a958ebb..9197c8a 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -3,19 +3,12 @@ import unittest import test.support import io -import os +import pathlib import random import tokenize import ast +import functools -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 def read_pyfile(filename): """Read and return the contents of a Python source file (as a @@ -26,6 +19,7 @@ def read_pyfile(filename): source = pyfile.read() return source + for_else = """\ def f(): for x in range(10): @@ -119,18 +113,21 @@ with f() as x, g() as y: suite1 """ + class ASTTestCase(unittest.TestCase): def assertASTEqual(self, ast1, ast2): self.assertEqual(ast.dump(ast1), ast.dump(ast2)) - def check_roundtrip(self, code1, filename="internal"): - ast1 = compile(code1, filename, "exec", ast.PyCF_ONLY_AST) - unparse_buffer = io.StringIO() - unparse.Unparser(ast1, unparse_buffer) - code2 = unparse_buffer.getvalue() - ast2 = compile(code2, filename, "exec", ast.PyCF_ONLY_AST) + def check_roundtrip(self, code1): + ast1 = ast.parse(code1) + code2 = ast.unparse(ast1) + ast2 = ast.parse(code2) self.assertASTEqual(ast1, ast2) + def check_invalid(self, node, raises=ValueError): + self.assertRaises(raises, ast.unparse, node) + + class UnparseTestCase(ASTTestCase): # Tests for specific bugs found in earlier versions of unparse @@ -174,8 +171,8 @@ class UnparseTestCase(ASTTestCase): self.check_roundtrip("-1e1000j") def test_min_int(self): - self.check_roundtrip(str(-2**31)) - self.check_roundtrip(str(-2**63)) + self.check_roundtrip(str(-(2 ** 31))) + self.check_roundtrip(str(-(2 ** 63))) def test_imaginary_literals(self): self.check_roundtrip("7j") @@ -265,54 +262,67 @@ class UnparseTestCase(ASTTestCase): self.check_roundtrip(r"""{**{'y': 2}, 'x': 1}""") self.check_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""") + def test_invalid_raise(self): + self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X"))) + + def test_invalid_fstring_constant(self): + self.check_invalid(ast.JoinedStr(values=[ast.Constant(value=100)])) + + def test_invalid_fstring_conversion(self): + self.check_invalid( + ast.FormattedValue( + value=ast.Constant(value="a", kind=None), + conversion=ord("Y"), # random character + format_spec=None, + ) + ) + + def test_invalid_set(self): + self.check_invalid(ast.Set(elts=[])) + class DirectoryTestCase(ASTTestCase): """Test roundtrip behaviour on all files in Lib and Lib/test.""" - NAMES = None - # test directories, relative to the root of the distribution - test_directories = 'Lib', os.path.join('Lib', 'test') + lib_dir = pathlib.Path(__file__).parent / ".." + test_directories = (lib_dir, lib_dir / "test") + skip_files = {"test_fstring.py"} - @classmethod - def get_names(cls): - if cls.NAMES is not None: - return cls.NAMES + @functools.cached_property + def files_to_test(self): + # bpo-31174: Use cached_property to store the names sample + # to always test the same files. It prevents false alarms + # when hunting reference leaks. - names = [] - for d in cls.test_directories: - test_dir = os.path.join(basepath, d) - for n in os.listdir(test_dir): - if n.endswith('.py') and not n.startswith('bad'): - names.append(os.path.join(test_dir, n)) + items = [ + item.resolve() + for directory in self.test_directories + for item in directory.glob("*.py") + if not item.name.startswith("bad") + ] # Test limited subset of files unless the 'cpu' resource is specified. if not test.support.is_resource_enabled("cpu"): - names = random.sample(names, 10) - # bpo-31174: Store the names sample to always test the same files. - # It prevents false alarms when hunting reference leaks. - cls.NAMES = names - return names + items = random.sample(items, 10) + return items def test_files(self): - # get names of files to test - names = self.get_names() - - for filename in names: + for item in self.files_to_test: if test.support.verbose: - print('Testing %s' % filename) + print(f"Testing {item.absolute()}") # 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', ): + # Tools/parser/unparse.py. See issue 28002 for details. + # We need to skip files that contain such f-strings. + if item.name in self.skip_files: if test.support.verbose: - print(f'Skipping {filename}: see issue 28002') + print(f"Skipping {item.absolute()}: see issue 28002") continue - with self.subTest(filename=filename): - source = read_pyfile(filename) + with self.subTest(filename=item): + source = read_pyfile(item) self.check_roundtrip(source) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() |