mirror of
https://github.com/python/cpython.git
synced 2025-10-03 05:35:59 +00:00
bpo-20524: adds better error message for .format()
(GH-28310)
It now lists the bad format_spec and the type of the object.
This commit is contained in:
parent
3f8b23f8dd
commit
8d8729146f
3 changed files with 50 additions and 7 deletions
|
@ -519,5 +519,33 @@ class FormatTest(unittest.TestCase):
|
|||
with self.assertRaisesRegex(ValueError, error_msg):
|
||||
'{:_,}'.format(1)
|
||||
|
||||
def test_better_error_message_format(self):
|
||||
# https://bugs.python.org/issue20524
|
||||
for value in [12j, 12, 12.0, "12"]:
|
||||
with self.subTest(value=value):
|
||||
# The format spec must be invalid for all types we're testing.
|
||||
# '%M' will suffice.
|
||||
bad_format_spec = '%M'
|
||||
err = re.escape("Invalid format specifier "
|
||||
f"'{bad_format_spec}' for object of type "
|
||||
f"'{type(value).__name__}'")
|
||||
with self.assertRaisesRegex(ValueError, err):
|
||||
f"xx{{value:{bad_format_spec}}}yy".format(value=value)
|
||||
|
||||
# Also test the builtin format() function.
|
||||
with self.assertRaisesRegex(ValueError, err):
|
||||
format(value, bad_format_spec)
|
||||
|
||||
# Also test f-strings.
|
||||
with self.assertRaisesRegex(ValueError, err):
|
||||
eval("f'xx{value:{bad_format_spec}}yy'")
|
||||
|
||||
def test_unicode_in_error_message(self):
|
||||
str_err = re.escape(
|
||||
"Invalid format specifier '%ЫйЯЧ' for object of type 'str'")
|
||||
with self.assertRaisesRegex(ValueError, str_err):
|
||||
"{a:%ЫйЯЧ}".format(a='a')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Improves error messages on ``.format()`` operation for ``str``, ``float``,
|
||||
``int``, and ``complex``. New format now shows the problematic pattern and
|
||||
the object type.
|
|
@ -162,7 +162,8 @@ DEBUG_PRINT_FORMAT_SPEC(InternalFormatSpec *format)
|
|||
if failure, sets the exception
|
||||
*/
|
||||
static int
|
||||
parse_internal_render_format_spec(PyObject *format_spec,
|
||||
parse_internal_render_format_spec(PyObject *obj,
|
||||
PyObject *format_spec,
|
||||
Py_ssize_t start, Py_ssize_t end,
|
||||
InternalFormatSpec *format,
|
||||
char default_type,
|
||||
|
@ -279,8 +280,19 @@ parse_internal_render_format_spec(PyObject *format_spec,
|
|||
/* Finally, parse the type field. */
|
||||
|
||||
if (end-pos > 1) {
|
||||
/* More than one char remain, invalid format specifier. */
|
||||
PyErr_Format(PyExc_ValueError, "Invalid format specifier");
|
||||
/* More than one char remains, so this is an invalid format
|
||||
specifier. */
|
||||
/* Create a temporary object that contains the format spec we're
|
||||
operating on. It's format_spec[start:end] (in Python syntax). */
|
||||
PyObject* actual_format_spec = PyUnicode_FromKindAndData(kind,
|
||||
(char*)data + kind*start,
|
||||
end-start);
|
||||
if (actual_format_spec != NULL) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Invalid format specifier '%U' for object of type '%.200s'",
|
||||
actual_format_spec, Py_TYPE(obj)->tp_name);
|
||||
Py_DECREF(actual_format_spec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1444,7 +1456,7 @@ _PyUnicode_FormatAdvancedWriter(_PyUnicodeWriter *writer,
|
|||
}
|
||||
|
||||
/* parse the format_spec */
|
||||
if (!parse_internal_render_format_spec(format_spec, start, end,
|
||||
if (!parse_internal_render_format_spec(obj, format_spec, start, end,
|
||||
&format, 's', '<'))
|
||||
return -1;
|
||||
|
||||
|
@ -1480,7 +1492,7 @@ _PyLong_FormatAdvancedWriter(_PyUnicodeWriter *writer,
|
|||
}
|
||||
|
||||
/* parse the format_spec */
|
||||
if (!parse_internal_render_format_spec(format_spec, start, end,
|
||||
if (!parse_internal_render_format_spec(obj, format_spec, start, end,
|
||||
&format, 'd', '>'))
|
||||
goto done;
|
||||
|
||||
|
@ -1536,7 +1548,7 @@ _PyFloat_FormatAdvancedWriter(_PyUnicodeWriter *writer,
|
|||
return format_obj(obj, writer);
|
||||
|
||||
/* parse the format_spec */
|
||||
if (!parse_internal_render_format_spec(format_spec, start, end,
|
||||
if (!parse_internal_render_format_spec(obj, format_spec, start, end,
|
||||
&format, '\0', '>'))
|
||||
return -1;
|
||||
|
||||
|
@ -1575,7 +1587,7 @@ _PyComplex_FormatAdvancedWriter(_PyUnicodeWriter *writer,
|
|||
return format_obj(obj, writer);
|
||||
|
||||
/* parse the format_spec */
|
||||
if (!parse_internal_render_format_spec(format_spec, start, end,
|
||||
if (!parse_internal_render_format_spec(obj, format_spec, start, end,
|
||||
&format, '\0', '>'))
|
||||
return -1;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue