gh-142037: Improve error messages for printf-style formatting (GH-142081)

This affects string formatting as well as bytes and bytearray formatting.

* For errors in the format string, always include the position of the
  start of the format unit.
* For errors related to the formatted arguments, always include the number
  or the name of the formatted argument.
* Suggest more probable causes of errors in the format string (stray %,
  unsupported format, unexpected character).
* Provide more information when the number of arguments does not match
  the number of format units.
* Raise more specific errors when access of arguments by name is mixed with
  sequential access and when * is used with a mapping.
* Add tests for some uncovered cases.
This commit is contained in:
Serhiy Storchaka 2026-01-24 13:13:50 +02:00 committed by GitHub
parent 5f736a0432
commit 012c498035
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 591 additions and 207 deletions

View file

@ -792,16 +792,16 @@ def __int__(self):
pi = PseudoFloat(3.1415)
exceptions_params = [
('%x format: an integer is required, not float', b'%x', 3.14),
('%X format: an integer is required, not float', b'%X', 2.11),
('%o format: an integer is required, not float', b'%o', 1.79),
('%x format: an integer is required, not PseudoFloat', b'%x', pi),
('%x format: an integer is required, not complex', b'%x', 3j),
('%X format: an integer is required, not complex', b'%X', 2j),
('%o format: an integer is required, not complex', b'%o', 1j),
('%u format: a real number is required, not complex', b'%u', 3j),
('%i format: a real number is required, not complex', b'%i', 2j),
('%d format: a real number is required, not complex', b'%d', 2j),
('%x requires an integer, not float', b'%x', 3.14),
('%X requires an integer, not float', b'%X', 2.11),
('%o requires an integer, not float', b'%o', 1.79),
(r'%x requires an integer, not .*\.PseudoFloat', b'%x', pi),
('%x requires an integer, not complex', b'%x', 3j),
('%X requires an integer, not complex', b'%X', 2j),
('%o requires an integer, not complex', b'%o', 1j),
('%u requires a real number, not complex', b'%u', 3j),
('%i requires a real number, not complex', b'%i', 2j),
('%d requires a real number, not complex', b'%d', 2j),
(
r'%c requires an integer in range\(256\)'
r' or a single byte, not .*\.PseudoFloat',
@ -810,7 +810,7 @@ def __int__(self):
]
for msg, format_bytes, value in exceptions_params:
with self.assertRaisesRegex(TypeError, msg):
with self.assertRaisesRegex(TypeError, 'format argument: ' + msg):
operator.mod(format_bytes, value)
def test_memory_leak_gh_140939(self):

View file

@ -57,10 +57,6 @@ def testcommon(formatstr, args, output=None, limit=None, overflowok=False):
else:
b_format = formatstr
ba_format = bytearray(b_format)
b_args = []
if not isinstance(args, tuple):
args = (args, )
b_args = tuple(args)
if output is None:
b_output = ba_output = None
else:
@ -69,8 +65,8 @@ def testcommon(formatstr, args, output=None, limit=None, overflowok=False):
else:
b_output = output
ba_output = bytearray(b_output)
testformat(b_format, b_args, b_output, limit, overflowok)
testformat(ba_format, b_args, ba_output, limit, overflowok)
testformat(b_format, args, b_output, limit, overflowok)
testformat(ba_format, args, ba_output, limit, overflowok)
def test_exc(formatstr, args, exception, excmsg):
try:
@ -82,6 +78,7 @@ def test_exc(formatstr, args, exception, excmsg):
else:
if verbose: print('no')
print('Unexpected ', exception, ':', repr(str(exc)))
raise
except:
if verbose: print('no')
print('Unexpected exception')
@ -92,6 +89,8 @@ def test_exc(formatstr, args, exception, excmsg):
def test_exc_common(formatstr, args, exception, excmsg):
# test str and bytes
test_exc(formatstr, args, exception, excmsg)
if isinstance(args, dict):
args = {k.encode('ascii'): v for k, v in args.items()}
test_exc(formatstr.encode('ascii'), args, exception, excmsg)
class FormatTest(unittest.TestCase):
@ -272,45 +271,154 @@ def test_common_format(self):
if verbose:
print('Testing exceptions')
test_exc_common('%', (), ValueError, "incomplete format")
test_exc_common('% %s', 1, ValueError,
"unsupported format character '%' (0x25) at index 2")
test_exc_common('abc %', (), ValueError, "stray % at position 4")
test_exc_common('abc % %s', 1, ValueError,
"stray % at position 4 or unexpected format character '%' at position 6")
test_exc_common('abc %z', 1, ValueError,
"unsupported format %z at position 4")
test_exc_common("abc %Id", 1, ValueError,
"unsupported format %I at position 4")
test_exc_common("abc %'d", 1, ValueError,
"stray % at position 4 or unexpected format character \"'\" at position 5")
test_exc_common("abc %1 d", 1, ValueError,
"stray % at position 4 or unexpected format character ' ' at position 6")
test_exc_common('abc % (x)r', {}, ValueError,
"stray % at position 4 or unexpected format character '(' at position 6")
test_exc_common('abc %((x)r', {}, ValueError,
"stray % or incomplete format key at position 4")
test_exc_common('%r %r', 1, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%r %r', (1,), TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%r', (), TypeError,
"not enough arguments for format string (got 0)")
test_exc_common('abc %' + '9'*50 + 'r', 1, ValueError,
"width too big at position 4")
test_exc_common('abc %.' + '9'*50 + 'r', 1, ValueError,
"precision too big at position 4")
test_exc_common('%r %*r', 1, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%r %*r', (1,), TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%*r', (1,), TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%*r', (), TypeError,
"not enough arguments for format string (got 0)")
test_exc_common('%r %.*r', 1, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%r %.*r', (1,), TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%.*r', (1,), TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%.*r', (), TypeError,
"not enough arguments for format string (got 0)")
test_exc_common('%(x)r', 1, TypeError,
"format requires a mapping, not int")
test_exc_common('%*r', 1, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%*r', 3.14, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%*r', (3.14, 1), TypeError,
"format argument 1: * requires int, not float")
test_exc_common('%.*r', 1, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%.*r', 3.14, TypeError,
"not enough arguments for format string (got 1)")
test_exc_common('%.*r', (3.14, 1), TypeError,
"format argument 1: * requires int, not float")
test_exc_common('%*r', (2**1000, 1), OverflowError,
"format argument 1: too big for width")
test_exc_common('%*r', (-2**1000, 1), OverflowError,
"format argument 1: too big for width")
test_exc_common('%.*r', (2**1000, 1), OverflowError,
"format argument 1: too big for precision")
test_exc_common('%.*r', (-2**1000, 1), OverflowError,
"format argument 1: too big for precision")
test_exc_common('%d', '1', TypeError,
"%d format: a real number is required, not str")
"format argument: %d requires a real number, not str")
test_exc_common('%d', b'1', TypeError,
"%d format: a real number is required, not bytes")
"format argument: %d requires a real number, not bytes")
test_exc_common('%d', ('1',), TypeError,
"format argument 1: %d requires a real number, not str")
test_exc_common('%x', '1', TypeError,
"%x format: an integer is required, not str")
"format argument: %x requires an integer, not str")
test_exc_common('%x', 3.14, TypeError,
"%x format: an integer is required, not float")
"format argument: %x requires an integer, not float")
test_exc_common('%x', ('1',), TypeError,
"format argument 1: %x requires an integer, not str")
test_exc_common('%i', '1', TypeError,
"%i format: a real number is required, not str")
"format argument: %i requires a real number, not str")
test_exc_common('%i', b'1', TypeError,
"%i format: a real number is required, not bytes")
"format argument: %i requires a real number, not bytes")
test_exc_common('%g', '1', TypeError,
"format argument: %g requires a real number, not str")
test_exc_common('%g', ('1',), TypeError,
"format argument 1: %g requires a real number, not str")
def test_str_format(self):
testformat("%r", "\u0378", "'\\u0378'") # non printable
testformat("%a", "\u0378", "'\\u0378'") # non printable
testformat("%r", "\u0374", "'\u0374'") # printable
testformat("%a", "\u0374", "'\\u0374'") # printable
testformat('%(x)r', {'x': 1}, '1')
# Test exception for unknown format characters, etc.
if verbose:
print('Testing exceptions')
test_exc('abc %b', 1, ValueError,
"unsupported format character 'b' (0x62) at index 5")
#test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError,
# "unsupported format character '?' (0x3000) at index 5")
test_exc('%g', '1', TypeError, "must be real number, not str")
"unsupported format %b at position 4")
test_exc("abc %\nd", 1, ValueError,
"stray % at position 4 or unexpected format character U+000A at position 5")
test_exc("abc %\x1fd", 1, ValueError,
"stray % at position 4 or unexpected format character U+001F at position 5")
test_exc("abc %\x7fd", 1, ValueError,
"stray % at position 4 or unexpected format character U+007F at position 5")
test_exc("abc %\x80d", 1, ValueError,
"stray % at position 4 or unexpected format character U+0080 at position 5")
test_exc('abc %äd', 1, ValueError,
"stray % at position 4 or unexpected format character 'ä' (U+00E4) at position 5")
test_exc('abc %€d', 1, ValueError,
"stray % at position 4 or unexpected format character '' (U+20AC) at position 5")
test_exc('no format', '1', TypeError,
"not all arguments converted during string formatting")
test_exc('%c', -1, OverflowError, "%c arg not in range(0x110000)")
"not all arguments converted during string formatting (required 0, got 1)")
test_exc('%r', (1, 2), TypeError,
"not all arguments converted during string formatting (required 1, got 2)")
test_exc('%(x)r %r', {'x': 1}, ValueError,
"format requires a parenthesised mapping key at position 6")
test_exc('%(x)*r', {'x': 1}, ValueError,
"* cannot be used with a parenthesised mapping key at position 0")
test_exc('%(x).*r', {'x': 1}, ValueError,
"* cannot be used with a parenthesised mapping key at position 0")
test_exc('%(x)d', {'x': '1'}, TypeError,
"format argument 'x': %d requires a real number, not str")
test_exc('%(x)x', {'x': '1'}, TypeError,
"format argument 'x': %x requires an integer, not str")
test_exc('%(x)g', {'x': '1'}, TypeError,
"format argument 'x': %g requires a real number, not str")
test_exc('%c', -1, OverflowError,
"format argument: %c argument not in range(0x110000)")
test_exc('%c', (-1,), OverflowError,
"format argument 1: %c argument not in range(0x110000)")
test_exc('%(x)c', {'x': -1}, OverflowError,
"format argument 'x': %c argument not in range(0x110000)")
test_exc('%c', sys.maxunicode+1, OverflowError,
"%c arg not in range(0x110000)")
#test_exc('%c', 2**128, OverflowError, "%c arg not in range(0x110000)")
test_exc('%c', 3.14, TypeError, "%c requires an int or a unicode character, not float")
test_exc('%c', 'ab', TypeError, "%c requires an int or a unicode character, not a string of length 2")
test_exc('%c', b'x', TypeError, "%c requires an int or a unicode character, not bytes")
"format argument: %c argument not in range(0x110000)")
test_exc('%c', 2**128, OverflowError,
"format argument: %c argument not in range(0x110000)")
test_exc('%c', 3.14, TypeError,
"format argument: %c requires an integer or a unicode character, not float")
test_exc('%c', (3.14,), TypeError,
"format argument 1: %c requires an integer or a unicode character, not float")
test_exc('%(x)c', {'x': 3.14}, TypeError,
"format argument 'x': %c requires an integer or a unicode character, not float")
test_exc('%c', 'ab', TypeError,
"format argument: %c requires an integer or a unicode character, not a string of length 2")
test_exc('%c', ('ab',), TypeError,
"format argument 1: %c requires an integer or a unicode character, not a string of length 2")
test_exc('%(x)c', {'x': 'ab'}, TypeError,
"format argument 'x': %c requires an integer or a unicode character, not a string of length 2")
test_exc('%c', b'x', TypeError,
"format argument: %c requires an integer or a unicode character, not bytes")
if maxsize == 2**31-1:
# crashes 2.2.1 and earlier:
@ -355,36 +463,83 @@ def __bytes__(self):
testcommon(b"%r", b"ghi", b"b'ghi'")
testcommon(b"%r", "jkl", b"'jkl'")
testcommon(b"%r", "\u0544", b"'\\u0544'")
testcommon(b'%(x)r', {b'x': 1}, b'1')
# Test exception for unknown format characters, etc.
if verbose:
print('Testing exceptions')
test_exc(b'%g', '1', TypeError, "float argument required, not str")
test_exc(b'%g', b'1', TypeError, "float argument required, not bytes")
test_exc(b"abc %\nd", 1, ValueError,
"stray % at position 4 or unexpected format character with code 0x0a at position 5")
test_exc(b"abc %'d", 1, ValueError,
"stray % at position 4 or unexpected format character \"'\" at position 5")
test_exc(b"abc %\x1fd", 1, ValueError,
"stray % at position 4 or unexpected format character with code 0x1f at position 5")
test_exc(b"abc %\x7fd", 1, ValueError,
"stray % at position 4 or unexpected format character with code 0x7f at position 5")
test_exc(b"abc %\x80d", 1, ValueError,
"stray % at position 4 or unexpected format character with code 0x80 at position 5")
test_exc(b'no format', 7, TypeError,
"not all arguments converted during bytes formatting")
"not all arguments converted during bytes formatting (required 0, got 1)")
test_exc(b'no format', b'1', TypeError,
"not all arguments converted during bytes formatting")
"not all arguments converted during bytes formatting (required 0, got 1)")
test_exc(b'no format', bytearray(b'1'), TypeError,
"not all arguments converted during bytes formatting")
"not all arguments converted during bytes formatting (required 0, got 1)")
test_exc(b'%r', (1, 2), TypeError,
"not all arguments converted during bytes formatting (required 1, got 2)")
test_exc(b'%(x)r %r', {b'x': 1}, ValueError,
"format requires a parenthesised mapping key at position 6")
test_exc(b'%(x)*r', {b'x': 1}, ValueError,
"* cannot be used with a parenthesised mapping key at position 0")
test_exc(b'%(x).*r', {b'x': 1}, ValueError,
"* cannot be used with a parenthesised mapping key at position 0")
test_exc(b'%(x)d', {b'x': '1'}, TypeError,
"format argument b'x': %d requires a real number, not str")
test_exc(b'%(x)x', {b'x': '1'}, TypeError,
"format argument b'x': %x requires an integer, not str")
test_exc(b'%(x)g', {b'x': '1'}, TypeError,
"format argument b'x': %g requires a real number, not str")
test_exc(b"%c", -1, OverflowError,
"%c arg not in range(256)")
"format argument: %c argument not in range(256)")
test_exc(b"%c", (-1,), OverflowError,
"format argument 1: %c argument not in range(256)")
test_exc(b"%(x)c", {b'x': -1}, OverflowError,
"format argument b'x': %c argument not in range(256)")
test_exc(b"%c", 256, OverflowError,
"%c arg not in range(256)")
"format argument: %c argument not in range(256)")
test_exc(b"%c", 2**128, OverflowError,
"%c arg not in range(256)")
"format argument: %c argument not in range(256)")
test_exc(b"%c", b"Za", TypeError,
"%c requires an integer in range(256) or a single byte, not a bytes object of length 2")
"format argument: %c requires an integer in range(256) or a single byte, not a bytes object of length 2")
test_exc(b"%c", (b"Za",), TypeError,
"format argument 1: %c requires an integer in range(256) or a single byte, not a bytes object of length 2")
test_exc(b"%(x)c", {b'x': b"Za"}, TypeError,
"format argument b'x': %c requires an integer in range(256) or a single byte, not a bytes object of length 2")
test_exc(b"%c", bytearray(b"Za"), TypeError,
"format argument: %c requires an integer in range(256) or a single byte, not a bytearray object of length 2")
test_exc(b"%c", (bytearray(b"Za"),), TypeError,
"format argument 1: %c requires an integer in range(256) or a single byte, not a bytearray object of length 2")
test_exc(b"%(x)c", {b'x': bytearray(b"Za")}, TypeError,
"format argument b'x': %c requires an integer in range(256) or a single byte, not a bytearray object of length 2")
test_exc(b"%c", "Y", TypeError,
"%c requires an integer in range(256) or a single byte, not str")
"format argument: %c requires an integer in range(256) or a single byte, not str")
test_exc(b"%c", 3.14, TypeError,
"%c requires an integer in range(256) or a single byte, not float")
"format argument: %c requires an integer in range(256) or a single byte, not float")
test_exc(b"%c", (3.14,), TypeError,
"format argument 1: %c requires an integer in range(256) or a single byte, not float")
test_exc(b"%(x)c", {b'x': 3.14}, TypeError,
"format argument b'x': %c requires an integer in range(256) or a single byte, not float")
test_exc(b"%b", "Xc", TypeError,
"%b requires a bytes-like object, "
"or an object that implements __bytes__, not 'str'")
"format argument: %b requires a bytes-like object, "
"or an object that implements __bytes__, not str")
test_exc(b"%b", ("Xc",), TypeError,
"format argument 1: %b requires a bytes-like object, "
"or an object that implements __bytes__, not str")
test_exc(b"%(x)b", {b'x': "Xc"}, TypeError,
"format argument b'x': %b requires a bytes-like object, "
"or an object that implements __bytes__, not str")
test_exc(b"%s", "Wd", TypeError,
"%b requires a bytes-like object, "
"or an object that implements __bytes__, not 'str'")
"format argument: %b requires a bytes-like object, "
"or an object that implements __bytes__, not str")
if maxsize == 2**31-1:
# crashes 2.2.1 and earlier:
@ -626,7 +781,7 @@ def test_specifier_z_error(self):
with self.assertRaisesRegex(ValueError, error_msg):
f"{'x':zs}" # can't apply to string
error_msg = re.escape("unsupported format character 'z'")
error_msg = re.escape("unsupported format %z at position 0")
with self.assertRaisesRegex(ValueError, error_msg):
"%z.1f" % 0 # not allowed in old style string interpolation
with self.assertRaisesRegex(ValueError, error_msg):

