]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93283: Improve error message for f-string with invalid conversion character (GH...
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 31 May 2022 17:38:29 +0000 (20:38 +0300)
committerGitHub <noreply@github.com>
Tue, 31 May 2022 17:38:29 +0000 (20:38 +0300)
Lib/test/test_fstring.py
Misc/NEWS.d/next/Core and Builtins/2022-05-30-14-50-03.gh-issue-93283.XDO2ZQ.rst [new file with mode: 0644]
Parser/string_parser.c

index 0c3372f0335517f90ade734b4dd73ffd5f9d4e85..e8bf420d699ed0b67cdd75e775ad070654e6a113 100644 (file)
@@ -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 (file)
index 0000000..550e869
--- /dev/null
@@ -0,0 +1,2 @@
+Improve error message for invalid syntax of conversion character in f-string
+expressions.
index 9c12d8ca101d00f63d3684aa5ad1997a0ac9e249..c56ed20ad4cfc23640644cbe76986cd57ae9aa3c 100644 (file)
@@ -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) {