blob: b0cdb9850be32282ceaf79b8666179a35d7423b5 [file] [log] [blame]
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
import textwrap
import pytest
import astroid
from astroid import builder, nodes
from astroid.const import IS_PYPY, PY38, PY39_PLUS, PY310_PLUS, PY312_PLUS
@pytest.mark.skipif(
not (PY38 and IS_PYPY),
reason="end_lineno and end_col_offset were added in PY38",
)
class TestEndLinenoNotSet:
"""Test 'end_lineno' and 'end_col_offset' are initialized as 'None' for Python <
3.8.
"""
@staticmethod
def test_end_lineno_not_set() -> None:
code = textwrap.dedent(
"""
[1, 2, 3] #@
var #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 2
n1 = ast_nodes[0]
assert isinstance(n1, nodes.List)
assert (n1.lineno, n1.col_offset) == (1, 0)
assert (n1.end_lineno, n1.end_col_offset) == (None, None)
n2 = ast_nodes[1]
assert isinstance(n2, nodes.Name)
assert (n2.lineno, n2.col_offset) == (2, 0)
assert (n2.end_lineno, n2.end_col_offset) == (None, None)
@pytest.mark.skipif(
PY38 and IS_PYPY,
reason="end_lineno and end_col_offset were added in PY38",
)
class TestLinenoColOffset:
"""Test 'lineno', 'col_offset', 'end_lineno', and 'end_col_offset' for all
nodes.
"""
@staticmethod
def test_end_lineno_container() -> None:
"""Container nodes: List, Tuple, Set."""
code = textwrap.dedent(
"""
[1, 2, 3] #@
[ #@
1, 2, 3
]
(1, 2, 3) #@
{1, 2, 3} #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 4
c1 = ast_nodes[0]
assert isinstance(c1, nodes.List)
assert (c1.lineno, c1.col_offset) == (1, 0)
assert (c1.end_lineno, c1.end_col_offset) == (1, 9)
c2 = ast_nodes[1]
assert isinstance(c2, nodes.List)
assert (c2.lineno, c2.col_offset) == (2, 0)
assert (c2.end_lineno, c2.end_col_offset) == (4, 1)
c3 = ast_nodes[2]
assert isinstance(c3, nodes.Tuple)
assert (c3.lineno, c3.col_offset) == (5, 0)
assert (c3.end_lineno, c3.end_col_offset) == (5, 9)
c4 = ast_nodes[3]
assert isinstance(c4, nodes.Set)
assert (c4.lineno, c4.col_offset) == (6, 0)
assert (c4.end_lineno, c4.end_col_offset) == (6, 9)
@staticmethod
def test_end_lineno_name() -> None:
"""Name, Assign, AssignName, Delete, DelName."""
code = textwrap.dedent(
"""
var = 42 #@
var #@
del var #@
var2 = ( #@
1, 2, 3
)
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 4
n1 = ast_nodes[0]
assert isinstance(n1, nodes.Assign)
assert isinstance(n1.targets[0], nodes.AssignName)
assert isinstance(n1.value, nodes.Const)
assert (n1.lineno, n1.col_offset) == (1, 0)
assert (n1.end_lineno, n1.end_col_offset) == (1, 8)
assert (n1.targets[0].lineno, n1.targets[0].col_offset) == (1, 0)
assert (n1.targets[0].end_lineno, n1.targets[0].end_col_offset) == (1, 3)
assert (n1.value.lineno, n1.value.col_offset) == (1, 6)
assert (n1.value.end_lineno, n1.value.end_col_offset) == (1, 8)
n2 = ast_nodes[1]
assert isinstance(n2, nodes.Name)
assert (n2.lineno, n2.col_offset) == (2, 0)
assert (n2.end_lineno, n2.end_col_offset) == (2, 3)
n3 = ast_nodes[2]
assert isinstance(n3, nodes.Delete) and isinstance(n3.targets[0], nodes.DelName)
assert (n3.lineno, n3.col_offset) == (3, 0)
assert (n3.end_lineno, n3.end_col_offset) == (3, 7)
assert (n3.targets[0].lineno, n3.targets[0].col_offset) == (3, 4)
assert (n3.targets[0].end_lineno, n3.targets[0].end_col_offset) == (3, 7)
n4 = ast_nodes[3]
assert isinstance(n4, nodes.Assign)
assert isinstance(n4.targets[0], nodes.AssignName)
assert (n4.lineno, n4.col_offset) == (5, 0)
assert (n4.end_lineno, n4.end_col_offset) == (7, 1)
assert (n4.targets[0].lineno, n4.targets[0].col_offset) == (5, 0)
assert (n4.targets[0].end_lineno, n4.targets[0].end_col_offset) == (5, 4)
@staticmethod
def test_end_lineno_attribute() -> None:
"""Attribute, AssignAttr, DelAttr."""
code = textwrap.dedent(
"""
class X:
var = 42
X.var2 = 2 #@
X.var2 #@
del X.var2 #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 3
a1 = ast_nodes[0]
assert isinstance(a1, nodes.Assign)
assert isinstance(a1.targets[0], nodes.AssignAttr)
assert isinstance(a1.value, nodes.Const)
assert (a1.lineno, a1.col_offset) == (4, 0)
assert (a1.end_lineno, a1.end_col_offset) == (4, 10)
assert (a1.targets[0].lineno, a1.targets[0].col_offset) == (4, 0)
assert (a1.targets[0].end_lineno, a1.targets[0].end_col_offset) == (4, 6)
assert (a1.value.lineno, a1.value.col_offset) == (4, 9)
assert (a1.value.end_lineno, a1.value.end_col_offset) == (4, 10)
a2 = ast_nodes[1]
assert isinstance(a2, nodes.Attribute) and isinstance(a2.expr, nodes.Name)
assert (a2.lineno, a2.col_offset) == (5, 0)
assert (a2.end_lineno, a2.end_col_offset) == (5, 6)
assert (a2.expr.lineno, a2.expr.col_offset) == (5, 0)
assert (a2.expr.end_lineno, a2.expr.end_col_offset) == (5, 1)
a3 = ast_nodes[2]
assert isinstance(a3, nodes.Delete) and isinstance(a3.targets[0], nodes.DelAttr)
assert (a3.lineno, a3.col_offset) == (6, 0)
assert (a3.end_lineno, a3.end_col_offset) == (6, 10)
assert (a3.targets[0].lineno, a3.targets[0].col_offset) == (6, 4)
assert (a3.targets[0].end_lineno, a3.targets[0].end_col_offset) == (6, 10)
@staticmethod
def test_end_lineno_call() -> None:
"""Call, Keyword."""
code = textwrap.dedent(
"""
func(arg1, arg2=value) #@
"""
).strip()
c1 = builder.extract_node(code)
assert isinstance(c1, nodes.Call)
assert isinstance(c1.func, nodes.Name)
assert isinstance(c1.args[0], nodes.Name)
assert isinstance(c1.keywords[0], nodes.Keyword)
assert isinstance(c1.keywords[0].value, nodes.Name)
assert (c1.lineno, c1.col_offset) == (1, 0)
assert (c1.end_lineno, c1.end_col_offset) == (1, 22)
assert (c1.func.lineno, c1.func.col_offset) == (1, 0)
assert (c1.func.end_lineno, c1.func.end_col_offset) == (1, 4)
assert (c1.args[0].lineno, c1.args[0].col_offset) == (1, 5)
assert (c1.args[0].end_lineno, c1.args[0].end_col_offset) == (1, 9)
# fmt: off
if PY39_PLUS:
# 'lineno' and 'col_offset' information only added in Python 3.9
assert (c1.keywords[0].lineno, c1.keywords[0].col_offset) == (1, 11)
assert (c1.keywords[0].end_lineno, c1.keywords[0].end_col_offset) == (1, 21)
else:
assert (c1.keywords[0].lineno, c1.keywords[0].col_offset) == (None, None)
assert (c1.keywords[0].end_lineno, c1.keywords[0].end_col_offset) == (None, None)
assert (c1.keywords[0].value.lineno, c1.keywords[0].value.col_offset) == (1, 16)
assert (c1.keywords[0].value.end_lineno, c1.keywords[0].value.end_col_offset) == (1, 21)
# fmt: on
@staticmethod
def test_end_lineno_assignment() -> None:
"""Assign, AnnAssign, AugAssign."""
code = textwrap.dedent(
"""
var = 2 #@
var2: int = 2 #@
var3 += 2 #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 3
a1 = ast_nodes[0]
assert isinstance(a1, nodes.Assign)
assert isinstance(a1.targets[0], nodes.AssignName)
assert isinstance(a1.value, nodes.Const)
assert (a1.lineno, a1.col_offset) == (1, 0)
assert (a1.end_lineno, a1.end_col_offset) == (1, 7)
assert (a1.targets[0].lineno, a1.targets[0].col_offset) == (1, 0)
assert (a1.targets[0].end_lineno, a1.targets[0].end_col_offset) == (1, 3)
assert (a1.value.lineno, a1.value.col_offset) == (1, 6)
assert (a1.value.end_lineno, a1.value.end_col_offset) == (1, 7)
a2 = ast_nodes[1]
assert isinstance(a2, nodes.AnnAssign)
assert isinstance(a2.target, nodes.AssignName)
assert isinstance(a2.annotation, nodes.Name)
assert isinstance(a2.value, nodes.Const)
assert (a2.lineno, a2.col_offset) == (2, 0)
assert (a2.end_lineno, a2.end_col_offset) == (2, 13)
assert (a2.target.lineno, a2.target.col_offset) == (2, 0)
assert (a2.target.end_lineno, a2.target.end_col_offset) == (2, 4)
assert (a2.annotation.lineno, a2.annotation.col_offset) == (2, 6)
assert (a2.annotation.end_lineno, a2.annotation.end_col_offset) == (2, 9)
assert (a2.value.lineno, a2.value.col_offset) == (2, 12)
assert (a2.value.end_lineno, a2.value.end_col_offset) == (2, 13)
a3 = ast_nodes[2]
assert isinstance(a3, nodes.AugAssign)
assert isinstance(a3.target, nodes.AssignName)
assert isinstance(a3.value, nodes.Const)
assert (a3.lineno, a3.col_offset) == (3, 0)
assert (a3.end_lineno, a3.end_col_offset) == (3, 9)
assert (a3.target.lineno, a3.target.col_offset) == (3, 0)
assert (a3.target.end_lineno, a3.target.end_col_offset) == (3, 4)
assert (a3.value.lineno, a3.value.col_offset) == (3, 8)
assert (a3.value.end_lineno, a3.value.end_col_offset) == (3, 9)
@staticmethod
def test_end_lineno_mix_stmts() -> None:
"""Assert, Break, Continue, Global, Nonlocal, Pass, Raise, Return, Expr."""
code = textwrap.dedent(
"""
assert True, "Some message" #@
break #@
continue #@
global var #@
nonlocal var #@
pass #@
raise Exception from ex #@
return 42 #@
var #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 9
s1 = ast_nodes[0]
assert isinstance(s1, nodes.Assert)
assert isinstance(s1.test, nodes.Const)
assert isinstance(s1.fail, nodes.Const)
assert (s1.lineno, s1.col_offset) == (1, 0)
assert (s1.end_lineno, s1.end_col_offset) == (1, 27)
assert (s1.test.lineno, s1.test.col_offset) == (1, 7)
assert (s1.test.end_lineno, s1.test.end_col_offset) == (1, 11)
assert (s1.fail.lineno, s1.fail.col_offset) == (1, 13)
assert (s1.fail.end_lineno, s1.fail.end_col_offset) == (1, 27)
s2 = ast_nodes[1]
assert isinstance(s2, nodes.Break)
assert (s2.lineno, s2.col_offset) == (2, 0)
assert (s2.end_lineno, s2.end_col_offset) == (2, 5)
s3 = ast_nodes[2]
assert isinstance(s3, nodes.Continue)
assert (s3.lineno, s3.col_offset) == (3, 0)
assert (s3.end_lineno, s3.end_col_offset) == (3, 8)
s4 = ast_nodes[3]
assert isinstance(s4, nodes.Global)
assert (s4.lineno, s4.col_offset) == (4, 0)
assert (s4.end_lineno, s4.end_col_offset) == (4, 10)
s5 = ast_nodes[4]
assert isinstance(s5, nodes.Nonlocal)
assert (s5.lineno, s5.col_offset) == (5, 0)
assert (s5.end_lineno, s5.end_col_offset) == (5, 12)
s6 = ast_nodes[5]
assert isinstance(s6, nodes.Pass)
assert (s6.lineno, s6.col_offset) == (6, 0)
assert (s6.end_lineno, s6.end_col_offset) == (6, 4)
s7 = ast_nodes[6]
assert isinstance(s7, nodes.Raise)
assert isinstance(s7.exc, nodes.Name)
assert isinstance(s7.cause, nodes.Name)
assert (s7.lineno, s7.col_offset) == (7, 0)
assert (s7.end_lineno, s7.end_col_offset) == (7, 23)
assert (s7.exc.lineno, s7.exc.col_offset) == (7, 6)
assert (s7.exc.end_lineno, s7.exc.end_col_offset) == (7, 15)
assert (s7.cause.lineno, s7.cause.col_offset) == (7, 21)
assert (s7.cause.end_lineno, s7.cause.end_col_offset) == (7, 23)
s8 = ast_nodes[7]
assert isinstance(s8, nodes.Return)
assert isinstance(s8.value, nodes.Const)
assert (s8.lineno, s8.col_offset) == (8, 0)
assert (s8.end_lineno, s8.end_col_offset) == (8, 9)
assert (s8.value.lineno, s8.value.col_offset) == (8, 7)
assert (s8.value.end_lineno, s8.value.end_col_offset) == (8, 9)
s9 = ast_nodes[8].parent
assert isinstance(s9, nodes.Expr)
assert isinstance(s9.value, nodes.Name)
assert (s9.lineno, s9.col_offset) == (9, 0)
assert (s9.end_lineno, s9.end_col_offset) == (9, 3)
assert (s9.value.lineno, s9.value.col_offset) == (9, 0)
assert (s9.value.end_lineno, s9.value.end_col_offset) == (9, 3)
@staticmethod
def test_end_lineno_mix_nodes() -> None:
"""Await, Starred, Yield, YieldFrom."""
code = textwrap.dedent(
"""
await func #@
*args #@
yield 42 #@
yield from (1, 2) #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 4
n1 = ast_nodes[0]
assert isinstance(n1, nodes.Await)
assert isinstance(n1.value, nodes.Name)
assert (n1.lineno, n1.col_offset) == (1, 0)
assert (n1.end_lineno, n1.end_col_offset) == (1, 10)
assert (n1.value.lineno, n1.value.col_offset) == (1, 6)
assert (n1.value.end_lineno, n1.value.end_col_offset) == (1, 10)
n2 = ast_nodes[1]
assert isinstance(n2, nodes.Starred)
assert isinstance(n2.value, nodes.Name)
assert (n2.lineno, n2.col_offset) == (2, 0)
assert (n2.end_lineno, n2.end_col_offset) == (2, 5)
assert (n2.value.lineno, n2.value.col_offset) == (2, 1)
assert (n2.value.end_lineno, n2.value.end_col_offset) == (2, 5)
n3 = ast_nodes[2]
assert isinstance(n3, nodes.Yield)
assert isinstance(n3.value, nodes.Const)
assert (n3.lineno, n3.col_offset) == (3, 0)
assert (n3.end_lineno, n3.end_col_offset) == (3, 8)
assert (n3.value.lineno, n3.value.col_offset) == (3, 6)
assert (n3.value.end_lineno, n3.value.end_col_offset) == (3, 8)
n4 = ast_nodes[3]
assert isinstance(n4, nodes.YieldFrom)
assert isinstance(n4.value, nodes.Tuple)
assert (n4.lineno, n4.col_offset) == (4, 0)
assert (n4.end_lineno, n4.end_col_offset) == (4, 17)
assert (n4.value.lineno, n4.value.col_offset) == (4, 11)
assert (n4.value.end_lineno, n4.value.end_col_offset) == (4, 17)
@staticmethod
def test_end_lineno_ops() -> None:
"""BinOp, BoolOp, UnaryOp, Compare."""
code = textwrap.dedent(
"""
x + y #@
a and b #@
-var #@
a < b #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 4
o1 = ast_nodes[0]
assert isinstance(o1, nodes.BinOp)
assert isinstance(o1.left, nodes.Name)
assert isinstance(o1.right, nodes.Name)
assert (o1.lineno, o1.col_offset) == (1, 0)
assert (o1.end_lineno, o1.end_col_offset) == (1, 5)
assert (o1.left.lineno, o1.left.col_offset) == (1, 0)
assert (o1.left.end_lineno, o1.left.end_col_offset) == (1, 1)
assert (o1.right.lineno, o1.right.col_offset) == (1, 4)
assert (o1.right.end_lineno, o1.right.end_col_offset) == (1, 5)
o2 = ast_nodes[1]
assert isinstance(o2, nodes.BoolOp)
assert isinstance(o2.values[0], nodes.Name)
assert isinstance(o2.values[1], nodes.Name)
assert (o2.lineno, o2.col_offset) == (2, 0)
assert (o2.end_lineno, o2.end_col_offset) == (2, 7)
assert (o2.values[0].lineno, o2.values[0].col_offset) == (2, 0)
assert (o2.values[0].end_lineno, o2.values[0].end_col_offset) == (2, 1)
assert (o2.values[1].lineno, o2.values[1].col_offset) == (2, 6)
assert (o2.values[1].end_lineno, o2.values[1].end_col_offset) == (2, 7)
o3 = ast_nodes[2]
assert isinstance(o3, nodes.UnaryOp)
assert isinstance(o3.operand, nodes.Name)
assert (o3.lineno, o3.col_offset) == (3, 0)
assert (o3.end_lineno, o3.end_col_offset) == (3, 4)
assert (o3.operand.lineno, o3.operand.col_offset) == (3, 1)
assert (o3.operand.end_lineno, o3.operand.end_col_offset) == (3, 4)
o4 = ast_nodes[3]
assert isinstance(o4, nodes.Compare)
assert isinstance(o4.left, nodes.Name)
assert isinstance(o4.ops[0][1], nodes.Name)
assert (o4.lineno, o4.col_offset) == (4, 0)
assert (o4.end_lineno, o4.end_col_offset) == (4, 5)
assert (o4.left.lineno, o4.left.col_offset) == (4, 0)
assert (o4.left.end_lineno, o4.left.end_col_offset) == (4, 1)
assert (o4.ops[0][1].lineno, o4.ops[0][1].col_offset) == (4, 4)
assert (o4.ops[0][1].end_lineno, o4.ops[0][1].end_col_offset) == (4, 5)
@staticmethod
def test_end_lineno_if() -> None:
"""If, IfExp, NamedExpr."""
code = textwrap.dedent(
"""
if ( #@
var := 2 #@
):
pass
else:
pass
2 if True else 1 #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 3
i1 = ast_nodes[0]
assert isinstance(i1, nodes.If)
assert isinstance(i1.test, nodes.NamedExpr)
assert isinstance(i1.body[0], nodes.Pass)
assert isinstance(i1.orelse[0], nodes.Pass)
assert (i1.lineno, i1.col_offset) == (1, 0)
assert (i1.end_lineno, i1.end_col_offset) == (6, 8)
assert (i1.test.lineno, i1.test.col_offset) == (2, 4)
assert (i1.test.end_lineno, i1.test.end_col_offset) == (2, 12)
assert (i1.body[0].lineno, i1.body[0].col_offset) == (4, 4)
assert (i1.body[0].end_lineno, i1.body[0].end_col_offset) == (4, 8)
assert (i1.orelse[0].lineno, i1.orelse[0].col_offset) == (6, 4)
assert (i1.orelse[0].end_lineno, i1.orelse[0].end_col_offset) == (6, 8)
i2 = ast_nodes[1]
assert isinstance(i2, nodes.NamedExpr)
assert isinstance(i2.target, nodes.AssignName)
assert isinstance(i2.value, nodes.Const)
assert (i2.lineno, i2.col_offset) == (2, 4)
assert (i2.end_lineno, i2.end_col_offset) == (2, 12)
assert (i2.target.lineno, i2.target.col_offset) == (2, 4)
assert (i2.target.end_lineno, i2.target.end_col_offset) == (2, 7)
assert (i2.value.lineno, i2.value.col_offset) == (2, 11)
assert (i2.value.end_lineno, i2.value.end_col_offset) == (2, 12)
i3 = ast_nodes[2]
assert isinstance(i3, nodes.IfExp)
assert isinstance(i3.test, nodes.Const)
assert isinstance(i3.body, nodes.Const)
assert isinstance(i3.orelse, nodes.Const)
assert (i3.lineno, i3.col_offset) == (8, 0)
assert (i3.end_lineno, i3.end_col_offset) == (8, 16)
assert (i3.test.lineno, i3.test.col_offset) == (8, 5)
assert (i3.test.end_lineno, i3.test.end_col_offset) == (8, 9)
assert (i3.body.lineno, i3.body.col_offset) == (8, 0)
assert (i3.body.end_lineno, i3.body.end_col_offset) == (8, 1)
assert (i3.orelse.lineno, i3.orelse.col_offset) == (8, 15)
assert (i3.orelse.end_lineno, i3.orelse.end_col_offset) == (8, 16)
@staticmethod
def test_end_lineno_for() -> None:
"""For, AsyncFor."""
code = textwrap.dedent(
"""
for i in lst: #@
pass
else:
pass
async for i in lst: #@
pass
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 2
f1 = ast_nodes[0]
assert isinstance(f1, nodes.For)
assert isinstance(f1.target, nodes.AssignName)
assert isinstance(f1.iter, nodes.Name)
assert isinstance(f1.body[0], nodes.Pass)
assert isinstance(f1.orelse[0], nodes.Pass)
assert (f1.lineno, f1.col_offset) == (1, 0)
assert (f1.end_lineno, f1.end_col_offset) == (4, 8)
assert (f1.target.lineno, f1.target.col_offset) == (1, 4)
assert (f1.target.end_lineno, f1.target.end_col_offset) == (1, 5)
assert (f1.iter.lineno, f1.iter.col_offset) == (1, 9)
assert (f1.iter.end_lineno, f1.iter.end_col_offset) == (1, 12)
assert (f1.body[0].lineno, f1.body[0].col_offset) == (2, 4)
assert (f1.body[0].end_lineno, f1.body[0].end_col_offset) == (2, 8)
assert (f1.orelse[0].lineno, f1.orelse[0].col_offset) == (4, 4)
assert (f1.orelse[0].end_lineno, f1.orelse[0].end_col_offset) == (4, 8)
f2 = ast_nodes[1]
assert isinstance(f2, nodes.AsyncFor)
assert isinstance(f2.target, nodes.AssignName)
assert isinstance(f2.iter, nodes.Name)
assert isinstance(f2.body[0], nodes.Pass)
assert (f2.lineno, f2.col_offset) == (6, 0)
assert (f2.end_lineno, f2.end_col_offset) == (7, 8)
assert (f2.target.lineno, f2.target.col_offset) == (6, 10)
assert (f2.target.end_lineno, f2.target.end_col_offset) == (6, 11)
assert (f2.iter.lineno, f2.iter.col_offset) == (6, 15)
assert (f2.iter.end_lineno, f2.iter.end_col_offset) == (6, 18)
assert (f2.body[0].lineno, f2.body[0].col_offset) == (7, 4)
assert (f2.body[0].end_lineno, f2.body[0].end_col_offset) == (7, 8)
@staticmethod
def test_end_lineno_const() -> None:
"""Const (int, str, bool, None, bytes, ellipsis)."""
code = textwrap.dedent(
"""
2 #@
"Hello" #@
True #@
None #@
b"01" #@
... #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 6
c1 = ast_nodes[0]
assert isinstance(c1, nodes.Const)
assert (c1.lineno, c1.col_offset) == (1, 0)
assert (c1.end_lineno, c1.end_col_offset) == (1, 1)
c2 = ast_nodes[1]
assert isinstance(c2, nodes.Const)
assert (c2.lineno, c2.col_offset) == (2, 0)
assert (c2.end_lineno, c2.end_col_offset) == (2, 7)
c3 = ast_nodes[2]
assert isinstance(c3, nodes.Const)
assert (c3.lineno, c3.col_offset) == (3, 0)
assert (c3.end_lineno, c3.end_col_offset) == (3, 4)
c4 = ast_nodes[3]
assert isinstance(c4, nodes.Const)
assert (c4.lineno, c4.col_offset) == (4, 0)
assert (c4.end_lineno, c4.end_col_offset) == (4, 4)
c5 = ast_nodes[4]
assert isinstance(c5, nodes.Const)
assert (c5.lineno, c5.col_offset) == (5, 0)
assert (c5.end_lineno, c5.end_col_offset) == (5, 5)
c6 = ast_nodes[5]
assert isinstance(c6, nodes.Const)
assert (c6.lineno, c6.col_offset) == (6, 0)
assert (c6.end_lineno, c6.end_col_offset) == (6, 3)
@staticmethod
def test_end_lineno_function() -> None:
"""FunctionDef, AsyncFunctionDef, Decorators, Lambda, Arguments."""
code = textwrap.dedent(
"""
def func( #@
a: int = 0, /,
var: int = 1, *args: Any,
keyword: int = 2, **kwargs: Any
) -> None:
pass
@decorator1
@decorator2
async def func(): #@
pass
lambda x: 2 #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 3
# fmt: off
f1 = ast_nodes[0]
assert isinstance(f1, nodes.FunctionDef)
assert isinstance(f1.args, nodes.Arguments)
assert isinstance(f1.returns, nodes.Const)
assert isinstance(f1.body[0], nodes.Pass)
assert (f1.lineno, f1.col_offset) == (1, 0)
assert (f1.end_lineno, f1.end_col_offset) == (6, 8)
assert (f1.returns.lineno, f1.returns.col_offset) == (5, 5)
assert (f1.returns.end_lineno, f1.returns.end_col_offset) == (5, 9)
assert (f1.body[0].lineno, f1.body[0].col_offset) == (6, 4)
assert (f1.body[0].end_lineno, f1.body[0].end_col_offset) == (6, 8)
# pos only arguments
# TODO fix column offset: arg -> arg (AssignName)
assert isinstance(f1.args.posonlyargs[0], nodes.AssignName)
assert (f1.args.posonlyargs[0].lineno, f1.args.posonlyargs[0].col_offset) == (2, 4)
assert (f1.args.posonlyargs[0].end_lineno, f1.args.posonlyargs[0].end_col_offset) == (2, 10)
assert isinstance(f1.args.posonlyargs_annotations[0], nodes.Name)
assert (
f1.args.posonlyargs_annotations[0].lineno, f1.args.posonlyargs_annotations[0].col_offset
) == (2, 7)
assert (
f1.args.posonlyargs_annotations[0].end_lineno, f1.args.posonlyargs_annotations[0].end_col_offset
) == (2, 10)
assert (f1.args.defaults[0].lineno, f1.args.defaults[0].col_offset) == (2, 13)
assert (f1.args.defaults[0].end_lineno, f1.args.defaults[0].end_col_offset) == (2, 14)
# pos or kw arguments
assert isinstance(f1.args.args[0], nodes.AssignName)
assert (f1.args.args[0].lineno, f1.args.args[0].col_offset) == (3, 4)
assert (f1.args.args[0].end_lineno, f1.args.args[0].end_col_offset) == (3, 12)
assert isinstance(f1.args.annotations[0], nodes.Name)
assert (f1.args.annotations[0].lineno, f1.args.annotations[0].col_offset) == (3, 9)
assert (f1.args.annotations[0].end_lineno, f1.args.annotations[0].end_col_offset) == (3, 12)
assert isinstance(f1.args.defaults[1], nodes.Const)
assert (f1.args.defaults[1].lineno, f1.args.defaults[1].col_offset) == (3, 15)
assert (f1.args.defaults[1].end_lineno, f1.args.defaults[1].end_col_offset) == (3, 16)
# *args
assert isinstance(f1.args.varargannotation, nodes.Name)
assert (f1.args.varargannotation.lineno, f1.args.varargannotation.col_offset) == (3, 25)
assert (f1.args.varargannotation.end_lineno, f1.args.varargannotation.end_col_offset) == (3, 28)
# kw_only arguments
assert isinstance(f1.args.kwonlyargs[0], nodes.AssignName)
assert (f1.args.kwonlyargs[0].lineno, f1.args.kwonlyargs[0].col_offset) == (4, 4)
assert (f1.args.kwonlyargs[0].end_lineno, f1.args.kwonlyargs[0].end_col_offset) == (4, 16)
annotations = f1.args.kwonlyargs_annotations
assert isinstance(annotations[0], nodes.Name)
assert (annotations[0].lineno, annotations[0].col_offset) == (4, 13)
assert (annotations[0].end_lineno, annotations[0].end_col_offset) == (4, 16)
assert isinstance(f1.args.kw_defaults[0], nodes.Const)
assert (f1.args.kw_defaults[0].lineno, f1.args.kw_defaults[0].col_offset) == (4, 19)
assert (f1.args.kw_defaults[0].end_lineno, f1.args.kw_defaults[0].end_col_offset) == (4, 20)
# **kwargs
assert isinstance(f1.args.kwargannotation, nodes.Name)
assert (f1.args.kwargannotation.lineno, f1.args.kwargannotation.col_offset) == (4, 32)
assert (f1.args.kwargannotation.end_lineno, f1.args.kwargannotation.end_col_offset) == (4, 35)
f2 = ast_nodes[1]
assert isinstance(f2, nodes.AsyncFunctionDef)
assert isinstance(f2.decorators, nodes.Decorators)
assert isinstance(f2.decorators.nodes[0], nodes.Name)
assert isinstance(f2.decorators.nodes[1], nodes.Name)
assert (f2.lineno, f2.col_offset) == (8, 0)
assert (f2.end_lineno, f2.end_col_offset) == (11, 8)
assert (f2.decorators.lineno, f2.decorators.col_offset) == (8, 0)
assert (f2.decorators.end_lineno, f2.decorators.end_col_offset) == (9, 11)
assert (f2.decorators.nodes[0].lineno, f2.decorators.nodes[0].col_offset) == (8, 1)
assert (f2.decorators.nodes[0].end_lineno, f2.decorators.nodes[0].end_col_offset) == (8, 11)
assert (f2.decorators.nodes[1].lineno, f2.decorators.nodes[1].col_offset) == (9, 1)
assert (f2.decorators.nodes[1].end_lineno, f2.decorators.nodes[1].end_col_offset) == (9, 11)
f3 = ast_nodes[2]
assert isinstance(f3, nodes.Lambda)
assert isinstance(f3.args, nodes.Arguments)
assert isinstance(f3.args.args[0], nodes.AssignName)
assert isinstance(f3.body, nodes.Const)
assert (f3.lineno, f3.col_offset) == (13, 0)
assert (f3.end_lineno, f3.end_col_offset) == (13, 11)
assert (f3.args.args[0].lineno, f3.args.args[0].col_offset) == (13, 7)
assert (f3.args.args[0].end_lineno, f3.args.args[0].end_col_offset) == (13, 8)
assert (f3.body.lineno, f3.body.col_offset) == (13, 10)
assert (f3.body.end_lineno, f3.body.end_col_offset) == (13, 11)
# fmt: on
@staticmethod
def test_end_lineno_dict() -> None:
"""Dict, DictUnpack."""
code = textwrap.dedent(
"""
{ #@
1: "Hello",
**{2: "World"} #@
}
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 2
d1 = ast_nodes[0]
assert isinstance(d1, nodes.Dict)
assert isinstance(d1.items[0][0], nodes.Const)
assert isinstance(d1.items[0][1], nodes.Const)
assert (d1.lineno, d1.col_offset) == (1, 0)
assert (d1.end_lineno, d1.end_col_offset) == (4, 1)
assert (d1.items[0][0].lineno, d1.items[0][0].col_offset) == (2, 4)
assert (d1.items[0][0].end_lineno, d1.items[0][0].end_col_offset) == (2, 5)
assert (d1.items[0][1].lineno, d1.items[0][1].col_offset) == (2, 7)
assert (d1.items[0][1].end_lineno, d1.items[0][1].end_col_offset) == (2, 14)
d2 = ast_nodes[1]
assert isinstance(d2, nodes.DictUnpack)
assert (d2.lineno, d2.col_offset) == (3, 6)
assert (d2.end_lineno, d2.end_col_offset) == (3, 18)
@staticmethod
def test_end_lineno_try() -> None:
"""Try, ExceptHandler."""
code = textwrap.dedent(
"""
try: #@
pass
except KeyError as ex:
pass
except AttributeError as ex:
pass
else:
pass
try: #@
pass
except KeyError as ex:
pass
else:
pass
finally:
pass
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 2
t1 = ast_nodes[0]
assert isinstance(t1, nodes.Try)
assert isinstance(t1.body[0], nodes.Pass)
assert isinstance(t1.orelse[0], nodes.Pass)
assert (t1.lineno, t1.col_offset) == (1, 0)
assert (t1.end_lineno, t1.end_col_offset) == (8, 8)
assert (t1.body[0].lineno, t1.body[0].col_offset) == (2, 4)
assert (t1.body[0].end_lineno, t1.body[0].end_col_offset) == (2, 8)
assert (t1.orelse[0].lineno, t1.orelse[0].col_offset) == (8, 4)
assert (t1.orelse[0].end_lineno, t1.orelse[0].end_col_offset) == (8, 8)
t2 = t1.handlers[0]
assert isinstance(t2, nodes.ExceptHandler)
assert isinstance(t2.type, nodes.Name)
assert isinstance(t2.name, nodes.AssignName)
assert isinstance(t2.body[0], nodes.Pass)
assert (t2.lineno, t2.col_offset) == (3, 0)
assert (t2.end_lineno, t2.end_col_offset) == (4, 8)
assert (t2.type.lineno, t2.type.col_offset) == (3, 7)
assert (t2.type.end_lineno, t2.type.end_col_offset) == (3, 15)
# TODO fix column offset: ExceptHandler -> name (AssignName)
assert (t2.name.lineno, t2.name.col_offset) == (3, 0)
assert (t2.name.end_lineno, t2.name.end_col_offset) == (4, 8)
assert (t2.body[0].lineno, t2.body[0].col_offset) == (4, 4)
assert (t2.body[0].end_lineno, t2.body[0].end_col_offset) == (4, 8)
t3 = ast_nodes[1]
assert isinstance(t3, nodes.Try)
assert isinstance(t3.finalbody[0], nodes.Pass)
assert (t3.lineno, t3.col_offset) == (10, 0)
assert (t3.end_lineno, t3.end_col_offset) == (17, 8)
assert (t3.body[0].lineno, t3.body[0].col_offset) == (11, 4)
assert (t3.body[0].end_lineno, t3.body[0].end_col_offset) == (11, 8)
assert (t3.finalbody[0].lineno, t3.finalbody[0].col_offset) == (17, 4)
assert (t3.finalbody[0].end_lineno, t3.finalbody[0].end_col_offset) == (17, 8)
@staticmethod
def test_end_lineno_subscript() -> None:
"""Subscript, Slice, (ExtSlice, Index)."""
code = textwrap.dedent(
"""
var[0] #@
var[1:2:1] #@
var[1:2, 2] #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 3
s1 = ast_nodes[0]
assert isinstance(s1, nodes.Subscript)
assert isinstance(s1.value, nodes.Name)
assert isinstance(s1.slice, nodes.Const)
assert (s1.lineno, s1.col_offset) == (1, 0)
assert (s1.end_lineno, s1.end_col_offset) == (1, 6)
assert (s1.value.lineno, s1.value.col_offset) == (1, 0)
assert (s1.value.end_lineno, s1.value.end_col_offset) == (1, 3)
assert (s1.slice.lineno, s1.slice.col_offset) == (1, 4)
assert (s1.slice.end_lineno, s1.slice.end_col_offset) == (1, 5)
s2 = ast_nodes[1]
assert isinstance(s2, nodes.Subscript)
assert isinstance(s2.slice, nodes.Slice)
assert isinstance(s2.slice.lower, nodes.Const)
assert isinstance(s2.slice.upper, nodes.Const)
assert isinstance(s2.slice.step, nodes.Const)
assert (s2.lineno, s2.col_offset) == (2, 0)
assert (s2.end_lineno, s2.end_col_offset) == (2, 10)
assert (s2.slice.lower.lineno, s2.slice.lower.col_offset) == (2, 4)
assert (s2.slice.lower.end_lineno, s2.slice.lower.end_col_offset) == (2, 5)
assert (s2.slice.upper.lineno, s2.slice.upper.col_offset) == (2, 6)
assert (s2.slice.upper.end_lineno, s2.slice.upper.end_col_offset) == (2, 7)
assert (s2.slice.step.lineno, s2.slice.step.col_offset) == (2, 8)
assert (s2.slice.step.end_lineno, s2.slice.step.end_col_offset) == (2, 9)
s3 = ast_nodes[2]
assert isinstance(s3, nodes.Subscript)
assert isinstance(s3.slice, nodes.Tuple)
assert (s3.lineno, s3.col_offset) == (3, 0)
assert (s3.end_lineno, s3.end_col_offset) == (3, 11)
if PY39_PLUS:
# 'lineno' and 'col_offset' information only added in Python 3.9
assert (s3.slice.lineno, s3.slice.col_offset) == (3, 4)
assert (s3.slice.end_lineno, s3.slice.end_col_offset) == (3, 10)
else:
assert (s3.slice.lineno, s3.slice.col_offset) == (None, None)
assert (s3.slice.end_lineno, s3.slice.end_col_offset) == (None, None)
@staticmethod
def test_end_lineno_import() -> None:
"""Import, ImportFrom."""
code = textwrap.dedent(
"""
import a.b #@
import a as x #@
from . import x #@
from .a import y as y #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 4
i1 = ast_nodes[0]
assert isinstance(i1, nodes.Import)
assert (i1.lineno, i1.col_offset) == (1, 0)
assert (i1.end_lineno, i1.end_col_offset) == (1, 10)
i2 = ast_nodes[1]
assert isinstance(i2, nodes.Import)
assert (i2.lineno, i2.col_offset) == (2, 0)
assert (i2.end_lineno, i2.end_col_offset) == (2, 13)
i3 = ast_nodes[2]
assert isinstance(i3, nodes.ImportFrom)
assert (i3.lineno, i3.col_offset) == (3, 0)
assert (i3.end_lineno, i3.end_col_offset) == (3, 15)
i4 = ast_nodes[3]
assert isinstance(i4, nodes.ImportFrom)
assert (i4.lineno, i4.col_offset) == (4, 0)
assert (i4.end_lineno, i4.end_col_offset) == (4, 21)
@staticmethod
def test_end_lineno_with() -> None:
"""With, AsyncWith."""
code = textwrap.dedent(
"""
with open(file) as fp, \\
open(file2) as fp2: #@
pass
async with open(file) as fp: #@
pass
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 2
w1 = ast_nodes[0].parent
assert isinstance(w1, nodes.With)
assert isinstance(w1.items[0][0], nodes.Call)
assert isinstance(w1.items[0][1], nodes.AssignName)
assert isinstance(w1.items[1][0], nodes.Call)
assert isinstance(w1.items[1][1], nodes.AssignName)
assert isinstance(w1.body[0], nodes.Pass)
assert (w1.lineno, w1.col_offset) == (1, 0)
assert (w1.end_lineno, w1.end_col_offset) == (3, 8)
assert (w1.items[0][0].lineno, w1.items[0][0].col_offset) == (1, 5)
assert (w1.items[0][0].end_lineno, w1.items[0][0].end_col_offset) == (1, 15)
assert (w1.items[0][1].lineno, w1.items[0][1].col_offset) == (1, 19)
assert (w1.items[0][1].end_lineno, w1.items[0][1].end_col_offset) == (1, 21)
assert (w1.items[1][0].lineno, w1.items[1][0].col_offset) == (2, 8)
assert (w1.items[1][0].end_lineno, w1.items[1][0].end_col_offset) == (2, 19)
assert (w1.items[1][1].lineno, w1.items[1][1].col_offset) == (2, 23)
assert (w1.items[1][1].end_lineno, w1.items[1][1].end_col_offset) == (2, 26)
assert (w1.body[0].lineno, w1.body[0].col_offset) == (3, 4)
assert (w1.body[0].end_lineno, w1.body[0].end_col_offset) == (3, 8)
w2 = ast_nodes[1]
assert isinstance(w2, nodes.AsyncWith)
assert isinstance(w2.items[0][0], nodes.Call)
assert isinstance(w2.items[0][1], nodes.AssignName)
assert isinstance(w2.body[0], nodes.Pass)
assert (w2.lineno, w2.col_offset) == (5, 0)
assert (w2.end_lineno, w2.end_col_offset) == (6, 8)
assert (w2.items[0][0].lineno, w2.items[0][0].col_offset) == (5, 11)
assert (w2.items[0][0].end_lineno, w2.items[0][0].end_col_offset) == (5, 21)
assert (w2.items[0][1].lineno, w2.items[0][1].col_offset) == (5, 25)
assert (w2.items[0][1].end_lineno, w2.items[0][1].end_col_offset) == (5, 27)
assert (w2.body[0].lineno, w2.body[0].col_offset) == (6, 4)
assert (w2.body[0].end_lineno, w2.body[0].end_col_offset) == (6, 8)
@staticmethod
def test_end_lineno_while() -> None:
"""While."""
code = textwrap.dedent(
"""
while 2:
pass
else:
pass
"""
).strip()
w1 = builder.extract_node(code)
assert isinstance(w1, nodes.While)
assert isinstance(w1.test, nodes.Const)
assert isinstance(w1.body[0], nodes.Pass)
assert isinstance(w1.orelse[0], nodes.Pass)
assert (w1.lineno, w1.col_offset) == (1, 0)
assert (w1.end_lineno, w1.end_col_offset) == (4, 8)
assert (w1.test.lineno, w1.test.col_offset) == (1, 6)
assert (w1.test.end_lineno, w1.test.end_col_offset) == (1, 7)
assert (w1.body[0].lineno, w1.body[0].col_offset) == (2, 4)
assert (w1.body[0].end_lineno, w1.body[0].end_col_offset) == (2, 8)
assert (w1.orelse[0].lineno, w1.orelse[0].col_offset) == (4, 4)
assert (w1.orelse[0].end_lineno, w1.orelse[0].end_col_offset) == (4, 8)
@staticmethod
def test_end_lineno_string() -> None:
"""FormattedValue, JoinedStr."""
code = textwrap.dedent(
"""
f"Hello World: {42.1234:02d}" #@
f"Hello: {name=}" #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 2
s1 = ast_nodes[0]
assert isinstance(s1, nodes.JoinedStr)
assert isinstance(s1.values[0], nodes.Const)
assert (s1.lineno, s1.col_offset) == (1, 0)
assert (s1.end_lineno, s1.end_col_offset) == (1, 29)
if PY312_PLUS:
assert (s1.values[0].lineno, s1.values[0].col_offset) == (1, 2)
assert (s1.values[0].end_lineno, s1.values[0].end_col_offset) == (1, 15)
else:
# Bug in Python 3.11
# https://github.com/python/cpython/issues/81639
assert (s1.values[0].lineno, s1.values[0].col_offset) == (1, 0)
assert (s1.values[0].end_lineno, s1.values[0].end_col_offset) == (1, 29)
s2 = s1.values[1]
assert isinstance(s2, nodes.FormattedValue)
if PY312_PLUS:
assert (s2.lineno, s2.col_offset) == (1, 15)
assert (s2.end_lineno, s2.end_col_offset) == (1, 28)
else:
assert (s2.lineno, s2.col_offset) == (1, 0)
assert (s2.end_lineno, s2.end_col_offset) == (1, 29)
assert isinstance(s2.value, nodes.Const) # 42.1234
if PY39_PLUS:
assert (s2.value.lineno, s2.value.col_offset) == (1, 16)
assert (s2.value.end_lineno, s2.value.end_col_offset) == (1, 23)
else:
# Bug in Python 3.8
# https://bugs.python.org/issue44885
assert (s2.value.lineno, s2.value.col_offset) == (1, 1)
assert (s2.value.end_lineno, s2.value.end_col_offset) == (1, 8)
assert isinstance(s2.format_spec, nodes.JoinedStr) # ':02d'
if PY312_PLUS:
assert (s2.format_spec.lineno, s2.format_spec.col_offset) == (1, 23)
assert (s2.format_spec.end_lineno, s2.format_spec.end_col_offset) == (1, 27)
else:
assert (s2.format_spec.lineno, s2.format_spec.col_offset) == (1, 0)
assert (s2.format_spec.end_lineno, s2.format_spec.end_col_offset) == (1, 29)
s3 = ast_nodes[1]
assert isinstance(s3, nodes.JoinedStr)
assert isinstance(s3.values[0], nodes.Const)
assert (s3.lineno, s3.col_offset) == (2, 0)
assert (s3.end_lineno, s3.end_col_offset) == (2, 17)
if PY312_PLUS:
assert (s3.values[0].lineno, s3.values[0].col_offset) == (2, 2)
assert (s3.values[0].end_lineno, s3.values[0].end_col_offset) == (2, 15)
else:
assert (s3.values[0].lineno, s3.values[0].col_offset) == (2, 0)
assert (s3.values[0].end_lineno, s3.values[0].end_col_offset) == (2, 17)
s4 = s3.values[1]
assert isinstance(s4, nodes.FormattedValue)
if PY312_PLUS:
assert (s4.lineno, s4.col_offset) == (2, 9)
assert (s4.end_lineno, s4.end_col_offset) == (2, 16)
else:
assert (s4.lineno, s4.col_offset) == (2, 0)
assert (s4.end_lineno, s4.end_col_offset) == (2, 17)
assert isinstance(s4.value, nodes.Name) # 'name'
if PY39_PLUS:
assert (s4.value.lineno, s4.value.col_offset) == (2, 10)
assert (s4.value.end_lineno, s4.value.end_col_offset) == (2, 14)
else:
# Bug in Python 3.8
# https://bugs.python.org/issue44885
assert (s4.value.lineno, s4.value.col_offset) == (2, 1)
assert (s4.value.end_lineno, s4.value.end_col_offset) == (2, 5)
@staticmethod
@pytest.mark.skipif(not PY310_PLUS, reason="pattern matching was added in PY310")
def test_end_lineno_match() -> None:
"""Match, MatchValue, MatchSingleton, MatchSequence, MatchMapping,
MatchClass, MatchStar, MatchOr, MatchAs.
"""
code = textwrap.dedent(
"""
match x: #@
case 200 if True: #@
pass
case True: #@
pass
case (1, 2, *args): #@
pass
case {1: "Hello", **rest}: #@
pass
case Point2d(0, y=0): #@
pass
case 200 | 300: #@
pass
case 200 as c: #@
pass
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 8
# fmt: off
m1 = ast_nodes[0]
assert isinstance(m1, nodes.Match)
assert (m1.lineno, m1.col_offset) == (1, 0)
assert (m1.end_lineno, m1.end_col_offset) == (15, 12)
assert (m1.subject.lineno, m1.subject.col_offset) == (1, 6)
assert (m1.subject.end_lineno, m1.subject.end_col_offset) == (1, 7)
m2 = ast_nodes[1]
assert isinstance(m2, nodes.MatchCase)
assert isinstance(m2.pattern, nodes.MatchValue)
assert isinstance(m2.guard, nodes.Const)
assert isinstance(m2.body[0], nodes.Pass)
assert (m2.pattern.lineno, m2.pattern.col_offset) == (2, 9)
assert (m2.pattern.end_lineno, m2.pattern.end_col_offset) == (2, 12)
assert (m2.guard.lineno, m2.guard.col_offset) == (2, 16)
assert (m2.guard.end_lineno, m2.guard.end_col_offset) == (2, 20)
assert (m2.body[0].lineno, m2.body[0].col_offset) == (3, 8)
assert (m2.body[0].end_lineno, m2.body[0].end_col_offset) == (3, 12)
m3 = ast_nodes[2]
assert isinstance(m3, nodes.MatchCase)
assert isinstance(m3.pattern, nodes.MatchSingleton)
assert (m3.pattern.lineno, m3.pattern.col_offset) == (4, 9)
assert (m3.pattern.end_lineno, m3.pattern.end_col_offset) == (4, 13)
m4 = ast_nodes[3]
assert isinstance(m4, nodes.MatchCase)
assert isinstance(m4.pattern, nodes.MatchSequence)
assert isinstance(m4.pattern.patterns[0], nodes.MatchValue)
assert (m4.pattern.lineno, m4.pattern.col_offset) == (6, 9)
assert (m4.pattern.end_lineno, m4.pattern.end_col_offset) == (6, 22)
assert (m4.pattern.patterns[0].lineno, m4.pattern.patterns[0].col_offset) == (6, 10)
assert (m4.pattern.patterns[0].end_lineno, m4.pattern.patterns[0].end_col_offset) == (6, 11)
m5 = m4.pattern.patterns[2]
assert isinstance(m5, nodes.MatchStar)
assert isinstance(m5.name, nodes.AssignName)
assert (m5.lineno, m5.col_offset) == (6, 16)
assert (m5.end_lineno, m5.end_col_offset) == (6, 21)
# TODO fix column offset: MatchStar -> name (AssignName)
assert (m5.name.lineno, m5.name.col_offset) == (6, 16)
assert (m5.name.end_lineno, m5.name.end_col_offset) == (6, 21)
m6 = ast_nodes[4]
assert isinstance(m6, nodes.MatchCase)
assert isinstance(m6.pattern, nodes.MatchMapping)
assert isinstance(m6.pattern.keys[0], nodes.Const)
assert isinstance(m6.pattern.patterns[0], nodes.MatchValue)
assert isinstance(m6.pattern.rest, nodes.AssignName)
assert (m6.pattern.lineno, m6.pattern.col_offset) == (8, 9)
assert (m6.pattern.end_lineno, m6.pattern.end_col_offset) == (8, 29)
assert (m6.pattern.keys[0].lineno, m6.pattern.keys[0].col_offset) == (8, 10)
assert (m6.pattern.keys[0].end_lineno, m6.pattern.keys[0].end_col_offset) == (8, 11)
assert (m6.pattern.patterns[0].lineno, m6.pattern.patterns[0].col_offset) == (8, 13)
assert (m6.pattern.patterns[0].end_lineno, m6.pattern.patterns[0].end_col_offset) == (8, 20)
# TODO fix column offset: MatchMapping -> rest (AssignName)
assert (m6.pattern.rest.lineno, m6.pattern.rest.col_offset) == (8, 9)
assert (m6.pattern.rest.end_lineno, m6.pattern.rest.end_col_offset) == (8, 29)
m7 = ast_nodes[5]
assert isinstance(m7, nodes.MatchCase)
assert isinstance(m7.pattern, nodes.MatchClass)
assert isinstance(m7.pattern.cls, nodes.Name)
assert isinstance(m7.pattern.patterns[0], nodes.MatchValue)
assert isinstance(m7.pattern.kwd_patterns[0], nodes.MatchValue)
assert (m7.pattern.lineno, m7.pattern.col_offset) == (10, 9)
assert (m7.pattern.end_lineno, m7.pattern.end_col_offset) == (10, 24)
assert (m7.pattern.cls.lineno, m7.pattern.cls.col_offset) == (10, 9)
assert (m7.pattern.cls.end_lineno, m7.pattern.cls.end_col_offset) == (10, 16)
assert (m7.pattern.patterns[0].lineno, m7.pattern.patterns[0].col_offset) == (10, 17)
assert (m7.pattern.patterns[0].end_lineno, m7.pattern.patterns[0].end_col_offset) == (10, 18)
assert (m7.pattern.kwd_patterns[0].lineno, m7.pattern.kwd_patterns[0].col_offset) == (10, 22)
assert (m7.pattern.kwd_patterns[0].end_lineno, m7.pattern.kwd_patterns[0].end_col_offset) == (10, 23)
m8 = ast_nodes[6]
assert isinstance(m8, nodes.MatchCase)
assert isinstance(m8.pattern, nodes.MatchOr)
assert isinstance(m8.pattern.patterns[0], nodes.MatchValue)
assert (m8.pattern.lineno, m8.pattern.col_offset) == (12, 9)
assert (m8.pattern.end_lineno, m8.pattern.end_col_offset) == (12, 18)
assert (m8.pattern.patterns[0].lineno, m8.pattern.patterns[0].col_offset) == (12, 9)
assert (m8.pattern.patterns[0].end_lineno, m8.pattern.patterns[0].end_col_offset) == (12, 12)
m9 = ast_nodes[7]
assert isinstance(m9, nodes.MatchCase)
assert isinstance(m9.pattern, nodes.MatchAs)
assert isinstance(m9.pattern.pattern, nodes.MatchValue)
assert isinstance(m9.pattern.name, nodes.AssignName)
assert (m9.pattern.lineno, m9.pattern.col_offset) == (14, 9)
assert (m9.pattern.end_lineno, m9.pattern.end_col_offset) == (14, 17)
assert (m9.pattern.pattern.lineno, m9.pattern.pattern.col_offset) == (14, 9)
assert (m9.pattern.pattern.end_lineno, m9.pattern.pattern.end_col_offset) == (14, 12)
# TODO fix column offset: MatchAs -> name (AssignName)
assert (m9.pattern.name.lineno, m9.pattern.name.col_offset) == (14, 9)
assert (m9.pattern.name.end_lineno, m9.pattern.name.end_col_offset) == (14, 17)
# fmt: on
@staticmethod
def test_end_lineno_comprehension() -> None:
"""ListComp, SetComp, DictComp, GeneratorExpr."""
code = textwrap.dedent(
"""
[x for x in var] #@
{x for x in var} #@
{x: y for x, y in var} #@
(x for x in var) #@
"""
).strip()
ast_nodes = builder.extract_node(code)
assert isinstance(ast_nodes, list) and len(ast_nodes) == 4
c1 = ast_nodes[0]
assert isinstance(c1, nodes.ListComp)
assert isinstance(c1.elt, nodes.Name)
assert isinstance(c1.generators[0], nodes.Comprehension) # type: ignore[index]
assert (c1.lineno, c1.col_offset) == (1, 0)
assert (c1.end_lineno, c1.end_col_offset) == (1, 16)
assert (c1.elt.lineno, c1.elt.col_offset) == (1, 1)
assert (c1.elt.end_lineno, c1.elt.end_col_offset) == (1, 2)
c2 = ast_nodes[1]
assert isinstance(c2, nodes.SetComp)
assert isinstance(c2.elt, nodes.Name)
assert isinstance(c2.generators[0], nodes.Comprehension) # type: ignore[index]
assert (c2.lineno, c2.col_offset) == (2, 0)
assert (c2.end_lineno, c2.end_col_offset) == (2, 16)
assert (c2.elt.lineno, c2.elt.col_offset) == (2, 1)
assert (c2.elt.end_lineno, c2.elt.end_col_offset) == (2, 2)
c3 = ast_nodes[2]
assert isinstance(c3, nodes.DictComp)
assert isinstance(c3.key, nodes.Name)
assert isinstance(c3.value, nodes.Name)
assert isinstance(c3.generators[0], nodes.Comprehension) # type: ignore[index]
assert (c3.lineno, c3.col_offset) == (3, 0)
assert (c3.end_lineno, c3.end_col_offset) == (3, 22)
assert (c3.key.lineno, c3.key.col_offset) == (3, 1)
assert (c3.key.end_lineno, c3.key.end_col_offset) == (3, 2)
assert (c3.value.lineno, c3.value.col_offset) == (3, 4)
assert (c3.value.end_lineno, c3.value.end_col_offset) == (3, 5)
c4 = ast_nodes[3]
assert isinstance(c4, nodes.GeneratorExp)
assert isinstance(c4.elt, nodes.Name)
assert isinstance(c4.generators[0], nodes.Comprehension) # type: ignore[index]
assert (c4.lineno, c4.col_offset) == (4, 0)
assert (c4.end_lineno, c4.end_col_offset) == (4, 16)
assert (c4.elt.lineno, c4.elt.col_offset) == (4, 1)
assert (c4.elt.end_lineno, c4.elt.end_col_offset) == (4, 2)
@staticmethod
def test_end_lineno_class() -> None:
"""ClassDef, Keyword."""
code = textwrap.dedent(
"""
@decorator1
@decorator2
class X(Parent, var=42):
pass
"""
).strip()
c1 = builder.extract_node(code)
assert isinstance(c1, nodes.ClassDef)
assert isinstance(c1.decorators, nodes.Decorators)
assert isinstance(c1.bases[0], nodes.Name)
assert isinstance(c1.keywords[0], nodes.Keyword)
assert isinstance(c1.body[0], nodes.Pass)
# fmt: off
assert (c1.lineno, c1.col_offset) == (3, 0)
assert (c1.end_lineno, c1.end_col_offset) == (4, 8)
assert (c1.decorators.lineno, c1.decorators.col_offset) == (1, 0)
assert (c1.decorators.end_lineno, c1.decorators.end_col_offset) == (2, 11)
assert (c1.bases[0].lineno, c1.bases[0].col_offset) == (3, 8)
assert (c1.bases[0].end_lineno, c1.bases[0].end_col_offset) == (3, 14)
if PY39_PLUS:
# 'lineno' and 'col_offset' information only added in Python 3.9
assert (c1.keywords[0].lineno, c1.keywords[0].col_offset) == (3, 16)
assert (c1.keywords[0].end_lineno, c1.keywords[0].end_col_offset) == (3, 22)
else:
assert (c1.keywords[0].lineno, c1.keywords[0].col_offset) == (None, None)
assert (c1.keywords[0].end_lineno, c1.keywords[0].end_col_offset) == (None, None)
assert (c1.body[0].lineno, c1.body[0].col_offset) == (4, 4)
assert (c1.body[0].end_lineno, c1.body[0].end_col_offset) == (4, 8)
# fmt: on
@staticmethod
def test_end_lineno_module() -> None:
"""Tests for Module."""
code = """print()"""
module = astroid.parse(code)
assert isinstance(module, nodes.Module)
assert module.lineno == 0
assert module.col_offset == 0
assert module.end_lineno is None
assert module.end_col_offset is None