Issue 7994: Make object.__format__ with a non-empty format string a PendingDecprecationWarning. Still need to remove uses of this from various tests.

This commit is contained in:
Eric Smith 2010-04-02 12:30:56 +00:00
parent 3b958e3b57
commit d44b2fc87c
4 changed files with 106 additions and 14 deletions

View File

@ -4,6 +4,7 @@ import platform
import unittest import unittest
from test.test_support import fcmp, have_unicode, TESTFN, unlink, \ from test.test_support import fcmp, have_unicode, TESTFN, unlink, \
run_unittest, check_py3k_warnings run_unittest, check_py3k_warnings
import warnings
from operator import neg from operator import neg
import sys, cStringIO, random, UserDict import sys, cStringIO, random, UserDict
@ -1483,6 +1484,41 @@ class BuiltinTest(unittest.TestCase):
self.assertRaises(TypeError, object().__format__, object()) self.assertRaises(TypeError, object().__format__, object())
self.assertRaises(TypeError, object().__format__, None) 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', str(w[0].message))
else:
self.assertEqual(len(w), 0)
fmt_strs = ['', 's', u'', u's']
class A:
def __format__(self, fmt_str):
return format('', fmt_str)
for fmt_str in fmt_strs:
test_deprecated_format_string(A(), fmt_str, False)
class B:
pass
class C(object):
pass
for cls in [object, B, C]:
for fmt_str in fmt_strs:
test_deprecated_format_string(cls(), fmt_str, len(fmt_str) != 0)
# --------------------------------------------------------------------
# make sure we can take a subclass of str as a format spec # make sure we can take a subclass of str as a format spec
class DerivedFromStr(str): pass class DerivedFromStr(str): pass
self.assertEqual(format(0, DerivedFromStr('10')), ' 0') self.assertEqual(format(0, DerivedFromStr('10')), ' 0')

View File

@ -12,6 +12,14 @@ What's New in Python 2.7 beta 1?
Core and Builtins 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 #8268: Old-style classes (not just instances) now support weak - Issue #8268: Old-style classes (not just instances) now support weak
references. references.

View File

@ -777,8 +777,9 @@ PyObject_Format(PyObject* obj, PyObject *format_spec)
NULL); NULL);
Py_DECREF(bound_method); Py_DECREF(bound_method);
} else { } else {
PyObject *self_as_str; PyObject *self_as_str = NULL;
PyObject *format_method; PyObject *format_method = NULL;
Py_ssize_t format_len;
PyErr_Clear(); PyErr_Clear();
/* Per the PEP, convert to str (or unicode, /* Per the PEP, convert to str (or unicode,
@ -786,29 +787,53 @@ PyObject_Format(PyObject* obj, PyObject *format_spec)
specifier). For new-style classes, this specifier). For new-style classes, this
logic is done by object.__format__(). */ logic is done by object.__format__(). */
#ifdef Py_USING_UNICODE #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); self_as_str = PyObject_Unicode(obj);
else } else
#endif #endif
{
format_len = PyString_GET_SIZE(format_spec);
self_as_str = PyObject_Str(obj); self_as_str = PyObject_Str(obj);
}
if (self_as_str == NULL) 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 */ /* Then call str.__format__ on that result */
format_method = PyObject_GetAttr(self_as_str, format_method = PyObject_GetAttr(self_as_str,
str__format__); str__format__);
if (format_method == NULL) { if (format_method == NULL) {
Py_DECREF(self_as_str); goto done1;
goto done;
} }
result = PyObject_CallFunctionObjArgs(format_method, result = PyObject_CallFunctionObjArgs(format_method,
format_spec, format_spec,
NULL); NULL);
Py_DECREF(self_as_str); done1:
Py_DECREF(format_method); Py_XDECREF(self_as_str);
Py_XDECREF(format_method);
if (result == NULL) if (result == NULL)
goto done; goto done;
} }
} else { } else {
/* Not an instance of a classic class, use the code /* Not an instance of a classic class, use the code
from py3k */ from py3k */

View File

@ -3414,31 +3414,54 @@ object_format(PyObject *self, PyObject *args)
PyObject *self_as_str = NULL; PyObject *self_as_str = NULL;
PyObject *result = NULL; PyObject *result = NULL;
PyObject *format_meth = NULL; PyObject *format_meth = NULL;
Py_ssize_t format_len;
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec)) if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
return NULL; return NULL;
#ifdef Py_USING_UNICODE #ifdef Py_USING_UNICODE
if (PyUnicode_Check(format_spec)) { if (PyUnicode_Check(format_spec)) {
format_len = PyUnicode_GET_SIZE(format_spec);
self_as_str = PyObject_Unicode(self); self_as_str = PyObject_Unicode(self);
} else if (PyString_Check(format_spec)) { } else if (PyString_Check(format_spec)) {
#else #else
if (PyString_Check(format_spec)) { if (PyString_Check(format_spec)) {
#endif #endif
format_len = PyString_GET_SIZE(format_spec);
self_as_str = PyObject_Str(self); self_as_str = PyObject_Str(self);
} else { } 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; return NULL;
} }
if (self_as_str != 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 */ /* find the format function */
format_meth = PyObject_GetAttrString(self_as_str, "__format__"); format_meth = PyObject_GetAttrString(self_as_str,
"__format__");
if (format_meth != NULL) { if (format_meth != NULL) {
/* and call it */ /* 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(self_as_str);
Py_XDECREF(format_meth); Py_XDECREF(format_meth);