View file

@ -733,22 +733,27 @@ def test_format_errors(self):
with self.assertRaisesRegex(TypeError,
'not all arguments converted during string formatting'):
eval("'%s' % (x, y)", {'x': 1, 'y': 2})
with self.assertRaisesRegex(ValueError, 'incomplete format'):
with self.assertRaisesRegex(ValueError, 'stray % at position 2'):
eval("'%s%' % (x,)", {'x': 1234})
with self.assertRaisesRegex(ValueError, 'incomplete format'):
with self.assertRaisesRegex(ValueError, 'stray % at position 4'):
eval("'%s%%%' % (x,)", {'x': 1234})
with self.assertRaisesRegex(TypeError,
'not enough arguments for format string'):
eval("'%s%z' % (x,)", {'x': 1234})
with self.assertRaisesRegex(ValueError, 'unsupported format character'):
with self.assertRaisesRegex(ValueError,
'unsupported format %z at position 2'):
eval("'%s%z' % (x, 5)", {'x': 1234})
with self.assertRaisesRegex(TypeError, 'a real number is required, not str'):
with self.assertRaisesRegex(TypeError,
'format argument 1: %d requires a real number, not str'):
eval("'%d' % (x,)", {'x': '1234'})
with self.assertRaisesRegex(TypeError, 'an integer is required, not float'):
with self.assertRaisesRegex(TypeError,
'format argument 1: %x requires an integer, not float'):
eval("'%x' % (x,)", {'x': 1234.56})
with self.assertRaisesRegex(TypeError, 'an integer is required, not str'):
with self.assertRaisesRegex(TypeError,
'format argument 1: %x requires an integer, not str'):
eval("'%x' % (x,)", {'x': '1234'})
with self.assertRaisesRegex(TypeError, 'must be real number, not str'):
with self.assertRaisesRegex(TypeError,
'format argument 1: %f requires a real number, not str'):
eval("'%f' % (x,)", {'x': '1234'})
with self.assertRaisesRegex(TypeError,
'not enough arguments for format string'):

View file

@ -1578,17 +1578,40 @@ def __int__(self):
self.assertEqual('%X' % letter_m, '6D')
self.assertEqual('%o' % letter_m, '155')
self.assertEqual('%c' % letter_m, 'm')
self.assertRaisesRegex(TypeError, '%x format: an integer is required, not float', operator.mod, '%x', 3.14)
self.assertRaisesRegex(TypeError, '%X format: an integer is required, not float', operator.mod, '%X', 2.11)
self.assertRaisesRegex(TypeError, '%o format: an integer is required, not float', operator.mod, '%o', 1.79)
self.assertRaisesRegex(TypeError, '%x format: an integer is required, not PseudoFloat', operator.mod, '%x', pi)
self.assertRaisesRegex(TypeError, '%x format: an integer is required, not complex', operator.mod, '%x', 3j)
self.assertRaisesRegex(TypeError, '%X format: an integer is required, not complex', operator.mod, '%X', 2j)
self.assertRaisesRegex(TypeError, '%o format: an integer is required, not complex', operator.mod, '%o', 1j)
self.assertRaisesRegex(TypeError, '%u format: a real number is required, not complex', operator.mod, '%u', 3j)
self.assertRaisesRegex(TypeError, '%i format: a real number is required, not complex', operator.mod, '%i', 2j)
self.assertRaisesRegex(TypeError, '%d format: a real number is required, not complex', operator.mod, '%d', 1j)
self.assertRaisesRegex(TypeError, r'%c requires an int or a unicode character, not .*\.PseudoFloat', operator.mod, '%c', pi)
with self.assertRaisesRegex(TypeError,
'format argument: %x requires an integer, not float'):
'%x' % 3.14
with self.assertRaisesRegex(TypeError,
'format argument: %X requires an integer, not float'):
'%X' % 2.11
with self.assertRaisesRegex(TypeError,
'format argument: %o requires an integer, not float'):
'%o' % 1.79
with self.assertRaisesRegex(TypeError,
r'format argument: %x requires an integer, not .*\.PseudoFloat'):
'%x' % pi
with self.assertRaisesRegex(TypeError,
'format argument: %x requires an integer, not complex'):
'%x' % 3j
with self.assertRaisesRegex(TypeError,
'format argument: %X requires an integer, not complex'):
'%X' % 2j
with self.assertRaisesRegex(TypeError,
'format argument: %o requires an integer, not complex'):
'%o' % 1j
with self.assertRaisesRegex(TypeError,
'format argument: %u requires a real number, not complex'):
'%u' % 3j
with self.assertRaisesRegex(TypeError,
'format argument: %i requires a real number, not complex'):
'%i' % 2j
with self.assertRaisesRegex(TypeError,
'format argument: %d requires a real number, not complex'):
'%d' % 1j
with self.assertRaisesRegex(TypeError,
r'format argument: %c requires an integer or a unicode character, '
r'not .*\.PseudoFloat'):
'%c' % pi
class RaisingNumber:
def __int__(self):

