From 2c7b5a9d0d6587a38f24da1f96f581bad0d3ded6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 30 Mar 2015 09:19:08 +0300 Subject: [PATCH] Issue #23466: %c, %o, %x, and %X in bytes formatting now raise TypeError on non-integer input. --- Lib/test/test_format.py | 19 +++++++++- Misc/NEWS | 3 ++ Objects/bytesobject.c | 81 ++++++++++++++++++++++++++++------------- 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index e1ea33fbbde..5a2a357e081 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -272,9 +272,18 @@ class FormatTest(unittest.TestCase): #test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError, # "unsupported format character '?' (0x3000) at index 5") test_exc('%d', '1', TypeError, "%d format: a number is required, not str") + test_exc('%x', '1', TypeError, "%x format: a number is required, not str") + test_exc('%x', 3.14, TypeError, "%x format: an integer is required, not float") test_exc('%g', '1', TypeError, "a float is required") test_exc('no format', '1', TypeError, "not all arguments converted during string formatting") + test_exc('%c', -1, OverflowError, "%c arg 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 int or char") + test_exc('%c', 'ab', TypeError, "%c requires int or char") + test_exc('%c', b'x', TypeError, "%c requires int or char") if maxsize == 2**31-1: # crashes 2.2.1 and earlier: @@ -339,6 +348,8 @@ class FormatTest(unittest.TestCase): "%d format: a number is required, not str") test_exc(b'%d', b'1', TypeError, "%d format: a number is required, not bytes") + test_exc(b'%x', 3.14, TypeError, + "%x format: an integer is required, not float") 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'no format', 7, TypeError, @@ -347,11 +358,17 @@ class FormatTest(unittest.TestCase): "not all arguments converted during bytes formatting") test_exc(b'no format', bytearray(b'1'), TypeError, "not all arguments converted during bytes formatting") + test_exc(b"%c", -1, TypeError, + "%c requires an integer in range(256) or a single byte") test_exc(b"%c", 256, TypeError, "%c requires an integer in range(256) or a single byte") + test_exc(b"%c", 2**128, TypeError, + "%c requires an integer in range(256) or a single byte") test_exc(b"%c", b"Za", TypeError, "%c requires an integer in range(256) or a single byte") - test_exc(b"%c", "Yb", TypeError, + test_exc(b"%c", "Y", TypeError, + "%c requires an integer in range(256) or a single byte") + test_exc(b"%c", 3.14, TypeError, "%c requires an integer in range(256) or a single byte") test_exc(b"%b", "Xc", TypeError, "%b requires bytes, or an object that implements __bytes__, not 'str'") diff --git a/Misc/NEWS b/Misc/NEWS index 0aae52cbd40..36415588b75 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -36,6 +36,9 @@ Release date: 2015-03-28 Core and Builtins ----------------- +- Issue #23466: %c, %o, %x, and %X in bytes formatting now raise TypeError on + non-integer input. + - Issue #23573: Increased performance of string search operations (str.find, str.index, str.count, the in operator, str.split, str.partition) with arguments of different kinds (UCS1, UCS2, UCS4). diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 4d6b3e4abe1..5a2d41c5a8f 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -433,7 +433,41 @@ formatfloat(PyObject *v, int flags, int prec, int type) return result; } -Py_LOCAL_INLINE(int) +static PyObject * +formatlong(PyObject *v, int flags, int prec, int type) +{ + PyObject *result, *iobj; + if (type == 'i') + type = 'd'; + if (PyLong_Check(v)) + return _PyUnicode_FormatLong(v, flags & F_ALT, prec, type); + if (PyNumber_Check(v)) { + /* make sure number is a type of integer for o, x, and X */ + if (type == 'o' || type == 'x' || type == 'X') + iobj = PyNumber_Index(v); + else + iobj = PyNumber_Long(v); + if (iobj == NULL) { + if (!PyErr_ExceptionMatches(PyExc_TypeError)) + return NULL; + } + else if (!PyLong_Check(iobj)) + Py_CLEAR(iobj); + if (iobj != NULL) { + result = _PyUnicode_FormatLong(iobj, flags & F_ALT, prec, type); + Py_DECREF(iobj); + return result; + } + } + PyErr_Format(PyExc_TypeError, + "%%%c format: %s is required, not %.200s", type, + (type == 'o' || type == 'x' || type == 'X') ? "an integer" + : "a number", + Py_TYPE(v)->tp_name); + return NULL; +} + +static int byte_converter(PyObject *arg, char *p) { if (PyBytes_Check(arg) && PyBytes_Size(arg) == 1) { @@ -445,12 +479,29 @@ byte_converter(PyObject *arg, char *p) return 1; } else { - long ival = PyLong_AsLong(arg); - if (0 <= ival && ival <= 255) { + PyObject *iobj; + long ival; + int overflow; + /* make sure number is a type of integer */ + if (PyLong_Check(arg)) { + ival = PyLong_AsLongAndOverflow(arg, &overflow); + } + else { + iobj = PyNumber_Index(arg); + if (iobj == NULL) { + if (!PyErr_ExceptionMatches(PyExc_TypeError)) + return 0; + goto onError; + } + ival = PyLong_AsLongAndOverflow(iobj, &overflow); + Py_DECREF(iobj); + } + if (!overflow && 0 <= ival && ival <= 255) { *p = (char)ival; return 1; } } + onError: PyErr_SetString(PyExc_TypeError, "%c requires an integer in range(256) or a single byte"); return 0; @@ -561,7 +612,6 @@ _PyBytes_Format(PyObject *format, PyObject *args) int prec = -1; int c = '\0'; int fill; - PyObject *iobj; PyObject *v = NULL; PyObject *temp = NULL; const char *pbuf = NULL; @@ -747,28 +797,7 @@ _PyBytes_Format(PyObject *format, PyObject *args) case 'o': case 'x': case 'X': - if (c == 'i') - c = 'd'; - iobj = NULL; - if (PyNumber_Check(v)) { - if ((PyLong_Check(v))) { - iobj = v; - Py_INCREF(iobj); - } - else { - iobj = PyNumber_Long(v); - if (iobj != NULL && !PyLong_Check(iobj)) - Py_CLEAR(iobj); - } - } - if (iobj == NULL) { - PyErr_Format(PyExc_TypeError, - "%%%c format: a number is required, " - "not %.200s", c, Py_TYPE(v)->tp_name); - goto error; - } - temp = _PyUnicode_FormatLong(iobj, flags & F_ALT, prec, c); - Py_DECREF(iobj); + temp = formatlong(v, flags, prec, c); if (!temp) goto error; assert(PyUnicode_IS_ASCII(temp));