From 07df8d5b2c715c9aa770c47986d59e272bf049f0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 31 May 2022 20:38:29 +0300 Subject: [PATCH] gh-93283: Improve error message for f-string with invalid conversion character (GH-93349) --- Lib/test/test_fstring.py | 35 ++++++++++------ ...2-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst | 2 + Parser/string_parser.c | 40 +++++++++++++------ 3 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 0c3372f0335..e8bf420d699 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -590,7 +590,9 @@ x = ( self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa') self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa') - self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + self.assertAllRaise(SyntaxError, + """f-string: invalid conversion character 'r{"': """ + """expected 's', 'r', or 'a'""", ["""f'{"s"!r{":10"}}'""", # This looks like a nested format spec. @@ -1012,19 +1014,28 @@ x = ( # Not a conversion, but show that ! is allowed in a format spec. self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!') - self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', - ["f'{3!g}'", - "f'{3!A}'", - "f'{3!3}'", - "f'{3!G}'", - "f'{3!!}'", - "f'{3!:}'", - "f'{3! s}'", # no space before conversion char + self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + ["f'{3!'", + "f'{3!s'", + "f'{3!g'", ]) - self.assertAllRaise(SyntaxError, "f-string: expecting '}'", - ["f'{x!s{y}}'", - "f'{3!ss}'", + self.assertAllRaise(SyntaxError, 'f-string: missed conversion character', + ["f'{3!}'", + "f'{3!:'", + "f'{3!:}'", + ]) + + for conv in 'g', 'A', '3', 'G', '!', ' s', 's ', ' s ', 'ä', 'ɐ', 'ª': + self.assertAllRaise(SyntaxError, + "f-string: invalid conversion character %r: " + "expected 's', 'r', or 'a'" % conv, + ["f'{3!" + conv + "}'"]) + + self.assertAllRaise(SyntaxError, + "f-string: invalid conversion character 'ss': " + "expected 's', 'r', or 'a'", + ["f'{3!ss}'", "f'{3!ss:}'", "f'{3!ss:s}'", ]) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst b/Misc/NEWS.d/next/Core and Builtins/2022-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst new file mode 100644 index 00000000000..550e86988b2 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst @@ -0,0 +1,2 @@ +Improve error message for invalid syntax of conversion character in f-string +expressions. diff --git a/Parser/string_parser.c b/Parser/string_parser.c index 9c12d8ca101..c56ed20ad4c 100644 --- a/Parser/string_parser.c +++ b/Parser/string_parser.c @@ -767,27 +767,43 @@ fstring_find_expr(Parser *p, const char **str, const char *end, int raw, int rec /* Check for a conversion char, if present. */ if (**str == '!') { *str += 1; - if (*str >= end) { - goto unexpected_end_of_string; + const char *conv_start = *str; + while (1) { + if (*str >= end) { + goto unexpected_end_of_string; + } + if (**str == '}' || **str == ':') { + break; + } + *str += 1; + } + if (*str == conv_start) { + RAISE_SYNTAX_ERROR( + "f-string: missed conversion character"); + goto error; } - conversion = (unsigned char)**str; - *str += 1; - + conversion = (unsigned char)*conv_start; /* Validate the conversion. */ - if (!(conversion == 's' || conversion == 'r' || conversion == 'a')) { - RAISE_SYNTAX_ERROR( - "f-string: invalid conversion character: " - "expected 's', 'r', or 'a'"); + if ((*str != conv_start + 1) || + !(conversion == 's' || conversion == 'r' || conversion == 'a')) + { + PyObject *conv_obj = PyUnicode_FromStringAndSize(conv_start, + *str-conv_start); + if (conv_obj) { + RAISE_SYNTAX_ERROR( + "f-string: invalid conversion character %R: " + "expected 's', 'r', or 'a'", + conv_obj); + Py_DECREF(conv_obj); + } goto error; } } /* Check for the format spec, if present. */ - if (*str >= end) { - goto unexpected_end_of_string; - } + assert(*str < end); if (**str == ':') { *str += 1; if (*str >= end) {