Index: Objects/abstract.c =================================================================== --- Objects/abstract.c (revision 78379) +++ Objects/abstract.c (working copy) @@ -777,8 +777,9 @@ NULL); Py_DECREF(bound_method); } else { - PyObject *self_as_str; - PyObject *format_method; + PyObject *self_as_str = NULL; + PyObject *format_method = NULL; + Py_ssize_t format_len; PyErr_Clear(); /* Per the PEP, convert to str (or unicode, @@ -786,29 +787,53 @@ specifier). For new-style classes, this logic is done by object.__format__(). */ #ifdef Py_USING_UNICODE - if (spec_is_unicode) + if (spec_is_unicode) { + format_len = PyUnicode_GET_SIZE(format_spec); self_as_str = PyObject_Unicode(obj); - else + } else #endif + { + format_len = PyString_GET_SIZE(format_spec); self_as_str = PyObject_Str(obj); + } if (self_as_str == NULL) - goto done; + goto done1; + if (format_len > 0) { + /* See the almost identical code in + typeobject.c for new-style + classes. */ + if (PyErr_WarnEx( + PyExc_PendingDeprecationWarning, + "object.__format__ with a non-empty " + "format string is deprecated", 1) + < 0) { + goto done1; + } + /* Eventually this will become an + error: + PyErr_Format(PyExc_TypeError, + "non-empty format string passed to " + "object.__format__"); + goto done1; + */ + } + /* Then call str.__format__ on that result */ format_method = PyObject_GetAttr(self_as_str, str__format__); if (format_method == NULL) { - Py_DECREF(self_as_str); - goto done; + goto done1; } - result = PyObject_CallFunctionObjArgs(format_method, + result = PyObject_CallFunctionObjArgs(format_method, format_spec, NULL); - Py_DECREF(self_as_str); - Py_DECREF(format_method); +done1: + Py_XDECREF(self_as_str); + Py_XDECREF(format_method); if (result == NULL) goto done; - } + } } else { /* Not an instance of a classic class, use the code from py3k */ Index: Objects/typeobject.c =================================================================== --- Objects/typeobject.c (revision 78379) +++ Objects/typeobject.c (working copy) @@ -3414,31 +3414,54 @@ PyObject *self_as_str = NULL; PyObject *result = NULL; PyObject *format_meth = NULL; + Py_ssize_t format_len; if (!PyArg_ParseTuple(args, "O:__format__", &format_spec)) return NULL; #ifdef Py_USING_UNICODE if (PyUnicode_Check(format_spec)) { + format_len = PyUnicode_GET_SIZE(format_spec); self_as_str = PyObject_Unicode(self); } else if (PyString_Check(format_spec)) { #else if (PyString_Check(format_spec)) { #endif + format_len = PyString_GET_SIZE(format_spec); self_as_str = PyObject_Str(self); } else { - PyErr_SetString(PyExc_TypeError, "argument to __format__ must be unicode or str"); + PyErr_SetString(PyExc_TypeError, + "argument to __format__ must be unicode or str"); return NULL; } if (self_as_str != NULL) { + /* Issue 7994: If we're converting to a string, we + should reject format specifications */ + if (format_len > 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; + */ + } + /* find the format function */ - format_meth = PyObject_GetAttrString(self_as_str, "__format__"); + format_meth = PyObject_GetAttrString(self_as_str, + "__format__"); if (format_meth != NULL) { /* and call it */ - result = PyObject_CallFunctionObjArgs(format_meth, format_spec, NULL); + result = PyObject_CallFunctionObjArgs(format_meth, + format_spec, + NULL); } } +done: Py_XDECREF(self_as_str); Py_XDECREF(format_meth); Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 78379) +++ Misc/NEWS (working copy) @@ -12,6 +12,14 @@ 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 #6902: Fix problem with built-in types format incorrectly with 0 padding. Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 78379) +++ Lib/test/test_builtin.py (working copy) @@ -1485,6 +1485,40 @@ self.assertRaises(TypeError, object().__format__, object()) self.assertRaises(TypeError, object().__format__, None) + # -------------------------------------------------------------------- + # Issue #7994: object.__format__ with a non-empty format string is + # pending deprecated + def test_deprecated_format_string(obj, fmt_str, should_raise_warning): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always", PendingDeprecationWarning) + format(obj, fmt_str) + if should_raise_warning: + self.assertEqual(len(w), 1) + self.assertIsInstance(w[0].message, PendingDeprecationWarning) + self.assertIn('object.__format__ with a non-empty format ' + 'string', w[0].message.message) + else: + self.assertEqual(len(w), 0) + test_deprecated_format_string(object(), '', False) + test_deprecated_format_string(object(), 's', True) + + class A: + def __format__(self, fmt_str): + return format('', fmt_str) + test_deprecated_format_string(A(), '', False) + test_deprecated_format_string(A(), 's', False) + + class B: + pass + test_deprecated_format_string(B(), '', False) + test_deprecated_format_string(B(), 's', True) + + class C(object): + pass + test_deprecated_format_string(C(), '', False) + test_deprecated_format_string(C(), 's', True) + # -------------------------------------------------------------------- + # make sure we can take a subclass of str as a format spec class DerivedFromStr(str): pass self.assertEqual(format(0, DerivedFromStr('10')), ' 0')