Commit 8883ffa5 authored by Ethan Furman's avatar Ethan Furman

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

parent 0addb2ed
...@@ -2080,9 +2080,17 @@ left undefined. ...@@ -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:
......
...@@ -196,7 +196,7 @@ def itn(n, digits=8, format=DEFAULT_FORMAT): ...@@ -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])
......
...@@ -142,7 +142,6 @@ class FormatTest(unittest.TestCase): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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
......
...@@ -1126,6 +1126,35 @@ class UnicodeTest(string_tests.CommonTest, ...@@ -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
......
...@@ -18,6 +18,10 @@ Core and Builtins ...@@ -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).
......
...@@ -13988,7 +13988,7 @@ formatlong(PyObject *val, struct unicode_format_arg_t *arg) ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment