Spaces:
Build error
Build error
# mode: run | |
# tag: syntax | |
""" | |
Uses TreeFragment to test invalid syntax. | |
""" | |
from __future__ import absolute_import | |
import ast | |
import textwrap | |
from ...TestUtils import CythonTest | |
from .. import ExprNodes | |
from ..Errors import CompileError | |
# Copied from CPython's test_grammar.py | |
VALID_UNDERSCORE_LITERALS = [ | |
'0_0_0', | |
'4_2', | |
'1_0000_0000', | |
'0b1001_0100', | |
'0xffff_ffff', | |
'0o5_7_7', | |
'1_00_00.5', | |
'1_00_00.5j', | |
'1_00_00.5e5', | |
'1_00_00j', | |
'1_00_00e5_1', | |
'1e1_0', | |
'.1_4', | |
'.1_4e1', | |
'0b_0', | |
'0x_f', | |
'0o_5', | |
'1_00_00j', | |
'1_00_00.5j', | |
'1_00_00e5_1j', | |
'.1_4j', | |
'(1_2.5+3_3j)', | |
'(.5_6j)', | |
] | |
# Copied from CPython's test_grammar.py | |
INVALID_UNDERSCORE_LITERALS = [ | |
# Trailing underscores: | |
'0_', | |
'42_', | |
'1.4j_', | |
'0x_', | |
'0b1_', | |
'0xf_', | |
'0o5_', | |
'0 if 1_Else 1', | |
# Underscores in the base selector: | |
'0_b0', | |
'0_xf', | |
'0_o5', | |
# Old-style octal, still disallowed: | |
# FIXME: still need to support PY_VERSION_HEX < 3 | |
'0_7', | |
'09_99', | |
# Multiple consecutive underscores: | |
'4_______2', | |
'0.1__4', | |
'0.1__4j', | |
'0b1001__0100', | |
'0xffff__ffff', | |
'0x___', | |
'0o5__77', | |
'1e1__0', | |
'1e1__0j', | |
# Underscore right before a dot: | |
'1_.4', | |
'1_.4j', | |
# Underscore right after a dot: | |
'1._4', | |
'1._4j', | |
'._5', | |
'._5j', | |
# Underscore right after a sign: | |
'1.0e+_1', | |
'1.0e+_1j', | |
# Underscore right before j: | |
'1.4_j', | |
'1.4e5_j', | |
# Underscore right before e: | |
'1_e1', | |
'1.4_e1', | |
'1.4_e1j', | |
# Underscore right after e: | |
'1e_1', | |
'1.4e_1', | |
'1.4e_1j', | |
# Complex cases with parens: | |
'(1+1.5_j_)', | |
'(1+1.5_j)', | |
# Whitespace in literals | |
'1_ 2', | |
'1 _2', | |
'1_2.2_ 1', | |
'1_2.2 _1', | |
'1_2e _1', | |
'1_2e2 _1', | |
'1_2e 2_1', | |
] | |
INVALID_ELLIPSIS = [ | |
(". . .", 2, 0), | |
(". ..", 2, 0), | |
(".. .", 2, 0), | |
(". ...", 2, 0), | |
(". ... .", 2, 0), | |
(".. ... .", 2, 0), | |
(". ... ..", 2, 0), | |
(""" | |
( | |
. | |
.. | |
) | |
""", 3, 4), | |
(""" | |
[ | |
.. | |
., | |
None | |
] | |
""", 3, 4), | |
(""" | |
{ | |
None, | |
. | |
. | |
. | |
} | |
""", 4, 4) | |
] | |
class TestGrammar(CythonTest): | |
def test_invalid_number_literals(self): | |
for literal in INVALID_UNDERSCORE_LITERALS: | |
for expression in ['%s', '1 + %s', '%s + 1', '2 * %s', '%s * 2']: | |
code = 'x = ' + expression % literal | |
try: | |
self.fragment(u'''\ | |
# cython: language_level=3 | |
''' + code) | |
except CompileError as exc: | |
assert code in [s.strip() for s in str(exc).splitlines()], str(exc) | |
else: | |
assert False, "Invalid Cython code '%s' failed to raise an exception" % code | |
def test_valid_number_literals(self): | |
for literal in VALID_UNDERSCORE_LITERALS: | |
for i, expression in enumerate(['%s', '1 + %s', '%s + 1', '2 * %s', '%s * 2']): | |
code = 'x = ' + expression % literal | |
node = self.fragment(u'''\ | |
# cython: language_level=3 | |
''' + code).root | |
assert node is not None | |
literal_node = node.stats[0].rhs # StatListNode([SingleAssignmentNode('x', expr)]) | |
if i > 0: | |
# Add/MulNode() -> literal is first or second operand | |
literal_node = literal_node.operand2 if i % 2 else literal_node.operand1 | |
if 'j' in literal or 'J' in literal: | |
if '+' in literal: | |
# FIXME: tighten this test | |
assert isinstance(literal_node, ExprNodes.AddNode), (literal, literal_node) | |
else: | |
assert isinstance(literal_node, ExprNodes.ImagNode), (literal, literal_node) | |
elif '.' in literal or 'e' in literal or 'E' in literal and not ('0x' in literal or '0X' in literal): | |
assert isinstance(literal_node, ExprNodes.FloatNode), (literal, literal_node) | |
else: | |
assert isinstance(literal_node, ExprNodes.IntNode), (literal, literal_node) | |
def test_invalid_ellipsis(self): | |
ERR = ":{0}:{1}: Expected an identifier or literal" | |
for code, line, col in INVALID_ELLIPSIS: | |
try: | |
ast.parse(textwrap.dedent(code)) | |
except SyntaxError as exc: | |
assert True | |
else: | |
assert False, "Invalid Python code '%s' failed to raise an exception" % code | |
try: | |
self.fragment(u'''\ | |
# cython: language_level=3 | |
''' + code) | |
except CompileError as exc: | |
assert ERR.format(line, col) in str(exc), str(exc) | |
else: | |
assert False, "Invalid Cython code '%s' failed to raise an exception" % code | |
if __name__ == "__main__": | |
import unittest | |
unittest.main() | |