Skip to content

Commit 8f4380d

Browse files
authored
bpo-40726: handle uninitalized end_lineno on ast.increment_lineno (GH-20312)
1 parent 270b4ad commit 8f4380d

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

Lib/ast.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,11 @@ def copy_location(new_node, old_node):
180180
for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
181181
if attr in old_node._attributes and attr in new_node._attributes:
182182
value = getattr(old_node, attr, None)
183-
if value is not None:
183+
# end_lineno and end_col_offset are optional attributes, and they
184+
# should be copied whether the value is None or not.
185+
if value is not None or (
186+
hasattr(old_node, attr) and attr.startswith("end_")
187+
):
184188
setattr(new_node, attr, value)
185189
return new_node
186190

@@ -229,8 +233,11 @@ def increment_lineno(node, n=1):
229233
for child in walk(node):
230234
if 'lineno' in child._attributes:
231235
child.lineno = getattr(child, 'lineno', 0) + n
232-
if 'end_lineno' in child._attributes:
233-
child.end_lineno = getattr(child, 'end_lineno', 0) + n
236+
if (
237+
"end_lineno" in child._attributes
238+
and (end_lineno := getattr(child, "end_lineno", 0)) is not None
239+
):
240+
child.end_lineno = end_lineno + n
234241
return node
235242

236243

Lib/test/test_ast.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,12 @@ def test_copy_location(self):
812812
'lineno=1, col_offset=4, end_lineno=1, end_col_offset=5), lineno=1, '
813813
'col_offset=0, end_lineno=1, end_col_offset=5))'
814814
)
815+
src = ast.Call(col_offset=1, lineno=1, end_lineno=1, end_col_offset=1)
816+
new = ast.copy_location(src, ast.Call(col_offset=None, lineno=None))
817+
self.assertIsNone(new.end_lineno)
818+
self.assertIsNone(new.end_col_offset)
819+
self.assertEqual(new.lineno, 1)
820+
self.assertEqual(new.col_offset, 1)
815821

816822
def test_fix_missing_locations(self):
817823
src = ast.parse('write("spam")')
@@ -851,6 +857,11 @@ def test_increment_lineno(self):
851857
'lineno=4, col_offset=4, end_lineno=4, end_col_offset=5), lineno=4, '
852858
'col_offset=0, end_lineno=4, end_col_offset=5))'
853859
)
860+
src = ast.Call(
861+
func=ast.Name("test", ast.Load()), args=[], keywords=[], lineno=1
862+
)
863+
self.assertEqual(ast.increment_lineno(src).lineno, 2)
864+
self.assertIsNone(ast.increment_lineno(src).end_lineno)
854865

855866
def test_iter_fields(self):
856867
node = ast.parse('foo()', mode='eval')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Handle cases where the ``end_lineno`` is ``None`` on
2+
:func:`ast.increment_lineno`.

0 commit comments

Comments
 (0)