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
|
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')
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue