summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2022-05-31 17:38:29 (GMT)
committerGitHub <noreply@github.com>2022-05-31 17:38:29 (GMT)
commit07df8d5b2c715c9aa770c47986d59e272bf049f0 (patch)
tree53f040830007d2dff4aa8fcb8c1b85f2a79dc57c
parentbb900712a5511ba82ef64105fe28d2a6886a8fed (diff)
downloadcpython-07df8d5b2c715c9aa770c47986d59e272bf049f0.zip
cpython-07df8d5b2c715c9aa770c47986d59e272bf049f0.tar.gz
cpython-07df8d5b2c715c9aa770c47986d59e272bf049f0.tar.bz2
gh-93283: Improve error message for f-string with invalid conversion character (GH-93349)
-rw-r--r--Lib/test/test_fstring.py33
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst2
-rw-r--r--Parser/string_parser.c40
3 files changed, 52 insertions, 23 deletions
diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py
index 0c3372f..e8bf420 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!!}'",
+ 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}'",
])
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 0000000..550e869
--- /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 9c12d8c..c56ed20 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) {