test_unparse.py 7.4 KB
Newer Older
1 2
"""Tests for the unparse.py script in the Tools/parser directory."""

3 4 5
import unittest
import test.support
import io
6
import os
7
import random
8
import tokenize
9
import ast
10

11 12 13 14 15 16 17 18 19
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

20 21 22 23 24 25 26 27 28
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

29
for_else = """\
30 31 32 33 34 35 36 37
def f():
    for x in range(10):
        break
    else:
        y = 2
    z = 3
"""

38
while_else = """\
39 40 41 42 43 44 45 46
def g():
    while True:
        break
    else:
        y = 2
    z = 3
"""

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
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
"""

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
elif1 = """\
if cond1:
    suite1
elif cond2:
    suite2
else:
    suite3
"""

elif2 = """\
if cond1:
    suite1
elif cond2:
    suite2
"""

94 95 96 97 98 99 100 101 102 103 104 105
try_except_finally = """\
try:
    suite1
except ex1:
    suite2
except ex2:
    suite3
else:
    suite4
finally:
    suite5
"""
106

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
with_simple = """\
with f():
    suite1
"""

with_as = """\
with f() as x:
    suite1
"""

with_two_items = """\
with f() as x, g() as y:
    suite1
"""

122 123 124
class ASTTestCase(unittest.TestCase):
    def assertASTEqual(self, ast1, ast2):
        self.assertEqual(ast.dump(ast1), ast.dump(ast2))
125 126

    def check_roundtrip(self, code1, filename="internal"):
127
        ast1 = compile(code1, filename, "exec", ast.PyCF_ONLY_AST)
128 129 130
        unparse_buffer = io.StringIO()
        unparse.Unparser(ast1, unparse_buffer)
        code2 = unparse_buffer.getvalue()
131 132 133 134 135
        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
136

137 138 139 140 141 142 143 144 145
    def test_fstrings(self):
        # See issue 25180
        self.check_roundtrip(r"""f'{f"{0}"*3}'""")
        self.check_roundtrip(r"""f'{f"{y}"*3}'""")
        self.check_roundtrip(r"""f'{f"{\'x\'}"*3}'""")

        self.check_roundtrip(r'''f"{r'x' f'{\"s\"}'}"''')
        self.check_roundtrip(r'''f"{r'x'rf'{\"s\"}'}"''')

146 147 148 149 150 151 152 153
    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):
154
        self.check_roundtrip(for_else)
155 156

    def test_while_else(self):
157
        self.check_roundtrip(while_else)
158 159 160

    def test_unary_parens(self):
        self.check_roundtrip("(-1)**7")
161 162
        self.check_roundtrip("(-1.)**8")
        self.check_roundtrip("(-1j)**6")
163 164 165
        self.check_roundtrip("not True or False")
        self.check_roundtrip("True or not False")

166 167 168
    def test_integer_parens(self):
        self.check_roundtrip("3 .__abs__()")

Mark Dickinson's avatar
Mark Dickinson committed
169 170 171
    def test_huge_float(self):
        self.check_roundtrip("1e1000")
        self.check_roundtrip("-1e1000")
172 173 174 175 176 177 178 179 180 181 182 183
        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")
Mark Dickinson's avatar
Mark Dickinson committed
184 185 186 187

    def test_lambda_parentheses(self):
        self.check_roundtrip("(lambda: int)()")

188 189 190 191
    def test_chained_comparisons(self):
        self.check_roundtrip("1 < 4 <= 5")
        self.check_roundtrip("a is b is c is not d")

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    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)
236

237 238
    def test_class_definition(self):
        self.check_roundtrip("class A(metaclass=type, *[], **{}): pass")
239

240 241 242 243
    def test_elifs(self):
        self.check_roundtrip(elif1)
        self.check_roundtrip(elif2)

244 245 246
    def test_try_except_finally(self):
        self.check_roundtrip(try_except_finally)

247 248 249 250 251 252
    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")

253 254 255 256 257 258 259 260 261
    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)

262

263 264 265 266 267 268
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')

269
    def test_files(self):
270 271 272 273
        # get names of files to test

        names = []
        for d in self.test_directories:
274
            test_dir = os.path.join(basepath, d)
275 276 277 278
            for n in os.listdir(test_dir):
                if n.endswith('.py') and not n.startswith('bad'):
                    names.append(os.path.join(test_dir, n))

279 280 281 282
        # Test limited subset of files unless the 'cpu' resource is specified.
        if not test.support.is_resource_enabled("cpu"):
            names = random.sample(names, 10)

283 284 285 286 287 288 289
        for filename in names:
            if test.support.verbose:
                print('Testing %s' % filename)
            source = read_pyfile(filename)
            self.check_roundtrip(source)


290
if __name__ == '__main__':
291
    unittest.main()