summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsunmy2019 <59365878+sunmy2019@users.noreply.github.com>2023-10-05 14:08:42 (GMT)
committerGitHub <noreply@github.com>2023-10-05 14:08:42 (GMT)
commit2cb62c6437fa07e08b4778f7ab9baa5f16ac01f2 (patch)
treee2745301a1a835dbf56197fd929db545f43dc208
parentcc389ef627b2a486ab89d9a11245bef48224efb1 (diff)
downloadcpython-2cb62c6437fa07e08b4778f7ab9baa5f16ac01f2.zip
cpython-2cb62c6437fa07e08b4778f7ab9baa5f16ac01f2.tar.gz
cpython-2cb62c6437fa07e08b4778f7ab9baa5f16ac01f2.tar.bz2
gh-110309: Prune empty constant in format specs (#110320)
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
-rw-r--r--Lib/test/test_fstring.py48
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-10-03-23-26-18.gh-issue-110309.Y8nDOF.rst1
-rw-r--r--Parser/action_helpers.c40
3 files changed, 79 insertions, 10 deletions
diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py
index 4f05a14..dd8c2dd 100644
--- a/Lib/test/test_fstring.py
+++ b/Lib/test/test_fstring.py
@@ -514,6 +514,54 @@ x = (
self.assertEqual(type(format_spec), ast.JoinedStr)
self.assertEqual(len(format_spec.values), 0)
+ def test_ast_fstring_format_spec(self):
+ expr = "f'{1:{name}}'"
+
+ mod = ast.parse(expr)
+ self.assertEqual(type(mod), ast.Module)
+ self.assertEqual(len(mod.body), 1)
+
+ fstring = mod.body[0].value
+ self.assertEqual(type(fstring), ast.JoinedStr)
+ self.assertEqual(len(fstring.values), 1)
+
+ fv = fstring.values[0]
+ self.assertEqual(type(fv), ast.FormattedValue)
+
+ format_spec = fv.format_spec
+ self.assertEqual(type(format_spec), ast.JoinedStr)
+ self.assertEqual(len(format_spec.values), 1)
+
+ format_spec_value = format_spec.values[0]
+ self.assertEqual(type(format_spec_value), ast.FormattedValue)
+ self.assertEqual(format_spec_value.value.id, 'name')
+
+ expr = "f'{1:{name1}{name2}}'"
+
+ mod = ast.parse(expr)
+ self.assertEqual(type(mod), ast.Module)
+ self.assertEqual(len(mod.body), 1)
+
+ fstring = mod.body[0].value
+ self.assertEqual(type(fstring), ast.JoinedStr)
+ self.assertEqual(len(fstring.values), 1)
+
+ fv = fstring.values[0]
+ self.assertEqual(type(fv), ast.FormattedValue)
+
+ format_spec = fv.format_spec
+ self.assertEqual(type(format_spec), ast.JoinedStr)
+ self.assertEqual(len(format_spec.values), 2)
+
+ format_spec_value = format_spec.values[0]
+ self.assertEqual(type(format_spec_value), ast.FormattedValue)
+ self.assertEqual(format_spec_value.value.id, 'name1')
+
+ format_spec_value = format_spec.values[1]
+ self.assertEqual(type(format_spec_value), ast.FormattedValue)
+ self.assertEqual(format_spec_value.value.id, 'name2')
+
+
def test_docstring(self):
def f():
f'''Not a docstring'''
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-03-23-26-18.gh-issue-110309.Y8nDOF.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-03-23-26-18.gh-issue-110309.Y8nDOF.rst
new file mode 100644
index 0000000..8304287
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-03-23-26-18.gh-issue-110309.Y8nDOF.rst
@@ -0,0 +1 @@
+Remove unnecessary empty constant nodes in the ast of f-string specs.
diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c
index 36e0750..b8713a3 100644
--- a/Parser/action_helpers.c
+++ b/Parser/action_helpers.c
@@ -998,18 +998,38 @@ _PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec, in
return NULL;
}
- // This is needed to keep compatibility with 3.11, where an empty format spec is parsed
- // as an *empty* JoinedStr node, instead of having an empty constant in it.
- if (asdl_seq_LEN(spec) == 1) {
- expr_ty e = asdl_seq_GET(spec, 0);
- if (e->kind == Constant_kind
- && PyUnicode_Check(e->v.Constant.value)
- && PyUnicode_GetLength(e->v.Constant.value) == 0) {
- spec = _Py_asdl_expr_seq_new(0, arena);
+ // This is needed to keep compatibility with 3.11, where an empty format
+ // spec is parsed as an *empty* JoinedStr node, instead of having an empty
+ // constant in it.
+ Py_ssize_t n_items = asdl_seq_LEN(spec);
+ Py_ssize_t non_empty_count = 0;
+ for (Py_ssize_t i = 0; i < n_items; i++) {
+ expr_ty item = asdl_seq_GET(spec, i);
+ non_empty_count += !(item->kind == Constant_kind &&
+ PyUnicode_CheckExact(item->v.Constant.value) &&
+ PyUnicode_GET_LENGTH(item->v.Constant.value) == 0);
+ }
+ if (non_empty_count != n_items) {
+ asdl_expr_seq *resized_spec =
+ _Py_asdl_expr_seq_new(non_empty_count, p->arena);
+ if (resized_spec == NULL) {
+ return NULL;
+ }
+ Py_ssize_t j = 0;
+ for (Py_ssize_t i = 0; i < n_items; i++) {
+ expr_ty item = asdl_seq_GET(spec, i);
+ if (item->kind == Constant_kind &&
+ PyUnicode_CheckExact(item->v.Constant.value) &&
+ PyUnicode_GET_LENGTH(item->v.Constant.value) == 0) {
+ continue;
+ }
+ asdl_seq_SET(resized_spec, j++, item);
}
+ assert(j == non_empty_count);
+ spec = resized_spec;
}
-
- expr_ty res = _PyAST_JoinedStr(spec, lineno, col_offset, end_lineno, end_col_offset, p->arena);
+ expr_ty res = _PyAST_JoinedStr(spec, lineno, col_offset, end_lineno,
+ end_col_offset, p->arena);
if (!res) {
return NULL;
}