| | | :c:func:`PyObject_Repr`. |
+-------------------+---------------------+----------------------------------+
- An unrecognized format character causes all the rest of the format string to be
- copied as-is to the result string, and any extra arguments discarded.
-
.. note::
The width formatter unit is number of characters rather than bytes.
The precision formatter unit is number of bytes for ``"%s"`` and
Support width and precision formatter for ``"%s"``, ``"%A"``, ``"%U"``,
``"%V"``, ``"%S"``, ``"%R"`` added.
+ .. versionchanged:: 3.12
+ An unrecognized format character now sets a :exc:`SystemError`.
+ In previous versions it caused all the rest of the format string to be
+ copied as-is to the result string, and any extra arguments discarded.
+
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
:py:meth:`~class.__subclasses__` (using :c:func:`PyObject_CallMethod`,
for example).
+* An unrecognized format character in :c:func:`PyUnicode_FromFormat` and
+ :c:func:`PyUnicode_FromFormatV` now sets a :exc:`SystemError`.
+ In previous versions it caused all the rest of the format string to be
+ copied as-is to the result string, and any extra arguments discarded.
+ (Contributed by Serhiy Storchaka in :gh:`95781`.)
+
Deprecated
----------
b'%c%c', c_int(0x10000), c_int(0x100000))
# test "%"
- check_format('%',
- b'%')
check_format('%',
b'%%')
check_format('%s',
check_format('repr=abc\ufffd',
b'repr=%V', None, b'abc\xff')
- # not supported: copy the raw format string. these tests are just here
- # to check for crashes and should not be considered as specifications
- check_format('%s',
- b'%1%s', b'abc')
- check_format('%1abc',
- b'%1abc')
- check_format('%+i',
- b'%+i', c_int(10))
- check_format('%.%s',
- b'%.%s', b'abc')
-
# Issue #33817: empty strings
check_format('',
b'')
check_format('',
b'%s', b'')
+ # check for crashes
+ for fmt in (b'%', b'%0', b'%01', b'%.', b'%.1',
+ b'%0%s', b'%1%s', b'%.%s', b'%.1%s', b'%1abc',
+ b'%l', b'%ll', b'%z', b'%ls', b'%lls', b'%zs'):
+ with self.subTest(fmt=fmt):
+ self.assertRaisesRegex(SystemError, 'invalid format string',
+ PyUnicode_FromFormat, fmt, b'abc')
+ self.assertRaisesRegex(SystemError, 'invalid format string',
+ PyUnicode_FromFormat, b'%+i', c_int(10))
+
# Test PyUnicode_AsWideChar()
@support.cpython_only
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
--- /dev/null
+An unrecognized format character in :c:func:`PyUnicode_FromFormat` and
+:c:func:`PyUnicode_FromFormatV` now sets a :exc:`SystemError`.
+In previous versions it caused all the rest of the format string to be
+copied as-is to the result string, and any extra arguments discarded.
p = f;
f++;
+ if (*f == '%') {
+ if (_PyUnicodeWriter_WriteCharInline(writer, '%') < 0)
+ return NULL;
+ f++;
+ return f;
+ }
+
zeropad = 0;
if (*f == '0') {
zeropad = 1;
f++;
}
}
- if (*f == '%') {
- /* "%.3%s" => f points to "3" */
- f--;
- }
- }
- if (*f == '\0') {
- /* bogus format "%.123" => go backward, f points to "3" */
- f--;
}
/* Handle %ld, %lu, %lld and %llu. */
++f;
}
- if (f[1] == '\0')
+ if (f[0] != '\0' && f[1] == '\0')
writer->overallocate = 0;
switch (*f) {
break;
}
- case '%':
- if (_PyUnicodeWriter_WriteCharInline(writer, '%') < 0)
- return NULL;
- break;
-
default:
- /* if we stumble upon an unknown formatting code, copy the rest
- of the format string to the output string. (we cannot just
- skip the code, since there's no way to know what's in the
- argument list) */
- len = strlen(p);
- if (_PyUnicodeWriter_WriteLatin1String(writer, p, len) == -1)
- return NULL;
- f = p+len;
- return f;
+ PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);
+ return NULL;
}
f++;