mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-07 13:24:39 +08:00
194 lines
5.8 KiB
Python
194 lines
5.8 KiB
Python
"""
|
|
Testing if parso finds syntax errors and indentation errors.
|
|
"""
|
|
import sys
|
|
from textwrap import dedent
|
|
|
|
import pytest
|
|
|
|
import parso
|
|
from parso.python.normalizer import ErrorFinderConfig
|
|
|
|
def _get_error_list(code, version=None):
|
|
tree = parso.parse(code, version=version)
|
|
config = ErrorFinderConfig()
|
|
return list(tree._get_normalizer_issues(config))
|
|
|
|
|
|
def assert_comparison(code, error_code, positions):
|
|
errors = [(error.start_pos, error.code) for error in _get_error_list(code)]
|
|
assert [(pos, error_code) for pos in positions] == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
('code', 'positions'), [
|
|
('1 +', [(1, 3)]),
|
|
('1 +\n', [(1, 3)]),
|
|
('1 +\n2 +', [(1, 3), (2, 3)]),
|
|
('x + 2', []),
|
|
('[\n', [(2, 0)]),
|
|
('[\ndef x(): pass', [(2, 0)]),
|
|
('[\nif 1: pass', [(2, 0)]),
|
|
('1+?', [(1, 2)]),
|
|
('?', [(1, 0)]),
|
|
('??', [(1, 0)]),
|
|
('? ?', [(1, 0)]),
|
|
('?\n?', [(1, 0), (2, 0)]),
|
|
('? * ?', [(1, 0)]),
|
|
('1 + * * 2', [(1, 4)]),
|
|
('?\n1\n?', [(1, 0), (3, 0)]),
|
|
]
|
|
)
|
|
def test_syntax_errors(code, positions):
|
|
assert_comparison(code, 901, positions)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
('code', 'positions'), [
|
|
(' 1', [(1, 0)]),
|
|
('def x():\n 1\n 2', [(3, 0)]),
|
|
('def x():\n 1\n 2', [(3, 0)]),
|
|
('def x():\n1', [(2, 0)]),
|
|
]
|
|
)
|
|
def test_indentation_errors(code, positions):
|
|
assert_comparison(code, 903, positions)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'code', [
|
|
'1 +',
|
|
'?',
|
|
dedent('''\
|
|
for a in [1]:
|
|
try:
|
|
pass
|
|
finally:
|
|
continue
|
|
'''), # 'continue' not supported inside 'finally' clause"
|
|
'continue',
|
|
'break',
|
|
'return',
|
|
'yield',
|
|
'try: pass\nexcept: pass\nexcept X: pass',
|
|
'f(x for x in bar, 1)',
|
|
|
|
# IndentationError
|
|
' foo',
|
|
'def x():\n 1\n 2',
|
|
'def x():\n 1\n 2',
|
|
'if 1:\nfoo',
|
|
]
|
|
)
|
|
def test_python_exception_matches(code):
|
|
error, = _get_error_list(code)
|
|
try:
|
|
compile(code, '<unknown>', 'exec')
|
|
except (SyntaxError, IndentationError) as e:
|
|
wanted = e.__class__.__name__ + ': ' + e.msg
|
|
else:
|
|
assert False, "The piece of code should raise an exception."
|
|
|
|
# SyntaxError
|
|
# Python 2.6 has a bit different error messages here, so skip it.
|
|
if sys.version_info[:2] == (2, 6) and wanted == 'SyntaxError: unexpected EOF while parsing':
|
|
wanted = 'SyntaxError: invalid syntax'
|
|
assert wanted == error.message
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
('code', 'version'), [
|
|
# SyntaxError
|
|
('async def bla():\n def x(): await bla()', '3.5'),
|
|
('yield from []', '3.5'),
|
|
('async def foo(): yield from []', '3.5'),
|
|
('async def foo():\n yield x\n return 1', '3.6'),
|
|
('async def foo():\n yield x\n return 1', '3.6'),
|
|
('*a, *b = 3, 3', '3.3'),
|
|
('*a = 3', '3.5'),
|
|
('del *a, b', '3.5'),
|
|
('def x(*): pass', '3.5'),
|
|
('async def foo():\n def nofoo():[x async for x in []]', '3.6'),
|
|
('[*[] for a in [1]]', '3.5'),
|
|
('{**{} for a in [1]}', '3.5'),
|
|
]
|
|
)
|
|
def test_python_exception_matches_version(code, version):
|
|
if '.'.join(str(v) for v in sys.version_info[:2]) != version:
|
|
pytest.skip()
|
|
|
|
error, = _get_error_list(code)
|
|
try:
|
|
compile(code, '<unknown>', 'exec')
|
|
except (SyntaxError, IndentationError) as e:
|
|
wanted = e.__class__.__name__ + ': ' + e.msg
|
|
else:
|
|
assert False, "The piece of code should raise an exception."
|
|
assert wanted == error.message
|
|
|
|
|
|
def test_statically_nested_blocks():
|
|
def indent(code):
|
|
lines = code.splitlines(True)
|
|
return ''.join([' ' + line for line in lines])
|
|
|
|
def build(code, depth):
|
|
if depth == 0:
|
|
return code
|
|
|
|
new_code = 'if 1:\n' + indent(code)
|
|
return build(new_code, depth - 1)
|
|
|
|
def get_error(depth, add_func=False):
|
|
code = build('foo', depth)
|
|
if add_func:
|
|
code = 'def bar():\n' + indent(code)
|
|
errors = _get_error_list(code)
|
|
if errors:
|
|
assert errors[0].message == 'SyntaxError: too many statically nested blocks'
|
|
return errors[0]
|
|
return None
|
|
|
|
assert get_error(19) is None
|
|
assert get_error(19, add_func=True) is None
|
|
|
|
assert get_error(20)
|
|
assert get_error(20, add_func=True)
|
|
|
|
|
|
def test_future_import_first():
|
|
def is_issue(code, *args):
|
|
code = code % args
|
|
return bool(_get_error_list(code))
|
|
|
|
i1 = 'from __future__ import division'
|
|
i2 = 'from __future__ import absolute_import'
|
|
assert not is_issue(i1)
|
|
assert not is_issue(i1 + ';' + i2)
|
|
assert not is_issue(i1 + '\n' + i2)
|
|
assert not is_issue('"";' + i1)
|
|
assert not is_issue('"";' + i1)
|
|
assert not is_issue('""\n' + i1)
|
|
assert not is_issue('""\n%s\n%s', i1, i2)
|
|
assert not is_issue('""\n%s;%s', i1, i2)
|
|
assert not is_issue('"";%s;%s ', i1, i2)
|
|
assert not is_issue('"";%s\n%s ', i1, i2)
|
|
assert is_issue('1;' + i1)
|
|
assert is_issue('1\n' + i1)
|
|
assert is_issue('"";1\n' + i1)
|
|
assert is_issue('""\n%s\nfrom x import a\n%s', i1, i2)
|
|
assert is_issue('%s\n""\n%s', i1, i2)
|
|
|
|
|
|
def test_named_argument_issues(works_not_in_py):
|
|
message = works_not_in_py.get_error_message('def foo(*, **dict): pass')
|
|
message = works_not_in_py.get_error_message('def foo(*): pass')
|
|
if works_not_in_py.version.startswith('2'):
|
|
assert message == 'SyntaxError: invalid syntax'
|
|
else:
|
|
assert message == 'SyntaxError: named arguments must follow bare *'
|
|
|
|
works_not_in_py.assert_no_error_in_passing('def foo(*, name): pass')
|
|
works_not_in_py.assert_no_error_in_passing('def foo(bar, *, name=1): pass')
|
|
works_not_in_py.assert_no_error_in_passing('def foo(bar, *, name=1, **dct): pass')
|