204 lines
5.0 KiB
Python
204 lines
5.0 KiB
Python
|
# 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()
|