Issue19995: %o, %x, %X now only accept ints

This commit is contained in:
Ethan Furman 2014-01-05 06:50:30 -08:00
parent 6d2ea21337
commit df3ed242c0
6 changed files with 72 additions and 17 deletions

View file

@ -2080,9 +2080,17 @@ left undefined.
.. method:: object.__index__(self) .. method:: object.__index__(self)
Called to implement :func:`operator.index`. Also called whenever Python needs Called to implement :func:`operator.index`, and whenever Python needs to
an integer object (such as in slicing, or in the built-in :func:`bin`, losslessly convert the numeric object to an integer object (such as in
:func:`hex` and :func:`oct` functions). Must return an integer. 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: .. _context-managers:

View 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 # A 0o200 byte indicates a positive number, a 0o377 byte a negative
# number. # number.
if 0 <= n < 8 ** (digits - 1): 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): elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1):
if n >= 0: if n >= 0:
s = bytearray([0o200]) s = bytearray([0o200])

View file

@ -142,7 +142,6 @@ class FormatTest(unittest.TestCase):
testformat("%#+027.23X", big, "+0X0001234567890ABCDEF12345") testformat("%#+027.23X", big, "+0X0001234567890ABCDEF12345")
# same, except no 0 flag # same, except no 0 flag
testformat("%#+27.23X", big, " +0X001234567890ABCDEF12345") testformat("%#+27.23X", big, " +0X001234567890ABCDEF12345")
testformat("%x", float(big), "123456_______________", 6)
big = 0o12345670123456701234567012345670 # 32 octal digits big = 0o12345670123456701234567012345670 # 32 octal digits
testformat("%o", big, "12345670123456701234567012345670") testformat("%o", big, "12345670123456701234567012345670")
testformat("%o", -big, "-12345670123456701234567012345670") testformat("%o", -big, "-12345670123456701234567012345670")
@ -182,7 +181,6 @@ class FormatTest(unittest.TestCase):
testformat("%034.33o", big, "0012345670123456701234567012345670") testformat("%034.33o", big, "0012345670123456701234567012345670")
# base marker shouldn't change that # base marker shouldn't change that
testformat("%0#34.33o", big, "0o012345670123456701234567012345670") testformat("%0#34.33o", big, "0o012345670123456701234567012345670")
testformat("%o", float(big), "123456__________________________", 6)
# Some small ints, in both Python int and flavors). # Some small ints, in both Python int and flavors).
testformat("%d", 42, "42") testformat("%d", 42, "42")
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, "0X1")
testformat("%#X", 1, "0X1") testformat("%#X", 1, "0X1")
testformat("%#x", 1.0, "0x1")
testformat("%#o", 1, "0o1") testformat("%#o", 1, "0o1")
testformat("%#o", 1, "0o1") testformat("%#o", 1, "0o1")
testformat("%#o", 0, "0o0") 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", 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", 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("%r", "\u0378", "'\\u0378'") # non printable
testformat("%a", "\u0378", "'\\u0378'") # non printable testformat("%a", "\u0378", "'\\u0378'") # non printable
testformat("%r", "\u0374", "'\u0374'") # printable testformat("%r", "\u0374", "'\u0374'") # printable

View file

@ -1126,6 +1126,35 @@ class UnicodeTest(string_tests.CommonTest,
self.assertEqual('%.1s' % "a\xe9\u20ac", 'a') self.assertEqual('%.1s' % "a\xe9\u20ac", 'a')
self.assertEqual('%.2s' % "a\xe9\u20ac", 'a\xe9') 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): def test_formatting_with_enum(self):
# issue18780 # issue18780
import enum import enum

View file

@ -18,6 +18,10 @@ Core and Builtins
- Issue #19969: PyBytes_FromFormatV() now raises an OverflowError if "%c" - Issue #19969: PyBytes_FromFormatV() now raises an OverflowError if "%c"
argument is not in range [0; 255]. 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 - 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 3.3, the function did nothing if the key already exists (if the current value
is a non-NULL pointer). is a non-NULL pointer).

View file

@ -13988,7 +13988,7 @@ formatlong(PyObject *val, struct unicode_format_arg_t *arg)
return result; 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, * Return 1 if the number has been formatted into the writer,
* 0 if the number has been formatted into *p_output * 0 if the number has been formatted into *p_output
* -1 and raise an exception on error */ * -1 and raise an exception on error */
@ -14005,12 +14005,20 @@ mainformatlong(PyObject *v,
goto wrongtype; goto wrongtype;
if (!PyLong_Check(v)) { if (!PyLong_Check(v)) {
iobj = PyNumber_Long(v); if (type == 'o' || type == 'x' || type == 'X') {
iobj = PyNumber_Index(v);
if (iobj == NULL) { if (iobj == NULL) {
return -1;
}
}
else {
iobj = PyNumber_Long(v);
if (iobj == NULL ) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) if (PyErr_ExceptionMatches(PyExc_TypeError))
goto wrongtype; goto wrongtype;
return -1; return -1;
} }
}
assert(PyLong_Check(iobj)); assert(PyLong_Check(iobj));
} }
else { else {
@ -14079,8 +14087,18 @@ formatchar(PyObject *v)
goto onError; goto onError;
} }
else { else {
/* Integer input truncated to a character */ PyObject *iobj;
long x; 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); x = PyLong_AsLong(v);
if (x == -1 && PyErr_Occurred()) if (x == -1 && PyErr_Occurred())
goto onError; goto onError;
@ -14282,7 +14300,8 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
/* Format one argument. Supported conversion specifiers: /* Format one argument. Supported conversion specifiers:
- "s", "r", "a": any type - "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 - "e", "E", "f", "F", "g", "G": float
- "c": int or str (1 character) - "c": int or str (1 character)