View file

@ -0,0 +1,7 @@
Improve error messages for printf-style formatting.
For errors in the format string, always include the position of the
start of the format unit.
For errors related to the formatted arguments, always include the number
or the name of the argument.
Raise more specific errors and include more information (type and number
of arguments, most probable causes of error).

View file

@ -404,26 +404,44 @@ PyBytes_FromFormat(const char *format, ...)
/* Helpers for formatstring */
#define FORMAT_ERROR(EXC, FMT, ...) do { \
if (key != NULL) { \
PyErr_Format((EXC), "format argument %R: " FMT, \
key, __VA_ARGS__); \
} \
else if (argidx >= 0) { \
PyErr_Format((EXC), "format argument %zd: " FMT, \
argidx, __VA_ARGS__); \
} \
else { \
PyErr_Format((EXC), "format argument: " FMT, __VA_ARGS__); \
} \
} while (0)
Py_LOCAL_INLINE(PyObject *)
getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx)
getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx, int allowone)
{
Py_ssize_t argidx = *p_argidx;
if (argidx < arglen) {
(*p_argidx)++;
if (arglen < 0)
return args;
else
if (arglen >= 0) {
return PyTuple_GetItem(args, argidx);
}
else if (allowone) {
return args;
}
}
PyErr_SetString(PyExc_TypeError,
"not enough arguments for format string");
PyErr_Format(PyExc_TypeError,
"not enough arguments for format string (got %zd)",
arglen < 0 ? 1 : arglen);
return NULL;
}
/* Returns a new reference to a PyBytes object, or NULL on failure. */
static char*
formatfloat(PyObject *v, int flags, int prec, int type,
formatfloat(PyObject *v, Py_ssize_t argidx, PyObject *key,
int flags, int prec, int type,
PyObject **p_result, PyBytesWriter *writer, char *str)
{
char *p;
@ -434,8 +452,11 @@ formatfloat(PyObject *v, int flags, int prec, int type,
x = PyFloat_AsDouble(v);
if (x == -1.0 && PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError, "float argument required, "
"not %.200s", Py_TYPE(v)->tp_name);
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
FORMAT_ERROR(PyExc_TypeError,
"%%%c requires a real number, not %T",
type, v);
}
return NULL;
}
@ -470,7 +491,8 @@ formatfloat(PyObject *v, int flags, int prec, int type,
}
static PyObject *
formatlong(PyObject *v, int flags, int prec, int type)
formatlong(PyObject *v, Py_ssize_t argidx, PyObject *key,
int flags, int prec, int type)
{
PyObject *result, *iobj;
if (PyLong_Check(v))
@ -490,20 +512,21 @@ formatlong(PyObject *v, int flags, int prec, int type)
if (!PyErr_ExceptionMatches(PyExc_TypeError))
return NULL;
}
PyErr_Format(PyExc_TypeError,
"%%%c format: %s is required, not %.200s", type,
(type == 'o' || type == 'x' || type == 'X') ? "an integer"
: "a real number",
Py_TYPE(v)->tp_name);
FORMAT_ERROR(PyExc_TypeError,
"%%%c requires %s, not %T",
type,
(type == 'o' || type == 'x' || type == 'X') ? "an integer"
: "a real number",
v);
return NULL;
}
static int
byte_converter(PyObject *arg, char *p)
byte_converter(PyObject *arg, Py_ssize_t argidx, PyObject *key, char *p)
{
if (PyBytes_Check(arg)) {
if (PyBytes_GET_SIZE(arg) != 1) {
PyErr_Format(PyExc_TypeError,
FORMAT_ERROR(PyExc_TypeError,
"%%c requires an integer in range(256) or "
"a single byte, not a bytes object of length %zd",
PyBytes_GET_SIZE(arg));
@ -514,7 +537,7 @@ byte_converter(PyObject *arg, char *p)
}
else if (PyByteArray_Check(arg)) {
if (PyByteArray_GET_SIZE(arg) != 1) {
PyErr_Format(PyExc_TypeError,
FORMAT_ERROR(PyExc_TypeError,
"%%c requires an integer in range(256) or "
"a single byte, not a bytearray object of length %zd",
PyByteArray_GET_SIZE(arg));
@ -531,23 +554,25 @@ byte_converter(PyObject *arg, char *p)
}
if (!(0 <= ival && ival <= 255)) {
/* this includes an overflow in converting to C long */
PyErr_SetString(PyExc_OverflowError,
"%c arg not in range(256)");
FORMAT_ERROR(PyExc_OverflowError,
"%%c argument not in range(256)%s", "");
return 0;
}
*p = (char)ival;
return 1;
}
PyErr_Format(PyExc_TypeError,
"%%c requires an integer in range(256) or a single byte, not %T",
arg);
FORMAT_ERROR(PyExc_TypeError,
"%%c requires an integer in range(256) or "
"a single byte, not %T",
arg);
return 0;
}
static PyObject *_PyBytes_FromBuffer(PyObject *x);
static PyObject *
format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
format_obj(PyObject *v, Py_ssize_t argidx, PyObject *key,
const char **pbuf, Py_ssize_t *plen)
{
PyObject *func, *result;
/* is it a bytes object? */
@ -589,10 +614,10 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
*plen = PyBytes_GET_SIZE(result);
return result;
}
PyErr_Format(PyExc_TypeError,
FORMAT_ERROR(PyExc_TypeError,
"%%b requires a bytes-like object, "
"or an object that implements __bytes__, not '%.100s'",
Py_TYPE(v)->tp_name);
"or an object that implements __bytes__, not %T",
v);
return NULL;
}
@ -607,6 +632,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
Py_ssize_t fmtcnt;
int args_owned = 0;
PyObject *dict = NULL;
PyObject *key = NULL;
if (args == NULL) {
PyErr_BadInternalCall();
@ -680,15 +706,17 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
fmtcnt--;
continue;
}
Py_CLEAR(key);
const char *fmtstart = fmt;
if (*fmt == '(') {
const char *keystart;
Py_ssize_t keylen;
PyObject *key;
int pcount = 1;
if (dict == NULL) {
PyErr_SetString(PyExc_TypeError,
"format requires a mapping");
PyErr_Format(PyExc_TypeError,
"format requires a mapping, not %T",
args);
goto error;
}
++fmt;
@ -704,8 +732,10 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
}
keylen = fmt - keystart - 1;
if (fmtcnt < 0 || pcount > 0) {
PyErr_SetString(PyExc_ValueError,
"incomplete format key");
PyErr_Format(PyExc_ValueError,
"stray %% or incomplete format key "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
key = PyBytes_FromStringAndSize(keystart,
@ -717,13 +747,21 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
args_owned = 0;
}
args = PyObject_GetItem(dict, key);
Py_DECREF(key);
if (args == NULL) {
goto error;
}
args_owned = 1;
arglen = -1;
argidx = -2;
arglen = -3;
argidx = -4;
}
else {
if (arglen < -1) {
PyErr_Format(PyExc_ValueError,
"format requires a parenthesised mapping key "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
}
/* Parse flags. Example: "%+i" => flags=F_SIGN. */
@ -740,17 +778,28 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
/* Parse width. Example: "%10s" => width=10 */
if (c == '*') {
v = getnextarg(args, arglen, &argidx);
if (arglen < -1) {
PyErr_Format(PyExc_ValueError,
"* cannot be used with a parenthesised mapping key "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
v = getnextarg(args, arglen, &argidx, 0);
if (v == NULL)
goto error;
if (!PyLong_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"* wants int");
FORMAT_ERROR(PyExc_TypeError, "* requires int, not %T", v);
goto error;
}
width = PyLong_AsSsize_t(v);
if (width == -1 && PyErr_Occurred())
if (width == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
FORMAT_ERROR(PyExc_OverflowError,
"too big for width%s", "");
}
goto error;
}
if (width < 0) {
flags |= F_LJUST;
width = -width;
@ -765,9 +814,9 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
if (!Py_ISDIGIT(c))
break;
if (width > (PY_SSIZE_T_MAX - ((int)c - '0')) / 10) {
PyErr_SetString(
PyExc_ValueError,
"width too big");
PyErr_Format(PyExc_ValueError,
"width too big at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
width = width*10 + (c - '0');
@ -780,18 +829,29 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
if (--fmtcnt >= 0)
c = *fmt++;
if (c == '*') {
v = getnextarg(args, arglen, &argidx);
if (arglen < -1) {
PyErr_Format(PyExc_ValueError,
"* cannot be used with a parenthesised mapping key "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
v = getnextarg(args, arglen, &argidx, 0);
if (v == NULL)
goto error;
if (!PyLong_Check(v)) {
PyErr_SetString(
PyExc_TypeError,
"* wants int");
FORMAT_ERROR(PyExc_TypeError,
"* requires int, not %T", v);
goto error;
}
prec = PyLong_AsInt(v);
if (prec == -1 && PyErr_Occurred())
if (prec == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
FORMAT_ERROR(PyExc_OverflowError,
"too big for precision%s", "");
}
goto error;
}
if (prec < 0)
prec = 0;
if (--fmtcnt >= 0)
@ -804,9 +864,9 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
if (!Py_ISDIGIT(c))
break;
if (prec > (INT_MAX - ((int)c - '0')) / 10) {
PyErr_SetString(
PyExc_ValueError,
"prec too big");
PyErr_Format(PyExc_ValueError,
"precision too big at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
prec = prec*10 + (c - '0');
@ -820,11 +880,12 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
}
}
if (fmtcnt < 0) {
PyErr_SetString(PyExc_ValueError,
"incomplete format");
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd",
(Py_ssize_t)(fmtstart - format - 1));
goto error;
}
v = getnextarg(args, arglen, &argidx);
v = getnextarg(args, arglen, &argidx, 1);
if (v == NULL)
goto error;
@ -852,7 +913,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
case 's':
// %s is only for 2/3 code; 3 only code should use %b
case 'b':
temp = format_obj(v, &pbuf, &len);
temp = format_obj(v, argidx, key, &pbuf, &len);
if (temp == NULL)
goto error;
if (prec >= 0 && len > prec)
@ -900,7 +961,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
continue;
}
temp = formatlong(v, flags, prec, c);
temp = formatlong(v, argidx, key, flags, prec, c);
if (!temp)
goto error;
assert(PyUnicode_IS_ASCII(temp));
@ -921,13 +982,13 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
&& !(flags & (F_SIGN | F_BLANK)))
{
/* Fast path */
res = formatfloat(v, flags, prec, c, NULL, writer, res);
res = formatfloat(v, argidx, key, flags, prec, c, NULL, writer, res);
if (res == NULL)
goto error;
continue;
}
if (!formatfloat(v, flags, prec, c, &temp, NULL, res))
if (!formatfloat(v, argidx, key, flags, prec, c, &temp, NULL, res))
goto error;
pbuf = PyBytes_AS_STRING(temp);
len = PyBytes_GET_SIZE(temp);
@ -938,7 +999,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
case 'c':
pbuf = &onechar;
len = byte_converter(v, &onechar);
len = byte_converter(v, argidx, key, &onechar);
if (!len)
goto error;
if (width == -1) {
@ -949,11 +1010,36 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
break;
default:
PyErr_Format(PyExc_ValueError,
"unsupported format character '%c' (0x%x) "
"at index %zd",
c, c,
(Py_ssize_t)(fmt - 1 - format));
if (Py_ISALPHA(c)) {
PyErr_Format(PyExc_ValueError,
"unsupported format %%%c at position %zd",
c, (Py_ssize_t)(fmtstart - format - 1));
}
else if (c == '\'') {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character \"'\" "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1),
(Py_ssize_t)(fmt - format - 1));
}
else if (c >= 32 && c < 127 && c != '\'') {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character '%c' "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1),
c, (Py_ssize_t)(fmt - format - 1));
}
else {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character with code 0x%02x "
"at position %zd",
(Py_ssize_t)(fmtstart - format - 1),
Py_CHARMASK(c),
(Py_ssize_t)(fmt - format - 1));
}
goto error;
}
@ -1042,6 +1128,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
}
if (dict && (argidx < arglen)) {
// XXX: Never happens?
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during bytes formatting");
Py_XDECREF(temp);
@ -1061,8 +1148,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
} /* until end */
if (argidx < arglen && !dict) {
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during bytes formatting");
PyErr_Format(PyExc_TypeError,
"not all arguments converted during bytes formatting "
"(required %zd, got %zd)",
arglen < 0 ? 0 : argidx,
arglen < 0 ? 1 : arglen);
goto error;
}
@ -1072,6 +1162,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
return PyBytesWriter_FinishWithPointer(writer, res);
error:
Py_XDECREF(key);
PyBytesWriter_Discard(writer);
if (args_owned) {
Py_DECREF(args);

View file

@ -72,23 +72,44 @@ struct unicode_format_arg_t {
Py_ssize_t width;
int prec;
int sign;
Py_ssize_t fmtstart;
PyObject *key;
};
// Use FORMAT_ERROR("...%s", "") when there is no arguments.
#define FORMAT_ERROR(EXC, FMT, ...) do { \
if (arg->key != NULL) { \
PyErr_Format((EXC), "format argument %R: " FMT, \
arg->key, __VA_ARGS__); \
} \
else if (ctx->argidx >= 0) { \
PyErr_Format((EXC), "format argument %zd: " FMT, \
ctx->argidx, __VA_ARGS__); \
} \
else { \
PyErr_Format((EXC), "format argument: " FMT, __VA_ARGS__); \
} \
} while (0)
static PyObject *
unicode_format_getnextarg(struct unicode_formatter_t *ctx)
unicode_format_getnextarg(struct unicode_formatter_t *ctx, int allowone)
{
Py_ssize_t argidx = ctx->argidx;
if (argidx < ctx->arglen) {
if (argidx < ctx->arglen && (allowone || ctx->arglen >= 0)) {
ctx->argidx++;
if (ctx->arglen < 0)
return ctx->args;
else
if (ctx->arglen >= 0) {
return PyTuple_GetItem(ctx->args, argidx);
}
else if (allowone) {
return ctx->args;
}
}
PyErr_SetString(PyExc_TypeError,
"not enough arguments for format string");
PyErr_Format(PyExc_TypeError,
"not enough arguments for format string (got %zd)",
ctx->arglen < 0 ? 1 : ctx->arglen);
return NULL;
}
@ -100,7 +121,9 @@ unicode_format_getnextarg(struct unicode_formatter_t *ctx)
Return 0 on success, raise an exception and return -1 on error. */
static int
formatfloat(PyObject *v, struct unicode_format_arg_t *arg,
formatfloat(PyObject *v,
struct unicode_formatter_t *ctx,
struct unicode_format_arg_t *arg,
PyObject **p_output,
_PyUnicodeWriter *writer)
{
@ -111,8 +134,14 @@ formatfloat(PyObject *v, struct unicode_format_arg_t *arg,
int dtoa_flags = 0;
x = PyFloat_AsDouble(v);
if (x == -1.0 && PyErr_Occurred())
if (x == -1.0 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
FORMAT_ERROR(PyExc_TypeError,
"%%%c requires a real number, not %T",
arg->ch, v);
}
return -1;
}
prec = arg->prec;
if (prec < 0)
@ -287,6 +316,7 @@ _PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type)
* -1 and raise an exception on error */
static int
mainformatlong(PyObject *v,
struct unicode_formatter_t *ctx,
struct unicode_format_arg_t *arg,
PyObject **p_output,
_PyUnicodeWriter *writer)
@ -364,16 +394,14 @@ mainformatlong(PyObject *v,
case 'o':
case 'x':
case 'X':
PyErr_Format(PyExc_TypeError,
"%%%c format: an integer is required, "
"not %.200s",
type, Py_TYPE(v)->tp_name);
FORMAT_ERROR(PyExc_TypeError,
"%%%c requires an integer, not %T",
arg->ch, v);
break;
default:
PyErr_Format(PyExc_TypeError,
"%%%c format: a real number is required, "
"not %.200s",
type, Py_TYPE(v)->tp_name);
FORMAT_ERROR(PyExc_TypeError,
"%%%c requires a real number, not %T",
arg->ch, v);
break;
}
return -1;
@ -381,15 +409,17 @@ mainformatlong(PyObject *v,
static Py_UCS4
formatchar(PyObject *v)
formatchar(PyObject *v,
struct unicode_formatter_t *ctx,
struct unicode_format_arg_t *arg)
{
/* presume that the buffer is at least 3 characters long */
if (PyUnicode_Check(v)) {
if (PyUnicode_GET_LENGTH(v) == 1) {
return PyUnicode_READ_CHAR(v, 0);
}
PyErr_Format(PyExc_TypeError,
"%%c requires an int or a unicode character, "
FORMAT_ERROR(PyExc_TypeError,
"%%c requires an integer or a unicode character, "
"not a string of length %zd",
PyUnicode_GET_LENGTH(v));
return (Py_UCS4) -1;
@ -399,18 +429,18 @@ formatchar(PyObject *v)
long x = PyLong_AsLongAndOverflow(v, &overflow);
if (x == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"%%c requires an int or a unicode character, not %T",
FORMAT_ERROR(PyExc_TypeError,
"%%c requires an integer or a unicode character, "
"not %T",
v);
return (Py_UCS4) -1;
}
return (Py_UCS4) -1;
}
if (x < 0 || x > MAX_UNICODE) {
/* this includes an overflow in converting to C long */
PyErr_SetString(PyExc_OverflowError,
"%c arg not in range(0x110000)");
FORMAT_ERROR(PyExc_OverflowError,
"%%c argument not in range(0x110000)%s", "");
return (Py_UCS4) -1;
}
@ -438,12 +468,12 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
/* Get argument value from a dictionary. Example: "%(name)s". */
Py_ssize_t keystart;
Py_ssize_t keylen;
PyObject *key;
int pcount = 1;
if (ctx->dict == NULL) {
PyErr_SetString(PyExc_TypeError,
"format requires a mapping");
PyErr_Format(PyExc_TypeError,
"format requires a mapping, not %T",
ctx->args);
return -1;
}
++ctx->fmtpos;
@ -460,25 +490,34 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
}
keylen = ctx->fmtpos - keystart - 1;
if (ctx->fmtcnt < 0 || pcount > 0) {
PyErr_SetString(PyExc_ValueError,
"incomplete format key");
PyErr_Format(PyExc_ValueError,
"stray %% or incomplete format key at position %zd",
arg->fmtstart);
return -1;
}
key = PyUnicode_Substring(ctx->fmtstr,
keystart, keystart + keylen);
if (key == NULL)
arg->key = PyUnicode_Substring(ctx->fmtstr,
keystart, keystart + keylen);
if (arg->key == NULL)
return -1;
if (ctx->args_owned) {
ctx->args_owned = 0;
Py_DECREF(ctx->args);
}
ctx->args = PyObject_GetItem(ctx->dict, key);
Py_DECREF(key);
ctx->args = PyObject_GetItem(ctx->dict, arg->key);
if (ctx->args == NULL)
return -1;
ctx->args_owned = 1;
ctx->arglen = -1;
ctx->argidx = -2;
ctx->arglen = -3;
ctx->argidx = -4;
}
else {
if (ctx->arglen < -1) {
PyErr_Format(PyExc_ValueError,
"format requires a parenthesised mapping key "
"at position %zd",
arg->fmtstart);
return -1;
}
}
/* Parse flags. Example: "%+i" => flags=F_SIGN. */
@ -497,17 +536,28 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
/* Parse width. Example: "%10s" => width=10 */
if (arg->ch == '*') {
v = unicode_format_getnextarg(ctx);
if (ctx->arglen < -1) {
PyErr_Format(PyExc_ValueError,
"* cannot be used with a parenthesised mapping key "
"at position %zd",
arg->fmtstart);
return -1;
}
v = unicode_format_getnextarg(ctx, 0);
if (v == NULL)
return -1;
if (!PyLong_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"* wants int");
FORMAT_ERROR(PyExc_TypeError, "* requires int, not %T", v);
return -1;
}
arg->width = PyLong_AsSsize_t(v);
if (arg->width == -1 && PyErr_Occurred())
if (arg->width == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
FORMAT_ERROR(PyExc_OverflowError,
"too big for width%s", "");
}
return -1;
}
if (arg->width < 0) {
arg->flags |= F_LJUST;
arg->width = -arg->width;
@ -528,8 +578,9 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
mixing signed and unsigned comparison. Since arg->ch is between
'0' and '9', casting to int is safe. */
if (arg->width > (PY_SSIZE_T_MAX - ((int)arg->ch - '0')) / 10) {
PyErr_SetString(PyExc_ValueError,
"width too big");
PyErr_Format(PyExc_ValueError,
"width too big at position %zd",
arg->fmtstart);
return -1;
}
arg->width = arg->width*10 + (arg->ch - '0');
@ -544,17 +595,28 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
ctx->fmtpos++;
}
if (arg->ch == '*') {
v = unicode_format_getnextarg(ctx);
if (ctx->arglen < -1) {
PyErr_Format(PyExc_ValueError,
"* cannot be used with a parenthesised mapping key "
"at position %zd",
arg->fmtstart);
return -1;
}
v = unicode_format_getnextarg(ctx, 0);
if (v == NULL)
return -1;
if (!PyLong_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"* wants int");
FORMAT_ERROR(PyExc_TypeError, "* requires int, not %T", v);
return -1;
}
arg->prec = PyLong_AsInt(v);
if (arg->prec == -1 && PyErr_Occurred())
if (arg->prec == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
FORMAT_ERROR(PyExc_OverflowError,
"too big for precision%s", "");
}
return -1;
}
if (arg->prec < 0)
arg->prec = 0;
if (--ctx->fmtcnt >= 0) {
@ -570,8 +632,9 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
if (arg->ch < '0' || arg->ch > '9')
break;
if (arg->prec > (INT_MAX - ((int)arg->ch - '0')) / 10) {
PyErr_SetString(PyExc_ValueError,
"precision too big");
PyErr_Format(PyExc_ValueError,
"precision too big at position %zd",
arg->fmtstart);
return -1;
}
arg->prec = arg->prec*10 + (arg->ch - '0');
@ -589,8 +652,8 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
}
}
if (ctx->fmtcnt < 0) {
PyErr_SetString(PyExc_ValueError,
"incomplete format");
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd", arg->fmtstart);
return -1;
}
return 0;
@ -624,7 +687,7 @@ unicode_format_arg_format(struct unicode_formatter_t *ctx,
if (ctx->fmtcnt == 0)
ctx->writer.overallocate = 0;
v = unicode_format_getnextarg(ctx);
v = unicode_format_getnextarg(ctx, 1);
if (v == NULL)
return -1;
@ -660,7 +723,7 @@ unicode_format_arg_format(struct unicode_formatter_t *ctx,
case 'x':
case 'X':
{
int ret = mainformatlong(v, arg, p_str, writer);
int ret = mainformatlong(v, ctx, arg, p_str, writer);
if (ret != 0)
return ret;
arg->sign = 1;
@ -677,19 +740,19 @@ unicode_format_arg_format(struct unicode_formatter_t *ctx,
&& !(arg->flags & (F_SIGN | F_BLANK)))
{
/* Fast path */
if (formatfloat(v, arg, NULL, writer) == -1)
if (formatfloat(v, ctx, arg, NULL, writer) == -1)
return -1;
return 1;
}
arg->sign = 1;
if (formatfloat(v, arg, p_str, NULL) == -1)
if (formatfloat(v, ctx, arg, p_str, NULL) == -1)
return -1;
break;
case 'c':
{
Py_UCS4 ch = formatchar(v);
Py_UCS4 ch = formatchar(v, ctx, arg);
if (ch == (Py_UCS4) -1)
return -1;
if (arg->width == -1 && arg->prec == -1) {
@ -703,12 +766,38 @@ unicode_format_arg_format(struct unicode_formatter_t *ctx,
}
default:
PyErr_Format(PyExc_ValueError,
"unsupported format character '%c' (0x%x) "
"at index %zd",
(31<=arg->ch && arg->ch<=126) ? (char)arg->ch : '?',
(int)arg->ch,
ctx->fmtpos - 1);
if (arg->ch < 128 && Py_ISALPHA(arg->ch)) {
PyErr_Format(PyExc_ValueError,
"unsupported format %%%c at position %zd",
(int)arg->ch, arg->fmtstart);
}
else if (arg->ch == '\'') {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character \"'\" at position %zd",
arg->fmtstart,
ctx->fmtpos - 1);
}
else if (arg->ch >= 32 && arg->ch < 127) {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character '%c' at position %zd",
arg->fmtstart,
(int)arg->ch, ctx->fmtpos - 1);
}
else if (Py_UNICODE_ISPRINTABLE(arg->ch)) {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character '%c' (U+%04X) at position %zd",
arg->fmtstart,
(int)arg->ch, (int)arg->ch, ctx->fmtpos - 1);
}
else {
PyErr_Format(PyExc_ValueError,
"stray %% at position %zd or unexpected "
"format character U+%04X at position %zd",
arg->fmtstart, (int)arg->ch, ctx->fmtpos - 1);
}
return -1;
}
if (*p_str == NULL)
@ -892,29 +981,40 @@ unicode_format_arg(struct unicode_formatter_t *ctx)
arg.width = -1;
arg.prec = -1;
arg.sign = 0;
arg.fmtstart = ctx->fmtpos - 1;
arg.key = NULL;
str = NULL;
ret = unicode_format_arg_parse(ctx, &arg);
if (ret == -1)
return -1;
if (ret == -1) {
goto onError;
}
ret = unicode_format_arg_format(ctx, &arg, &str);
if (ret == -1)
return -1;
if (ret == -1) {
goto onError;
}
if (ret != 1) {
ret = unicode_format_arg_output(ctx, &arg, str);
Py_DECREF(str);
if (ret == -1)
return -1;
if (ret == -1) {
goto onError;
}
}
if (ctx->dict && (ctx->argidx < ctx->arglen)) {
// XXX: Never happens?
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during string formatting");
return -1;
goto onError;
}
Py_XDECREF(arg.key);
return 0;
onError:
Py_XDECREF(arg.key);
return -1;
}
@ -983,8 +1083,11 @@ PyUnicode_Format(PyObject *format, PyObject *args)
}
if (ctx.argidx < ctx.arglen && !ctx.dict) {
PyErr_SetString(PyExc_TypeError,
"not all arguments converted during string formatting");
PyErr_Format(PyExc_TypeError,
"not all arguments converted during string formatting "
"(required %zd, got %zd)",
ctx.arglen < 0 ? 0 : ctx.argidx,
ctx.arglen < 0 ? 1 : ctx.arglen);
goto onError;
}