Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fff5325

Browse files
committed
Bug 415514 reported that e.g.
"%#x" % 0 blew up, at heart because C sprintf supplies a base marker if and only if the value is not 0. I then fixed that, by tolerating C's inconsistency when it does %#x, and taking away that *Python* produced 0x0 when formatting 0L (the "long" flavor of 0) under %#x itself. But after talking with Guido, we agreed it would be better to supply 0x for the short int case too, despite that it's inconsistent with C, because C is inconsistent with itself and with Python's hex(0) (plus, while "%#x" % 0 didn't work before, "%#x" % 0L *did*, and returned "0x0"). Similarly for %#X conversion.
1 parent bfb0cf8 commit fff5325

3 files changed

Lines changed: 47 additions & 42 deletions

File tree

Lib/test/test_format.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ def testboth(formatstr, *args):
176176
testboth("%o", 0L, "0")
177177
testboth("%d", 0, "0")
178178
testboth("%d", 0L, "0")
179-
testboth("%#x", 0, "0")
180-
testboth("%#x", 0L, "0")
181-
testboth("%#X", 0, "0")
182-
testboth("%#X", 0L, "0")
179+
testboth("%#x", 0, "0x0")
180+
testboth("%#x", 0L, "0x0")
181+
testboth("%#X", 0, "0X0")
182+
testboth("%#X", 0L, "0X0")
183183

184184
testboth("%x", 0x42, "42")
185185
# testboth("%x", -0x42, "ffffffbe") # Alas, that's specific to 32-bit machines

Objects/stringobject.c

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,16 +2575,8 @@ _PyString_FormatLong(PyObject *val, int flags, int prec, int type,
25752575
numdigits = len - numnondigits;
25762576
assert(numdigits > 0);
25772577

2578-
/* Get rid of base marker unless F_ALT. Even if F_ALT, leading 0x
2579-
* must be stripped if the *value* is 0.
2580-
*/
2581-
if ((flags & F_ALT) == 0 ||
2582-
((flags & F_ALT) &&
2583-
(type == 'x' || type == 'X') &&
2584-
numdigits == 1 &&
2585-
!sign &&
2586-
buf[2] == '0'
2587-
)) {
2578+
/* Get rid of base marker unless F_ALT */
2579+
if ((flags & F_ALT) == 0) {
25882580
/* Need to skip 0x, 0X or 0. */
25892581
int skipped = 0;
25902582
switch (type) {
@@ -2678,6 +2670,16 @@ formatint(char *buf, size_t buflen, int flags,
26782670
return -1;
26792671
}
26802672
sprintf(buf, fmt, x);
2673+
/* When converting 0 under %#x or %#X, C leaves off the base marker,
2674+
* but we want it (for consistency with other %#x conversions, and
2675+
* for consistency with Python's hex() function).
2676+
*/
2677+
if (x == 0 && (flags & F_ALT) && (type == 'x' || type == 'X')) {
2678+
assert(buf[1] != type); /* else this C *is* adding 0x/0X */
2679+
memmove(buf+2, buf, strlen(buf) + 1);
2680+
buf[0] = '0';
2681+
buf[1] = (char)type;
2682+
}
26812683
return strlen(buf);
26822684
}
26832685

@@ -3023,21 +3025,17 @@ PyString_Format(PyObject *format, PyObject *args)
30233025
width--;
30243026
}
30253027
if ((flags & F_ALT) && (c == 'x' || c == 'X')) {
3026-
/* There's a base marker ("0x" or "0X") if and
3027-
* only if the value is non-zero.
3028-
*/
30293028
assert(pbuf[0] == '0');
3030-
if (pbuf[1] == c) {
3031-
if (fill != ' ') {
3032-
*res++ = *pbuf++;
3033-
*res++ = *pbuf++;
3034-
}
3035-
rescnt -= 2;
3036-
width -= 2;
3037-
if (width < 0)
3038-
width = 0;
3039-
len -= 2;
3029+
assert(pbuf[1] == c);
3030+
if (fill != ' ') {
3031+
*res++ = *pbuf++;
3032+
*res++ = *pbuf++;
30403033
}
3034+
rescnt -= 2;
3035+
width -= 2;
3036+
if (width < 0)
3037+
width = 0;
3038+
len -= 2;
30413039
}
30423040
if (width > len && !(flags & F_LJUST)) {
30433041
do {
@@ -3049,8 +3047,9 @@ PyString_Format(PyObject *format, PyObject *args)
30493047
if (sign)
30503048
*res++ = sign;
30513049
if ((flags & F_ALT) &&
3052-
(c == 'x' || c == 'X') &&
3053-
pbuf[1] == c) {
3050+
(c == 'x' || c == 'X')) {
3051+
assert(pbuf[0] == '0');
3052+
assert(pbuf[1] == c);
30543053
*res++ = *pbuf++;
30553054
*res++ = *pbuf++;
30563055
}

Objects/unicodeobject.c

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4683,7 +4683,14 @@ formatint(Py_UNICODE *buf,
46834683
"formatted integer is too long (precision too long?)");
46844684
return -1;
46854685
}
4686-
sprintf(fmt, "%%%s.%dl%c", (flags & F_ALT) ? "#" : "", prec, type);
4686+
/* When converting 0 under %#x or %#X, C leaves off the base marker,
4687+
* but we want it (for consistency with other %#x conversions, and
4688+
* for consistency with Python's hex() function).
4689+
*/
4690+
if (x == 0 && (flags & F_ALT) && (type == 'x' || type == 'X'))
4691+
sprintf(fmt, "0%c%%%s.%dl%c", type, "#", prec, type);
4692+
else
4693+
sprintf(fmt, "%%%s.%dl%c", (flags & F_ALT) ? "#" : "", prec, type);
46874694
return usprintf(buf, fmt, x);
46884695
}
46894696

@@ -5081,17 +5088,16 @@ PyObject *PyUnicode_Format(PyObject *format,
50815088
}
50825089
if ((flags & F_ALT) && (c == 'x' || c == 'X')) {
50835090
assert(pbuf[0] == '0');
5084-
if (pbuf[1] == c) {
5085-
if (fill != ' ') {
5086-
*res++ = *pbuf++;
5087-
*res++ = *pbuf++;
5088-
}
5089-
rescnt -= 2;
5090-
width -= 2;
5091-
if (width < 0)
5092-
width = 0;
5093-
len -= 2;
5091+
assert(pbuf[1] == c);
5092+
if (fill != ' ') {
5093+
*res++ = *pbuf++;
5094+
*res++ = *pbuf++;
50945095
}
5096+
rescnt -= 2;
5097+
width -= 2;
5098+
if (width < 0)
5099+
width = 0;
5100+
len -= 2;
50955101
}
50965102
if (width > len && !(flags & F_LJUST)) {
50975103
do {
@@ -5102,9 +5108,9 @@ PyObject *PyUnicode_Format(PyObject *format,
51025108
if (fill == ' ') {
51035109
if (sign)
51045110
*res++ = sign;
5105-
if ((flags & F_ALT) && (c == 'x' || c == 'X') &&
5106-
pbuf[1] == c) {
5111+
if ((flags & F_ALT) && (c == 'x' || c == 'X')) {
51075112
assert(pbuf[0] == '0');
5113+
assert(pbuf[1] == c);
51085114
*res++ = *pbuf++;
51095115
*res++ = *pbuf++;
51105116
}

0 commit comments

Comments
 (0)