From fff53250789c3879e5f63d4dde80d17e0b9c4dbb Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 12 Apr 2001 18:38:48 +0000 Subject: 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. --- Lib/test/test_format.py | 8 ++++---- Objects/stringobject.c | 49 ++++++++++++++++++++++++------------------------- Objects/unicodeobject.c | 32 +++++++++++++++++++------------- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index ce5d5f2..c74db0f 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -176,10 +176,10 @@ testboth("%o", 0, "0") testboth("%o", 0L, "0") testboth("%d", 0, "0") testboth("%d", 0L, "0") -testboth("%#x", 0, "0") -testboth("%#x", 0L, "0") -testboth("%#X", 0, "0") -testboth("%#X", 0L, "0") +testboth("%#x", 0, "0x0") +testboth("%#x", 0L, "0x0") +testboth("%#X", 0, "0X0") +testboth("%#X", 0L, "0X0") testboth("%x", 0x42, "42") # testboth("%x", -0x42, "ffffffbe") # Alas, that's specific to 32-bit machines diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 8e11536..1701b2f 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -2575,16 +2575,8 @@ _PyString_FormatLong(PyObject *val, int flags, int prec, int type, numdigits = len - numnondigits; assert(numdigits > 0); - /* Get rid of base marker unless F_ALT. Even if F_ALT, leading 0x - * must be stripped if the *value* is 0. - */ - if ((flags & F_ALT) == 0 || - ((flags & F_ALT) && - (type == 'x' || type == 'X') && - numdigits == 1 && - !sign && - buf[2] == '0' - )) { + /* Get rid of base marker unless F_ALT */ + if ((flags & F_ALT) == 0) { /* Need to skip 0x, 0X or 0. */ int skipped = 0; switch (type) { @@ -2678,6 +2670,16 @@ formatint(char *buf, size_t buflen, int flags, return -1; } sprintf(buf, fmt, x); + /* When converting 0 under %#x or %#X, C leaves off the base marker, + * but we want it (for consistency with other %#x conversions, and + * for consistency with Python's hex() function). + */ + if (x == 0 && (flags & F_ALT) && (type == 'x' || type == 'X')) { + assert(buf[1] != type); /* else this C *is* adding 0x/0X */ + memmove(buf+2, buf, strlen(buf) + 1); + buf[0] = '0'; + buf[1] = (char)type; + } return strlen(buf); } @@ -3023,21 +3025,17 @@ PyString_Format(PyObject *format, PyObject *args) width--; } if ((flags & F_ALT) && (c == 'x' || c == 'X')) { - /* There's a base marker ("0x" or "0X") if and - * only if the value is non-zero. - */ assert(pbuf[0] == '0'); - if (pbuf[1] == c) { - if (fill != ' ') { - *res++ = *pbuf++; - *res++ = *pbuf++; - } - rescnt -= 2; - width -= 2; - if (width < 0) - width = 0; - len -= 2; + assert(pbuf[1] == c); + if (fill != ' ') { + *res++ = *pbuf++; + *res++ = *pbuf++; } + rescnt -= 2; + width -= 2; + if (width < 0) + width = 0; + len -= 2; } if (width > len && !(flags & F_LJUST)) { do { @@ -3049,8 +3047,9 @@ PyString_Format(PyObject *format, PyObject *args) if (sign) *res++ = sign; if ((flags & F_ALT) && - (c == 'x' || c == 'X') && - pbuf[1] == c) { + (c == 'x' || c == 'X')) { + assert(pbuf[0] == '0'); + assert(pbuf[1] == c); *res++ = *pbuf++; *res++ = *pbuf++; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index aecc261..b623c20 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -4683,7 +4683,14 @@ formatint(Py_UNICODE *buf, "formatted integer is too long (precision too long?)"); return -1; } - sprintf(fmt, "%%%s.%dl%c", (flags & F_ALT) ? "#" : "", prec, type); + /* When converting 0 under %#x or %#X, C leaves off the base marker, + * but we want it (for consistency with other %#x conversions, and + * for consistency with Python's hex() function). + */ + if (x == 0 && (flags & F_ALT) && (type == 'x' || type == 'X')) + sprintf(fmt, "0%c%%%s.%dl%c", type, "#", prec, type); + else + sprintf(fmt, "%%%s.%dl%c", (flags & F_ALT) ? "#" : "", prec, type); return usprintf(buf, fmt, x); } @@ -5081,17 +5088,16 @@ PyObject *PyUnicode_Format(PyObject *format, } if ((flags & F_ALT) && (c == 'x' || c == 'X')) { assert(pbuf[0] == '0'); - if (pbuf[1] == c) { - if (fill != ' ') { - *res++ = *pbuf++; - *res++ = *pbuf++; - } - rescnt -= 2; - width -= 2; - if (width < 0) - width = 0; - len -= 2; + assert(pbuf[1] == c); + if (fill != ' ') { + *res++ = *pbuf++; + *res++ = *pbuf++; } + rescnt -= 2; + width -= 2; + if (width < 0) + width = 0; + len -= 2; } if (width > len && !(flags & F_LJUST)) { do { @@ -5102,9 +5108,9 @@ PyObject *PyUnicode_Format(PyObject *format, if (fill == ' ') { if (sign) *res++ = sign; - if ((flags & F_ALT) && (c == 'x' || c == 'X') && - pbuf[1] == c) { + if ((flags & F_ALT) && (c == 'x' || c == 'X')) { assert(pbuf[0] == '0'); + assert(pbuf[1] == c); *res++ = *pbuf++; *res++ = *pbuf++; } -- cgit v0.12