]> granicus.if.org Git - python/commitdiff
Issue #23466: %c, %o, %x, and %X in bytes formatting now raise TypeError on
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 30 Mar 2015 06:19:08 +0000 (09:19 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Mon, 30 Mar 2015 06:19:08 +0000 (09:19 +0300)
non-integer input.

Lib/test/test_format.py
Misc/NEWS
Objects/bytesobject.c

index e1ea33fbbde348f9664f54a54fa782b6e535d1c6..5a2a357e081c6af1efd5a26df7e96676d949859f 100644 (file)
@@ -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'")
index 0aae52cbd4046c490d6644b8b0c9df1b59559d60..36415588b75cb21886d92e6a8b3d4c55c9d7cef0 100644 (file)
--- 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).
index 4d6b3e4abe150d76acf203efbee82d5c9a209c36..5a2d41c5a8f5ea2e6b0f26169610f0a2e993c371 100644 (file)
@@ -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));