Add the each_version fixture to a lot more parser tests.

This commit is contained in:
Dave Halter
2017-07-12 23:09:21 +02:00
parent 161c51b050
commit b1dabc8596

View File

@@ -1,20 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys
from textwrap import dedent from textwrap import dedent
import pytest import pytest
from parso._compatibility import u, py_version from parso._compatibility import u
from parso import parse from parso import parse
from parso import load_grammar
from parso.python import tree from parso.python import tree
from parso.utils import splitlines from parso.utils import splitlines
def test_basic_parsing(): def test_basic_parsing(each_version):
def compare(string): def compare(string):
"""Generates the AST object and then regenerates the code.""" """Generates the AST object and then regenerates the code."""
assert parse(string).get_code() == string assert parse(string, version=each_version).get_code() == string
compare('\na #pass\n') compare('\na #pass\n')
compare('wblabla* 1\t\n') compare('wblabla* 1\t\n')
@@ -22,9 +20,9 @@ def test_basic_parsing():
compare('assert foo\n') compare('assert foo\n')
def test_subscope_names(): def test_subscope_names(each_version):
def get_sub(source): def get_sub(source):
return parse(source).children[0] return parse(source, version=each_version).children[0]
name = get_sub('class Foo: pass').name name = get_sub('class Foo: pass').name
assert name.start_pos == (1, len('class ')) assert name.start_pos == (1, len('class '))
@@ -37,9 +35,9 @@ def test_subscope_names():
assert name.value == 'foo' assert name.value == 'foo'
def test_import_names(): def test_import_names(each_version):
def get_import(source): def get_import(source):
return next(parse(source).iter_imports()) return next(parse(source, version=each_version).iter_imports())
imp = get_import('import math\n') imp = get_import('import math\n')
names = imp.get_defined_names() names = imp.get_defined_names()
@@ -52,77 +50,76 @@ def test_import_names():
assert imp.end_pos == (1, len('import math')) assert imp.end_pos == (1, len('import math'))
def test_end_pos(): def test_end_pos(each_version):
s = dedent(''' s = dedent('''
x = ['a', 'b', 'c'] x = ['a', 'b', 'c']
def func(): def func():
y = None y = None
''') ''')
parser = parse(s) parser = parse(s, version=each_version)
scope = next(parser.iter_funcdefs()) scope = next(parser.iter_funcdefs())
assert scope.start_pos == (3, 0) assert scope.start_pos == (3, 0)
assert scope.end_pos == (5, 0) assert scope.end_pos == (5, 0)
def test_carriage_return_statements(): def test_carriage_return_statements(each_version):
source = dedent(''' source = dedent('''
foo = 'ns1!' foo = 'ns1!'
# this is a namespace package # this is a namespace package
''') ''')
source = source.replace('\n', '\r\n') source = source.replace('\n', '\r\n')
stmt = parse(source).children[0] stmt = parse(source, version=each_version).children[0]
assert '#' not in stmt.get_code() assert '#' not in stmt.get_code()
def test_incomplete_list_comprehension(): def test_incomplete_list_comprehension(each_version):
""" Shouldn't raise an error, same bug as #418. """ """ Shouldn't raise an error, same bug as #418. """
# With the old parser this actually returned a statement. With the new # With the old parser this actually returned a statement. With the new
# parser only valid statements generate one. # parser only valid statements generate one.
children = parse('(1 for def').children children = parse('(1 for def', version=each_version).children
assert [c.type for c in children] == \ assert [c.type for c in children] == \
['error_node', 'error_node', 'endmarker'] ['error_node', 'error_node', 'endmarker']
def test_newline_positions(): def test_newline_positions(each_version):
endmarker = parse('a\n').children[-1] endmarker = parse('a\n', version=each_version).children[-1]
assert endmarker.end_pos == (2, 0) assert endmarker.end_pos == (2, 0)
new_line = endmarker.get_previous_leaf() new_line = endmarker.get_previous_leaf()
assert new_line.start_pos == (1, 1) assert new_line.start_pos == (1, 1)
assert new_line.end_pos == (2, 0) assert new_line.end_pos == (2, 0)
def test_end_pos_error_correction(): def test_end_pos_error_correction(each_version):
""" """
Source code without ending newline are given one, because the Python Source code without ending newline are given one, because the Python
grammar needs it. However, they are removed again. We still want the right grammar needs it. However, they are removed again. We still want the right
end_pos, even if something breaks in the parser (error correction). end_pos, even if something breaks in the parser (error correction).
""" """
s = 'def x():\n .' s = 'def x():\n .'
m = parse(s) m = parse(s, version=each_version)
func = m.children[0] func = m.children[0]
assert func.type == 'funcdef' assert func.type == 'funcdef'
assert func.end_pos == (2, 2) assert func.end_pos == (2, 2)
assert m.end_pos == (2, 2) assert m.end_pos == (2, 2)
def test_param_splitting(): def test_param_splitting(each_version):
""" """
Jedi splits parameters into params, this is not what the grammar does, Jedi splits parameters into params, this is not what the grammar does,
but Jedi does this to simplify argument parsing. but Jedi does this to simplify argument parsing.
""" """
def check(src, result): def check(src, result):
# Python 2 tuple params should be ignored for now. # Python 2 tuple params should be ignored for now.
grammar = load_grammar('%s.%s' % sys.version_info[:2]) m = parse(src, version=each_version)
m = grammar.parse(src) if each_version.startswith('2'):
if py_version >= 30:
assert not list(m.iter_funcdefs())
else:
# We don't want b and c to be a part of the param enumeration. Just # We don't want b and c to be a part of the param enumeration. Just
# ignore them, because it's not what we want to support in the # ignore them, because it's not what we want to support in the
# future. # future.
func = next(m.iter_funcdefs()) func = next(m.iter_funcdefs())
assert [param.name.value for param in func.params] == result assert [param.name.value for param in func.params] == result
else:
assert not list(m.iter_funcdefs())
check('def x(a, (b, c)):\n pass', ['a']) check('def x(a, (b, c)):\n pass', ['a'])
check('def x((b, c)):\n pass', []) check('def x((b, c)):\n pass', [])
@@ -133,32 +130,32 @@ def test_unicode_string():
assert repr(s) # Should not raise an Error! assert repr(s) # Should not raise an Error!
def test_backslash_dos_style(): def test_backslash_dos_style(each_version):
assert parse('\\\r\n') assert parse('\\\r\n', version=each_version)
def test_started_lambda_stmt(): def test_started_lambda_stmt(each_version):
m = parse(u'lambda a, b: a i') m = parse(u'lambda a, b: a i', version=each_version)
assert m.children[0].type == 'error_node' assert m.children[0].type == 'error_node'
def test_python2_octal(each_version): def test_python2_octal(each_version):
module = parse('0660', version=each_version) module = parse('0660', version=each_version)
first = module.children[0] first = module.children[0]
if each_version.startswith('3'): if each_version.startswith('2'):
assert first.type == 'error_node'
else:
assert first.type == 'number' assert first.type == 'number'
else:
assert first.type == 'error_node'
@pytest.mark.parametrize('code', ['foo "', 'foo """\n', 'foo """\nbar']) @pytest.mark.parametrize('code', ['foo "', 'foo """\n', 'foo """\nbar'])
def test_open_string_literal(code): def test_open_string_literal(each_version, code):
""" """
Testing mostly if removing the last newline works. Testing mostly if removing the last newline works.
""" """
lines = splitlines(code, keepends=True) lines = splitlines(code, keepends=True)
end_pos = (len(lines), len(lines[-1])) end_pos = (len(lines), len(lines[-1]))
module = parse(code) module = parse(code, version=each_version)
assert module.get_code() == code assert module.get_code() == code
assert module.end_pos == end_pos == module.children[1].end_pos assert module.end_pos == end_pos == module.children[1].end_pos
@@ -168,18 +165,18 @@ def test_too_many_params():
parse('asdf', hello=3) parse('asdf', hello=3)
def test_dedent_at_end(): def test_dedent_at_end(each_version):
code = dedent(''' code = dedent('''
for foobar in [1]: for foobar in [1]:
foobar''') foobar''')
module = parse(code) module = parse(code, version=each_version)
assert module.get_code() == code assert module.get_code() == code
suite = module.children[0].children[-1] suite = module.children[0].children[-1]
foobar = suite.children[-1] foobar = suite.children[-1]
assert foobar.type == 'name' assert foobar.type == 'name'
def test_no_error_nodes(): def test_no_error_nodes(each_version):
def check(node): def check(node):
assert node.type not in ('error_leaf', 'error_node') assert node.type not in ('error_leaf', 'error_node')
@@ -191,4 +188,4 @@ def test_no_error_nodes():
for child in children: for child in children:
check(child) check(child)
check(parse("if foo:\n bar")) check(parse("if foo:\n bar", version=each_version))