diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index cefdcacfec6..4e2b37eca00 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -1033,7 +1033,14 @@ class MixinStrUnicodeUserStringTest: # unicode raises ValueError, str raises OverflowError self.checkraises((ValueError, OverflowError), '%c', '__mod__', ordinal) + longvalue = sys.maxint + 10L + slongvalue = str(longvalue) + if slongvalue[-1] in ("L","l"): slongvalue = slongvalue[:-1] self.checkequal(' 42', '%3ld', '__mod__', 42) + self.checkequal('42', '%d', '__mod__', 42L) + self.checkequal('42', '%d', '__mod__', 42.0) + self.checkequal(slongvalue, '%d', '__mod__', longvalue) + self.checkcall('%d', '__mod__', float(longvalue)) self.checkequal('0042.00', '%07.2f', '__mod__', 42) self.checkequal('0042.00', '%07.2F', '__mod__', 42) @@ -1043,6 +1050,8 @@ class MixinStrUnicodeUserStringTest: self.checkraises(TypeError, '%c', '__mod__', (None,)) self.checkraises(ValueError, '%(foo', '__mod__', {}) self.checkraises(TypeError, '%(foo)s %(bar)s', '__mod__', ('foo', 42)) + self.checkraises(TypeError, '%d', '__mod__', "42") # not numeric + self.checkraises(TypeError, '%d', '__mod__', (42+0j)) # no int/long conversion provided # argument names with properly nested brackets are supported self.checkequal('bar', '%((foo))s', '__mod__', {'(foo)': 'bar'}) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index c4cfa119354..76b9fd43f20 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -11,7 +11,7 @@ maxsize = MAX_Py_ssize_t overflowok = 1 overflowrequired = 0 -def testformat(formatstr, args, output=None): +def testformat(formatstr, args, output=None, limit=None): if verbose: if output: print "%s %% %s =? %s ..." %\ @@ -31,7 +31,18 @@ def testformat(formatstr, args, output=None): print 'no' print "overflow expected on %s %% %s" % \ (repr(formatstr), repr(args)) - elif output and result != output: + elif output and limit is None and result != output: + if verbose: + print 'no' + print "%s %% %s == %s != %s" % \ + (repr(formatstr), repr(args), repr(result), repr(output)) + # when 'limit' is specified, it determines how many characters + # must match exactly; lengths must always match. + # ex: limit=5, '12345678' matches '12345___' + # (mainly for floating point format tests for which an exact match + # can't be guaranteed due to rounding and representation errors) + elif output and limit is not None and ( + len(result)!=len(output) or result[:limit]!=output[:limit]): if verbose: print 'no' print "%s %% %s == %s != %s" % \ @@ -98,6 +109,7 @@ testboth("%.2d", big, "123456789012345678901234567890") testboth("%.30d", big, "123456789012345678901234567890") testboth("%.31d", big, "0123456789012345678901234567890") testboth("%32.31d", big, " 0123456789012345678901234567890") +testboth("%d", float(big), "123456________________________", 6) big = 0x1234567890abcdef12345L # 21 hex digits testboth("%x", big, "1234567890abcdef12345") @@ -135,6 +147,7 @@ testboth("%#+27.23X", big, " +0X001234567890ABCDEF12345") testboth("%#+027.23X", big, "+0X0001234567890ABCDEF12345") # same, except no 0 flag testboth("%#+27.23X", big, " +0X001234567890ABCDEF12345") +testboth("%x", float(big), "123456_______________", 6) big = 012345670123456701234567012345670L # 32 octal digits testboth("%o", big, "12345670123456701234567012345670") @@ -175,16 +188,19 @@ testboth("%#.32o", big, "012345670123456701234567012345670") testboth("%034.33o", big, "0012345670123456701234567012345670") # base marker shouldn't change that testboth("%0#34.33o", big, "0012345670123456701234567012345670") +testboth("%o", float(big), "123456__________________________", 6) # Some small ints, in both Python int and long flavors). testboth("%d", 42, "42") testboth("%d", -42, "-42") testboth("%d", 42L, "42") testboth("%d", -42L, "-42") +testboth("%d", 42.0, "42") testboth("%#x", 1, "0x1") testboth("%#x", 1L, "0x1") testboth("%#X", 1, "0X1") testboth("%#X", 1L, "0X1") +testboth("%#x", 1.0, "0x1") testboth("%#o", 1, "01") testboth("%#o", 1L, "01") testboth("%#o", 0, "0") @@ -202,11 +218,13 @@ testboth("%x", 0x42, "42") testboth("%x", -0x42, "-42") testboth("%x", 0x42L, "42") testboth("%x", -0x42L, "-42") +testboth("%x", float(0x42), "42") testboth("%o", 042, "42") testboth("%o", -042, "-42") testboth("%o", 042L, "42") testboth("%o", -042L, "-42") +testboth("%o", float(042), "42") # Test exception for unknown format characters if verbose: @@ -235,7 +253,7 @@ if have_unicode: test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError, "unsupported format character '?' (0x3000) at index 5") -test_exc('%d', '1', TypeError, "int argument required, not str") +test_exc('%d', '1', TypeError, "%d format: a number is required, not str") test_exc('%g', '1', TypeError, "float argument required, not str") test_exc('no format', '1', TypeError, "not all arguments converted during string formatting") diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 392da93a909..314ec426287 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -4585,6 +4585,7 @@ PyString_Format(PyObject *format, PyObject *args) int prec = -1; int c = '\0'; int fill; + int isnumok; PyObject *v = NULL; PyObject *temp = NULL; char *pbuf; @@ -4786,23 +4787,52 @@ PyString_Format(PyObject *format, PyObject *args) case 'X': if (c == 'i') c = 'd'; - if (PyLong_Check(v)) { - int ilen; - temp = _PyString_FormatLong(v, flags, - prec, c, &pbuf, &ilen); - len = ilen; - if (!temp) - goto error; - sign = 1; + isnumok = 0; + if (PyNumber_Check(v)) { + PyObject *iobj=NULL; + + if (PyInt_Check(v) || (PyLong_Check(v))) { + iobj = v; + Py_INCREF(iobj); + } + else { + iobj = PyNumber_Int(v); + if (iobj==NULL) iobj = PyNumber_Long(v); + } + if (iobj!=NULL) { + if (PyInt_Check(iobj)) { + isnumok = 1; + pbuf = formatbuf; + len = formatint(pbuf, + sizeof(formatbuf), + flags, prec, c, iobj); + Py_DECREF(iobj); + if (len < 0) + goto error; + sign = 1; + } + else if (PyLong_Check(iobj)) { + int ilen; + + isnumok = 1; + temp = _PyString_FormatLong(iobj, flags, + prec, c, &pbuf, &ilen); + Py_DECREF(iobj); + len = ilen; + if (!temp) + goto error; + sign = 1; + } + else { + Py_DECREF(iobj); + } + } } - else { - pbuf = formatbuf; - len = formatint(pbuf, - sizeof(formatbuf), - flags, prec, c, v); - if (len < 0) - goto error; - sign = 1; + if (!isnumok) { + PyErr_Format(PyExc_TypeError, + "%%%c format: a number is required, " + "not %.200s", c, Py_TYPE(v)->tp_name); + goto error; } if (flags & F_ZERO) fill = '0'; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 0dca976ab4f..0470ef4bc5b 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8334,6 +8334,7 @@ PyObject *PyUnicode_Format(PyObject *format, int prec = -1; Py_UNICODE c = '\0'; Py_UNICODE fill; + int isnumok; PyObject *v = NULL; PyObject *temp = NULL; Py_UNICODE *pbuf; @@ -8546,21 +8547,49 @@ PyObject *PyUnicode_Format(PyObject *format, case 'X': if (c == 'i') c = 'd'; - if (PyLong_Check(v)) { - temp = formatlong(v, flags, prec, c); - if (!temp) - goto onError; - pbuf = PyUnicode_AS_UNICODE(temp); - len = PyUnicode_GET_SIZE(temp); - sign = 1; + isnumok = 0; + if (PyNumber_Check(v)) { + PyObject *iobj=NULL; + + if (PyInt_Check(v) || (PyLong_Check(v))) { + iobj = v; + Py_INCREF(iobj); + } + else { + iobj = PyNumber_Int(v); + if (iobj==NULL) iobj = PyNumber_Long(v); + } + if (iobj!=NULL) { + if (PyInt_Check(iobj)) { + isnumok = 1; + pbuf = formatbuf; + len = formatint(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE), + flags, prec, c, iobj); + Py_DECREF(iobj); + if (len < 0) + goto onError; + sign = 1; + } + else if (PyLong_Check(iobj)) { + isnumok = 1; + temp = formatlong(iobj, flags, prec, c); + Py_DECREF(iobj); + if (!temp) + goto onError; + pbuf = PyUnicode_AS_UNICODE(temp); + len = PyUnicode_GET_SIZE(temp); + sign = 1; + } + else { + Py_DECREF(iobj); + } + } } - else { - pbuf = formatbuf; - len = formatint(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE), - flags, prec, c, v); - if (len < 0) + if (!isnumok) { + PyErr_Format(PyExc_TypeError, + "%%%c format: a number is required, " + "not %.200s", c, Py_TYPE(v)->tp_name); goto onError; - sign = 1; } if (flags & F_ZERO) fill = '0';