]> granicus.if.org Git - python/commitdiff
Issue19995: %o, %x, %X now only accept ints
authorEthan Furman <ethan@stoneleaf.us>
Sun, 5 Jan 2014 14:50:30 +0000 (06:50 -0800)
committerEthan Furman <ethan@stoneleaf.us>
Sun, 5 Jan 2014 14:50:30 +0000 (06:50 -0800)
Doc/reference/datamodel.rst
Lib/tarfile.py
Lib/test/test_format.py
Lib/test/test_unicode.py
Misc/NEWS
Objects/unicodeobject.c

index 26d93a6baa2c9fa8049a50703bc9a8dadbb21c5e..4f19b37cd26cf27814b6de1151be45f0350b0c4b 100644 (file)
@@ -2080,9 +2080,17 @@ left undefined.
 
 .. method:: object.__index__(self)
 
-   Called to implement :func:`operator.index`.  Also called whenever Python needs
-   an integer object (such as in slicing, or in the built-in :func:`bin`,
-   :func:`hex` and :func:`oct` functions). Must return an integer.
+   Called to implement :func:`operator.index`, and whenever Python needs to
+   losslessly convert the numeric object to an integer object (such as in
+   slicing, or in the built-in :func:`bin`, :func:`hex` and :func:`oct`
+   functions). Presence of this method indicates that the numeric object is
+   an integer type.  Must return an integer.
+
+   .. note::
+
+      When :meth:`__index__` is defined, :meth:`__int__` should also be defined,
+      and both shuld return the same value, in order to have a coherent integer
+      type class.
 
 
 .. _context-managers:
index f4df6c7b0ac4ccbc3443e52fc0907026d71ef217..aec7009feddfb833ea613015e5d7912faff559bc 100644 (file)
@@ -196,7 +196,7 @@ def itn(n, digits=8, format=DEFAULT_FORMAT):
     # A 0o200 byte indicates a positive number, a 0o377 byte a negative
     # number.
     if 0 <= n < 8 ** (digits - 1):
-        s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL
+        s = bytes("%0*o" % (digits - 1, int(n)), "ascii") + NUL
     elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1):
         if n >= 0:
             s = bytearray([0o200])
index 29330f917132d9ae0aa99b852812fa77f7fa628b..4b1fdf9171654730f2a455988c5ee32fd36200e4 100644 (file)
@@ -142,7 +142,6 @@ class FormatTest(unittest.TestCase):
         testformat("%#+027.23X", big, "+0X0001234567890ABCDEF12345")
         # same, except no 0 flag
         testformat("%#+27.23X", big, " +0X001234567890ABCDEF12345")
-        testformat("%x", float(big), "123456_______________", 6)
         big = 0o12345670123456701234567012345670  # 32 octal digits
         testformat("%o", big, "12345670123456701234567012345670")
         testformat("%o", -big, "-12345670123456701234567012345670")
@@ -182,7 +181,6 @@ class FormatTest(unittest.TestCase):
         testformat("%034.33o", big, "0012345670123456701234567012345670")
         # base marker shouldn't change that
         testformat("%0#34.33o", big, "0o012345670123456701234567012345670")
-        testformat("%o", float(big), "123456__________________________", 6)
         # Some small ints, in both Python int and flavors).
         testformat("%d", 42, "42")
         testformat("%d", -42, "-42")
@@ -193,7 +191,6 @@ class FormatTest(unittest.TestCase):
         testformat("%#x", 1, "0x1")
         testformat("%#X", 1, "0X1")
         testformat("%#X", 1, "0X1")
-        testformat("%#x", 1.0, "0x1")
         testformat("%#o", 1, "0o1")
         testformat("%#o", 1, "0o1")
         testformat("%#o", 0, "0o0")
@@ -210,12 +207,10 @@ class FormatTest(unittest.TestCase):
         testformat("%x", -0x42, "-42")
         testformat("%x", 0x42, "42")
         testformat("%x", -0x42, "-42")
-        testformat("%x", float(0x42), "42")
         testformat("%o", 0o42, "42")
         testformat("%o", -0o42, "-42")
         testformat("%o", 0o42, "42")
         testformat("%o", -0o42, "-42")
-        testformat("%o", float(0o42), "42")
         testformat("%r", "\u0378", "'\\u0378'")  # non printable
         testformat("%a", "\u0378", "'\\u0378'")  # non printable
         testformat("%r", "\u0374", "'\u0374'")   # printable
