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
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')
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.
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,
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 */
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);