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:
parent
3b958e3b57
commit
d44b2fc87c
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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,26 +787,50 @@ 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,
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue