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() | |