index c0d5dae9638a56a5404b7a03767be0c0b525bb15..f64a9622e1eb14ab7db0d304e1b5ddec019f22e1 100644 (file)
@@ -1126,6 +1126,35 @@ class UnicodeTest(string_tests.CommonTest,
         self.assertEqual('%.1s' % "a\xe9\u20ac", 'a')
         self.assertEqual('%.2s' % "a\xe9\u20ac", 'a\xe9')
 
+        #issue 19995
+        class PsuedoInt:
+            def __init__(self, value):
+                self.value = int(value)
+            def __int__(self):
+                return self.value
+            def __index__(self):
+                return self.value
+        class PsuedoFloat:
+            def __init__(self, value):
+                self.value = float(value)
+            def __int__(self):
+                return int(self.value)
+        pi = PsuedoFloat(3.1415)
+        letter_m = PsuedoInt(109)
+        self.assertEquals('%x' % 42, '2a')
+        self.assertEquals('%X' % 15, 'F')
+        self.assertEquals('%o' % 9, '11')
+        self.assertEquals('%c' % 109, 'm')
+        self.assertEquals('%x' % letter_m, '6d')
+        self.assertEquals('%X' % letter_m, '6D')
+        self.assertEquals('%o' % letter_m, '155')
+        self.assertEquals('%c' % letter_m, 'm')
+        self.assertRaises(TypeError, '%x'.__mod__, pi)
+        self.assertRaises(TypeError, '%x'.__mod__, 3.14)
+        self.assertRaises(TypeError, '%X'.__mod__, 2.11)
+        self.assertRaises(TypeError, '%o'.__mod__, 1.79)
+        self.assertRaises(TypeError, '%c'.__mod__, pi)
+
     def test_formatting_with_enum(self):
         # issue18780
         import enum
index d512d5d39cc5ab082d36bc6ef2a1d55abb328dd3..2857633e91d33640cae286dd3288989b7b6a2842 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,10 @@ Core and Builtins
 - Issue #19969: PyBytes_FromFormatV() now raises an OverflowError if "%c"
   argument is not in range [0; 255].
 
+- Issue #19995: %c, %o, %x, and %X now raise TypeError on non-integer input;
+  reworded docs to clarify that an integer type should define both __int__
+  and __index__.
+
 - Issue #19787: PyThread_set_key_value() now always set the value. In Python
   3.3, the function did nothing if the key already exists (if the current value
   is a non-NULL pointer).
index cdbaa0c34198c52e25540ced4a382ab9f4d4c640..fc6f0d05a09dbf26e8d6ba008401e7d287ab72d4 100644 (file)
@@ -13988,7 +13988,7 @@ formatlong(PyObject *val, struct unicode_format_arg_t *arg)
     return result;
 }
 
-/* Format an integer.
+/* Format an integer or a float as an integer.
  * Return 1 if the number has been formatted into the writer,
  *        0 if the number has been formatted into *p_output
  *       -1 and raise an exception on error */
@@ -14005,11 +14005,19 @@ mainformatlong(PyObject *v,
         goto wrongtype;
 
     if (!PyLong_Check(v)) {
-        iobj = PyNumber_Long(v);
-        if (iobj == NULL) {
-            if (PyErr_ExceptionMatches(PyExc_TypeError))
-                goto wrongtype;
-            return -1;
+        if (type == 'o' || type == 'x' || type == 'X') {
+            iobj = PyNumber_Index(v);
+            if (iobj == NULL) {
+                return -1;
+            }
+        }
+        else {
+            iobj = PyNumber_Long(v);
+            if (iobj == NULL ) {
+                if (PyErr_ExceptionMatches(PyExc_TypeError))
+                    goto wrongtype;
+                return -1;
+            }
         }
         assert(PyLong_Check(iobj));
     }
@@ -14079,8 +14087,18 @@ formatchar(PyObject *v)
         goto onError;
     }
     else {
-        /* Integer input truncated to a character */
+        PyObject *iobj;
         long x;
+        /* make sure number is a type of integer */
+        if (!PyLong_Check(v)) {
+            iobj = PyNumber_Index(v);
+            if (iobj == NULL) {
+                goto onError;
+            }
+            v = iobj;
+            Py_DECREF(iobj);
+        }
+        /* Integer input truncated to a character */
         x = PyLong_AsLong(v);
         if (x == -1 && PyErr_Occurred())
             goto onError;
@@ -14282,7 +14300,8 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
 /* Format one argument. Supported conversion specifiers:
 
    - "s", "r", "a": any type
-   - "i", "d", "u", "o", "x", "X": int
+   - "i", "d", "u": int or float
+   - "o", "x", "X": int
    - "e", "E", "f", "F", "g", "G": float
    - "c": int or str (1 character)