From e4d6317c8725f9f341c6f2dd628e3b3ac79ef309 Mon Sep 17 00:00:00 2001 From: Eric Smith Date: Mon, 13 Sep 2010 20:48:43 +0000 Subject: [PATCH] Issue 7994: Make object.__format__() raise a PendingDeprecationWarning if the format string is not empty. Manually merge r79596 and r84772 from 2.x. Also, apparently test_format() from test_builtin never made it into 3.x. I've added it as well. It tests the basic format() infrastructure. --- Lib/test/test_builtin.py | 110 +++++++++++++++++++++++++++++++++++++++ Lib/test/test_unicode.py | 11 ++-- Misc/NEWS | 8 +++ Objects/typeobject.c | 21 +++++++- 4 files changed, 144 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 35b652bd92c..f00091b2956 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1279,6 +1279,116 @@ class BuiltinTest(unittest.TestCase): return i self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq())) + def test_format(self): + # Test the basic machinery of the format() builtin. Don't test + # the specifics of the various formatters + self.assertEqual(format(3, ''), '3') + + # Returns some classes to use for various tests. There's + # an old-style version, and a new-style version + def classes_new(): + class A(object): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromA(A): + pass + + class Simple(object): pass + class DerivedFromSimple(Simple): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromSimple2(DerivedFromSimple): pass + return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2 + + def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2): + self.assertEqual(format(A(3), 'spec'), '3spec') + self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec') + self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc') + self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'), + '10abcdef') + + class_test(*classes_new()) + + def empty_format_spec(value): + # test that: + # format(x, '') == str(x) + # format(x) == str(x) + self.assertEqual(format(value, ""), str(value)) + self.assertEqual(format(value), str(value)) + + # for builtin types, format(x, "") == str(x) + empty_format_spec(17**13) + empty_format_spec(1.0) + empty_format_spec(3.1415e104) + empty_format_spec(-3.1415e104) + empty_format_spec(3.1415e-104) + empty_format_spec(-3.1415e-104) + empty_format_spec(object) + empty_format_spec(None) + + # TypeError because self.__format__ returns the wrong type + class BadFormatResult: + def __format__(self, format_spec): + return 1.0 + self.assertRaises(TypeError, format, BadFormatResult(), "") + + # TypeError because format_spec is not unicode or str + self.assertRaises(TypeError, format, object(), 4) + self.assertRaises(TypeError, format, object(), object()) + + # tests for object.__format__ really belong elsewhere, but + # there's no good place to put them + x = object().__format__('') + self.assertTrue(x.startswith('15s}'.format(G('data')), ' string is data') self.assertEqual('{0!s}'.format(G('data')), 'string is data') + msg = 'object.__format__ with a non-empty format string is deprecated' + with support.check_warnings((msg, PendingDeprecationWarning)): + self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ') + self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ') + self.assertEqual('{0:>15s}'.format(G('data')), ' string is data') + self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007, month=8, day=27)), diff --git a/Misc/NEWS b/Misc/NEWS index 04f5799c249..0b86bc9c74d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,14 @@ What's New in Python 3.2 Alpha 3? Core and Builtins ----------------- +- Issue #7994: Issue a PendingDeprecationWarning if object.__format__ + is called with a non-empty format string. This is an effort to + future-proof user code. If a derived class does not currently + implement __format__ but later adds its own __format__, it would + most likely break user code that had supplied a format string. This + will be changed to a DeprecationWaring in Python 3.3 and it will be + an error in Python 3.4. + - Issue #9828: Destroy the GIL in Py_Finalize(), so that it gets properly re-created on a subsequent call to Py_Initialize(). The problem (a crash) wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 8b74e1e0768..897374d4879 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3315,9 +3315,26 @@ object_format(PyObject *self, PyObject *args) return NULL; self_as_str = PyObject_Str(self); - if (self_as_str != NULL) - result = PyObject_Format(self_as_str, format_spec); + if (self_as_str != NULL) { + /* Issue 7994: If we're converting to a string, we + should reject format specifications */ + if (PyUnicode_GET_SIZE(format_spec) > 0) { + if (PyErr_WarnEx(PyExc_PendingDeprecationWarning, + "object.__format__ with a non-empty format " + "string is deprecated", 1) < 0) { + goto done; + } + /* Eventually this will become an error: + PyErr_Format(PyExc_TypeError, + "non-empty format string passed to object.__format__"); + goto done; + */ + } + result = PyObject_Format(self_as_str, format_spec); + } + +done: Py_XDECREF(self_as_str); return result;