[Python-checkins] cpython (2.7): Issue #11145: Fixed miscellaneous issues with C-style formatting of types
serhiy.storchaka
python-checkins at python.org
Thu Dec 1 03:27:42 EST 2016
https://hg.python.org/cpython/rev/adb296e4bcaa
changeset: 105401:adb296e4bcaa
branch: 2.7
user: Serhiy Storchaka <storchaka at gmail.com>
date: Thu Dec 01 10:27:11 2016 +0200
summary:
Issue #11145: Fixed miscellaneous issues with C-style formatting of types
with custom __oct__ and __hex__.
files:
Lib/test/test_format.py | 38 ++++++
Misc/NEWS | 3 +
Objects/stringobject.c | 154 +++++++++++++++------------
3 files changed, 126 insertions(+), 69 deletions(-)
diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py
--- a/Lib/test/test_format.py
+++ b/Lib/test/test_format.py
@@ -300,6 +300,44 @@
else:
raise TestFailed, '"%*d"%(maxsize, -127) should fail'
+ def test_invalid_special_methods(self):
+ tests = []
+ for f in 'sriduoxXfge':
+ tests.append(('%' + f, 1, TypeError))
+ tests.append(('%#' + f, 1, TypeError))
+ for r in ['', '-', 'L', '-L']:
+ for f in 'iduoxX':
+ tests.append(('%' + f, r, ValueError))
+ tests.append(('%#' + f, r, ValueError))
+ tests.append(('%o', 'abc', ValueError))
+ for r in ('abc', '0abc', '0x', '0xL'):
+ for f in 'xX':
+ tests.append(('%' + f, r, ValueError))
+ for r in ('0x', '0xL'):
+ for f in 'xX':
+ tests.append(('%#' + f, r, ValueError))
+
+ class X(long):
+ def __repr__(self):
+ return result
+ def __str__(self):
+ return result
+ def __oct__(self):
+ return result
+ def __hex__(self):
+ return result
+ def __float__(self):
+ return result
+ for fmt, result, exc in tests:
+ try:
+ fmt % X()
+ except exc:
+ pass
+ else:
+ self.fail('%s not raised for %r format of %r' %
+ (exc.__name__, fmt, result))
+
+
def test_main():
test_support.run_unittest(FormatTest)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
Core and Builtins
-----------------
+- Issue #11145: Fixed miscellaneous issues with C-style formatting of types
+ with custom __oct__ and __hex__.
+
- Issue #24469: Fixed memory leak caused by int subclasses without overridden
tp_free (e.g. C-inherited Cython classes).
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -4006,26 +4006,30 @@
_PyString_FormatLong(PyObject *val, int flags, int prec, int type,
char **pbuf, int *plen)
{
- PyObject *result = NULL;
+ PyObject *result = NULL, *r1;
+ const char *s;
char *buf;
Py_ssize_t i;
int sign; /* 1 if '-', else 0 */
int len; /* number of characters */
Py_ssize_t llen;
- int numdigits; /* len == numnondigits + numdigits */
- int numnondigits = 0;
+ int numdigits; /* len == numnondigits + skipped + numdigits */
+ int numnondigits, skipped, filled;
+ const char *method;
switch (type) {
case 'd':
case 'u':
+ method = "str";
result = Py_TYPE(val)->tp_str(val);
break;
case 'o':
+ method = "oct";
result = Py_TYPE(val)->tp_as_number->nb_oct(val);
break;
case 'x':
case 'X':
- numnondigits = 2;
+ method = "hex";
result = Py_TYPE(val)->tp_as_number->nb_hex(val);
break;
default:
@@ -4034,97 +4038,109 @@
if (!result)
return NULL;
- buf = PyString_AsString(result);
- if (!buf) {
+ if (PyString_AsStringAndSize(result, (char **)&s, &llen) < 0) {
Py_DECREF(result);
return NULL;
}
-
- /* To modify the string in-place, there can only be one reference. */
- if (Py_REFCNT(result) != 1) {
- PyErr_BadInternalCall();
- return NULL;
- }
- llen = PyString_Size(result);
if (llen > INT_MAX) {
PyErr_SetString(PyExc_ValueError, "string too large in _PyString_FormatLong");
+ Py_DECREF(result);
return NULL;
}
len = (int)llen;
- if (buf[len-1] == 'L') {
+ if (len > 0 && s[len-1] == 'L') {
--len;
- buf[len] = '\0';
+ if (len == 0)
+ goto error;
}
- sign = buf[0] == '-';
- numnondigits += sign;
- numdigits = len - numnondigits;
- assert(numdigits > 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) {
- case 'o':
- assert(buf[sign] == '0');
- /* If 0 is only digit, leave it alone. */
- if (numdigits > 1) {
- skipped = 1;
- --numdigits;
- }
- break;
- case 'x':
- case 'X':
- assert(buf[sign] == '0');
- assert(buf[sign + 1] == 'x');
+ sign = s[0] == '-';
+ numnondigits = sign;
+
+ /* Need to skip 0x, 0X or 0. */
+ skipped = 0;
+ switch (type) {
+ case 'o':
+ if (s[sign] != '0')
+ goto error;
+ /* If 0 is only digit, leave it alone. */
+ if ((flags & F_ALT) == 0 && len - sign > 1)
+ skipped = 1;
+ break;
+ case 'x':
+ case 'X':
+ if (s[sign] != '0' || (s[sign + 1] != 'x' && s[sign + 1] != 'X'))
+ goto error;
+ if ((flags & F_ALT) == 0)
skipped = 2;
- numnondigits -= 2;
- break;
- }
- if (skipped) {
- buf += skipped;
- len -= skipped;
- if (sign)
- buf[0] = '-';
- }
- assert(len == numnondigits + numdigits);
- assert(numdigits > 0);
+ else
+ numnondigits += 2;
+ break;
}
-
- /* Fill with leading zeroes to meet minimum width. */
- if (prec > numdigits) {
- PyObject *r1 = PyString_FromStringAndSize(NULL,
- numnondigits + prec);
- char *b1;
- if (!r1) {
- Py_DECREF(result);
+ numdigits = len - numnondigits - skipped;
+ if (numdigits <= 0)
+ goto error;
+
+ filled = prec - numdigits;
+ if (filled < 0)
+ filled = 0;
+ len = numnondigits + filled + numdigits;
+
+ /* To modify the string in-place, there can only be one reference. */
+ if (skipped >= filled &&
+ PyString_CheckExact(result) &&
+ Py_REFCNT(result) == 1 &&
+ !PyString_CHECK_INTERNED(result))
+ {
+ r1 = NULL;
+ buf = (char *)s + skipped - filled;
+ }
+ else {
+ r1 = result;
+ result = PyString_FromStringAndSize(NULL, len);
+ if (!result) {
+ Py_DECREF(r1);
return NULL;
}
- b1 = PyString_AS_STRING(r1);
- for (i = 0; i < numnondigits; ++i)
- *b1++ = *buf++;
- for (i = 0; i < prec - numdigits; i++)
- *b1++ = '0';
+ buf = PyString_AS_STRING(result);
+ }
+
+ for (i = numnondigits; --i >= 0;)
+ buf[i] = s[i];
+ buf += numnondigits;
+ s += numnondigits + skipped;
+ for (i = 0; i < filled; i++)
+ *buf++ = '0';
+ if (r1 == NULL) {
+ assert(buf == s);
+ buf += numdigits;
+ }
+ else {
for (i = 0; i < numdigits; i++)
- *b1++ = *buf++;
- *b1 = '\0';
- Py_DECREF(result);
- result = r1;
- buf = PyString_AS_STRING(result);
- len = numnondigits + prec;
+ *buf++ = *s++;
}
+ *buf = '\0';
+ buf -= len;
+ Py_XDECREF(r1);
/* Fix up case for hex conversions. */
if (type == 'X') {
/* Need to convert all lower case letters to upper case.
and need to convert 0x to 0X (and -0x to -0X). */
- for (i = 0; i < len; i++)
- if (buf[i] >= 'a' && buf[i] <= 'x')
+ for (i = 0; i < len; i++) {
+ if (buf[i] >= 'a' && buf[i] <= 'z')
buf[i] -= 'a'-'A';
+ }
}
*pbuf = buf;
*plen = len;
return result;
+
+error:
+ PyErr_Format(PyExc_ValueError,
+ "%%%c format: invalid result of __%s__ (type=%.200s)",
+ type, method, Py_TYPE(val)->tp_name);
+ Py_DECREF(result);
+ return NULL;
}
Py_LOCAL_INLINE(int)
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list