diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 6f86eb954ec55a..e23641afea4010 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -645,6 +645,21 @@ def test_issue39579_dotted_name_end_col_offset(self): attr_b = tree.body[0].decorator_list[0].value self.assertEqual(attr_b.end_col_offset, 4) + def test_issue35212_fstring_col_offset(self): + tree = ast.parse(dedent(''' + ( + f"""Some multi-line text. + {a:b} + It goes on...""" + ) + ''').strip()) + fstr = tree.body[0].value + value = fstr.values[1].value + self.assertEqual(value.col_offset, 9) + self.assertEqual(value.lineno, 3) + self.assertEqual(value.end_col_offset, 10) + self.assertEqual(value.end_lineno, 3) + def test_ast_asdl_signature(self): self.assertEqual(ast.withitem.__doc__, "withitem(expr context_expr, expr? optional_vars)") self.assertEqual(ast.GtE.__doc__, "GtE") diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 49663923e7f5aa..20d345fc4b9a79 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -324,8 +324,8 @@ def test_ast_line_numbers_multiline_fstring(self): self.assertEqual(binop.lineno, 4) self.assertEqual(binop.left.lineno, 4) self.assertEqual(binop.right.lineno, 6) - self.assertEqual(binop.col_offset, 4) - self.assertEqual(binop.left.col_offset, 4) + self.assertEqual(binop.col_offset, 3) + self.assertEqual(binop.left.col_offset, 3) self.assertEqual(binop.right.col_offset, 7) def test_docstring(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-04-07-06-00-20.bpo-35212.86avLy.rst b/Misc/NEWS.d/next/Core and Builtins/2020-04-07-06-00-20.bpo-35212.86avLy.rst new file mode 100644 index 00000000000000..4ae8a38ace5e4f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-04-07-06-00-20.bpo-35212.86avLy.rst @@ -0,0 +1 @@ +fix col_offset in f-strings \ No newline at end of file diff --git a/Python/ast.c b/Python/ast.c index 550ee03b1ac384..4467485fefd72a 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -4695,8 +4695,12 @@ fstring_fix_node_location(const node *parent, node *n, char *expr_str) if (substr) { start = substr; while (start > parent->n_str) { - if (start[0] == '\n') + if (start[0] == '\n') { + /* when the f-string expression is on a new line, clear + old offset and also don't count the `\n` */ + cols = -1; break; + } start--; } cols += (int)(substr - start); @@ -4770,7 +4774,9 @@ fstring_compile_expr(const char *expr_start, const char *expr_end, } /* Reuse str to find the correct column offset. */ str[0] = '{'; - str[len+1] = '}'; + /* bpo-35212: if the fstring has a format_spec, it doesn't match a close + brace. */ + str[len+1] = 0; fstring_fix_node_location(n, mod_n, str); mod = PyAST_FromNode(mod_n, &cf, "", c->c_arena); PyMem_RawFree(str);