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
from test.test_support import fcmp, have_unicode, TESTFN, unlink, \
run_unittest, check_py3k_warnings
import warnings
from operator import neg
import sys, cStringIO, random, UserDict
@ -1483,6 +1484,41 @@ class BuiltinTest(unittest.TestCase):
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', 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
class DerivedFromStr(str): pass
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
-----------------
- 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
references.

View File

@ -777,8 +777,9 @@ PyObject_Format(PyObject* obj, PyObject *format_spec)
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 @@ PyObject_Format(PyObject* obj, PyObject *format_spec)
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 */

View File

@ -3414,31 +3414,54 @@ object_format(PyObject *self, PyObject *args)
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);