1# -*- coding: utf-8 -*- 2from textwrap import dedent 3 4import pytest 5 6from parso import parse 7from parso.python import tree 8from parso.utils import split_lines 9 10 11def test_basic_parsing(each_version): 12 def compare(string): 13 """Generates the AST object and then regenerates the code.""" 14 assert parse(string, version=each_version).get_code() == string 15 16 compare('\na #pass\n') 17 compare('wblabla* 1\t\n') 18 compare('def x(a, b:3): pass\n') 19 compare('assert foo\n') 20 21 22def test_subscope_names(each_version): 23 def get_sub(source): 24 return parse(source, version=each_version).children[0] 25 26 name = get_sub('class Foo: pass').name 27 assert name.start_pos == (1, len('class ')) 28 assert name.end_pos == (1, len('class Foo')) 29 assert name.value == 'Foo' 30 31 name = get_sub('def foo(): pass').name 32 assert name.start_pos == (1, len('def ')) 33 assert name.end_pos == (1, len('def foo')) 34 assert name.value == 'foo' 35 36 37def test_import_names(each_version): 38 def get_import(source): 39 return next(parse(source, version=each_version).iter_imports()) 40 41 imp = get_import('import math\n') 42 names = imp.get_defined_names() 43 assert len(names) == 1 44 assert names[0].value == 'math' 45 assert names[0].start_pos == (1, len('import ')) 46 assert names[0].end_pos == (1, len('import math')) 47 48 assert imp.start_pos == (1, 0) 49 assert imp.end_pos == (1, len('import math')) 50 51 52def test_end_pos(each_version): 53 s = dedent(''' 54 x = ['a', 'b', 'c'] 55 def func(): 56 y = None 57 ''') 58 parser = parse(s, version=each_version) 59 scope = next(parser.iter_funcdefs()) 60 assert scope.start_pos == (3, 0) 61 assert scope.end_pos == (5, 0) 62 63 64def test_carriage_return_statements(each_version): 65 source = dedent(''' 66 foo = 'ns1!' 67 68 # this is a namespace package 69 ''') 70 source = source.replace('\n', '\r\n') 71 stmt = parse(source, version=each_version).children[0] 72 assert '#' not in stmt.get_code() 73 74 75def test_incomplete_list_comprehension(each_version): 76 """ Shouldn't raise an error, same bug as #418. """ 77 # With the old parser this actually returned a statement. With the new 78 # parser only valid statements generate one. 79 children = parse('(1 for def', version=each_version).children 80 assert [c.type for c in children] == \ 81 ['error_node', 'error_node', 'endmarker'] 82 83 84def test_newline_positions(each_version): 85 endmarker = parse('a\n', version=each_version).children[-1] 86 assert endmarker.end_pos == (2, 0) 87 new_line = endmarker.get_previous_leaf() 88 assert new_line.start_pos == (1, 1) 89 assert new_line.end_pos == (2, 0) 90 91 92def test_end_pos_error_correction(each_version): 93 """ 94 Source code without ending newline are given one, because the Python 95 grammar needs it. However, they are removed again. We still want the right 96 end_pos, even if something breaks in the parser (error correction). 97 """ 98 s = 'def x():\n .' 99 m = parse(s, version=each_version) 100 func = m.children[0] 101 assert func.type == 'funcdef' 102 assert func.end_pos == (2, 2) 103 assert m.end_pos == (2, 2) 104 105 106def test_param_splitting(each_version): 107 """ 108 Jedi splits parameters into params, this is not what the grammar does, 109 but Jedi does this to simplify argument parsing. 110 """ 111 def check(src, result): 112 m = parse(src, version=each_version) 113 assert not list(m.iter_funcdefs()) 114 115 check('def x(a, (b, c)):\n pass', ['a']) 116 check('def x((b, c)):\n pass', []) 117 118 119def test_unicode_string(): 120 s = tree.String(None, 'bö', (0, 0)) 121 assert repr(s) # Should not raise an Error! 122 123 124def test_backslash_dos_style(each_version): 125 assert parse('\\\r\n', version=each_version) 126 127 128def test_started_lambda_stmt(each_version): 129 m = parse('lambda a, b: a i', version=each_version) 130 assert m.children[0].type == 'error_node' 131 132 133@pytest.mark.parametrize('code', ['foo "', 'foo """\n', 'foo """\nbar']) 134def test_open_string_literal(each_version, code): 135 """ 136 Testing mostly if removing the last newline works. 137 """ 138 lines = split_lines(code, keepends=True) 139 end_pos = (len(lines), len(lines[-1])) 140 module = parse(code, version=each_version) 141 assert module.get_code() == code 142 assert module.end_pos == end_pos == module.children[1].end_pos 143 144 145def test_too_many_params(): 146 with pytest.raises(TypeError): 147 parse('asdf', hello=3) 148 149 150def test_dedent_at_end(each_version): 151 code = dedent(''' 152 for foobar in [1]: 153 foobar''') 154 module = parse(code, version=each_version) 155 assert module.get_code() == code 156 suite = module.children[0].children[-1] 157 foobar = suite.children[-1] 158 assert foobar.type == 'name' 159 160 161def test_no_error_nodes(each_version): 162 def check(node): 163 assert node.type not in ('error_leaf', 'error_node') 164 165 try: 166 children = node.children 167 except AttributeError: 168 pass 169 else: 170 for child in children: 171 check(child) 172 173 check(parse("if foo:\n bar", version=each_version)) 174 175 176def test_named_expression(works_ge_py38): 177 works_ge_py38.parse("(a := 1, a + 1)") 178 179 180def test_extended_rhs_annassign(works_ge_py38): 181 works_ge_py38.parse("x: y = z,") 182 works_ge_py38.parse("x: Tuple[int, ...] = z, *q, w") 183 184 185@pytest.mark.parametrize( 186 'param_code', [ 187 'a=1, /', 188 'a, /', 189 'a=1, /, b=3', 190 'a, /, b', 191 'a, /, b', 192 'a, /, *, b', 193 'a, /, **kwargs', 194 ] 195) 196def test_positional_only_arguments(works_ge_py38, param_code): 197 works_ge_py38.parse("def x(%s): pass" % param_code) 198 199 200@pytest.mark.parametrize( 201 'expression', [ 202 'a + a', 203 'lambda x: x', 204 'a := lambda x: x' 205 ] 206) 207def test_decorator_expression(works_ge_py39, expression): 208 works_ge_py39.parse("@%s\ndef x(): pass" % expression) 209