Skip to content

Commit

Permalink
gh-93283: Improve error message for f-string with invalid conversion …
Browse files Browse the repository at this point in the history
…character (GH-93349)
  • Loading branch information
serhiy-storchaka authored May 31, 2022
1 parent bb90071 commit 07df8d5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 23 deletions.
33 changes: 22 additions & 11 deletions Lib/test/test_fstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,9 @@ def test_format_specifier_expressions(self):
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.
Expand Down Expand Up @@ -1012,19 +1014,28 @@ def test_conversions(self):
# 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!!}'",
self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
["f'{3!'",
"f'{3!s'",
"f'{3!g'",
])

self.assertAllRaise(SyntaxError, 'f-string: missed conversion character',
["f'{3!}'",
"f'{3!:'",
"f'{3!:}'",
"f'{3! s}'", # no space before conversion char
])

self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
["f'{x!s{y}}'",
"f'{3!ss}'",
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}'",
])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve error message for invalid syntax of conversion character in f-string
expressions.
40 changes: 28 additions & 12 deletions Parser/string_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 07df8d5

Please sign in to comment.