From: Batuhan Taskaya Date: Wed, 5 Aug 2020 18:37:19 +0000 (+0300) Subject: [3.8] bpo-40726: handle uninitalized end_lineno on ast.increment_lineno (GH-21745) X-Git-Tag: v3.8.6rc1~57 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b24c9d2b0656764bef48120d9511faf833bd7ead;p=thirdparty%2FPython%2Fcpython.git [3.8] bpo-40726: handle uninitalized end_lineno on ast.increment_lineno (GH-21745) …no (GH-20312). (cherry picked from commit 8f4380d2f5839a321475104765221a7394a9d649) Co-authored-by: Batuhan Taskaya Automerge-Triggered-By: @pablogsal --- diff --git a/Lib/ast.py b/Lib/ast.py index 99a1148a3ba2..d29db80a085f 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -144,9 +144,14 @@ def copy_location(new_node, old_node): attributes) from *old_node* to *new_node* if possible, and return *new_node*. """ for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset': - if attr in old_node._attributes and attr in new_node._attributes \ - and hasattr(old_node, attr): - setattr(new_node, attr, getattr(old_node, attr)) + if attr in old_node._attributes and attr in new_node._attributes: + value = getattr(old_node, attr, None) + # end_lineno and end_col_offset are optional attributes, and they + # should be copied whether the value is None or not. + if value is not None or ( + hasattr(old_node, attr) and attr.startswith("end_") + ): + setattr(new_node, attr, value) return new_node @@ -194,8 +199,11 @@ def increment_lineno(node, n=1): for child in walk(node): if 'lineno' in child._attributes: child.lineno = getattr(child, 'lineno', 0) + n - if 'end_lineno' in child._attributes: - child.end_lineno = getattr(child, 'end_lineno', 0) + n + if ( + "end_lineno" in child._attributes + and (end_lineno := getattr(child, "end_lineno", 0)) is not None + ): + child.end_lineno = end_lineno + n return node diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index b921f4a5d682..c625e693f50d 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -718,6 +718,15 @@ class ASTHelpers_Test(unittest.TestCase): 'lineno=1, col_offset=4, end_lineno=1, end_col_offset=5), lineno=1, ' 'col_offset=0, end_lineno=1, end_col_offset=5))' ) + src = ast.Call(col_offset=1, lineno=1, end_lineno=1, end_col_offset=1) + new = ast.copy_location(src, ast.Call( + col_offset=None, lineno=None, + end_lineno=None, end_col_offset=None + )) + self.assertIsNone(new.end_lineno) + self.assertIsNone(new.end_col_offset) + self.assertEqual(new.lineno, 1) + self.assertEqual(new.col_offset, 1) def test_fix_missing_locations(self): src = ast.parse('write("spam")') @@ -757,6 +766,12 @@ class ASTHelpers_Test(unittest.TestCase): 'lineno=4, col_offset=4, end_lineno=4, end_col_offset=5), lineno=4, ' 'col_offset=0, end_lineno=4, end_col_offset=5))' ) + src = ast.Call( + func=ast.Name("test", ast.Load()), args=[], keywords=[], + lineno=1, end_lineno=None + ) + self.assertEqual(ast.increment_lineno(src).lineno, 2) + self.assertIsNone(ast.increment_lineno(src).end_lineno) def test_iter_fields(self): node = ast.parse('foo()', mode='eval') diff --git a/Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst b/Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst new file mode 100644 index 000000000000..7409eb3d80df --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-22-12-45-58.bpo-40726.7oBdMw.rst @@ -0,0 +1,2 @@ +Handle cases where the ``end_lineno`` is ``None`` on +:func:`ast.increment_lineno`.