Commit e4d6317c authored by Eric Smith's avatar Eric Smith

Issue 7994: Make object.__format__() raise a PendingDeprecationWarning

if the format string is not empty. Manually merge r79596 and r84772
from 2.x.

Also, apparently test_format() from test_builtin never made it into
3.x. I've added it as well. It tests the basic format()
infrastructure.
parent af9d10aa
...@@ -1279,6 +1279,116 @@ class BuiltinTest(unittest.TestCase): ...@@ -1279,6 +1279,116 @@ class BuiltinTest(unittest.TestCase):
return i return i
self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq())) self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq()))
def test_format(self):
# Test the basic machinery of the format() builtin. Don't test
# the specifics of the various formatters
self.assertEqual(format(3, ''), '3')
# Returns some classes to use for various tests. There's
# an old-style version, and a new-style version
def classes_new():
class A(object):
def __init__(self, x):
self.x = x
def __format__(self, format_spec):
return str(self.x) + format_spec
class DerivedFromA(A):
pass
class Simple(object): pass
class DerivedFromSimple(Simple):
def __init__(self, x):
self.x = x
def __format__(self, format_spec):
return str(self.x) + format_spec
class DerivedFromSimple2(DerivedFromSimple): pass
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2):
self.assertEqual(format(A(3), 'spec'), '3spec')
self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec')
self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc')
self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'),
'10abcdef')
class_test(*classes_new())
def empty_format_spec(value):
# test that:
# format(x, '') == str(x)
# format(x) == str(x)
self.assertEqual(format(value, ""), str(value))
self.assertEqual(format(value), str(value))
# for builtin types, format(x, "") == str(x)
empty_format_spec(17**13)
empty_format_spec(1.0)
empty_format_spec(3.1415e104)
empty_format_spec(-3.1415e104)
empty_format_spec(3.1415e-104)
empty_format_spec(-3.1415e-104)
empty_format_spec(object)
empty_format_spec(None)
# TypeError because self.__format__ returns the wrong type
class BadFormatResult:
def __format__(self, format_spec):
return 1.0
self.assertRaises(TypeError, format, BadFormatResult(), "")
# TypeError because format_spec is not unicode or str
self.assertRaises(TypeError, format, object(), 4)
self.assertRaises(TypeError, format, object(), object())
# tests for object.__format__ really belong elsewhere, but
# there's no good place to put them
x = object().__format__('')
self.assertTrue(x.startswith('<object object at'))
# first argument to object.__format__ must be string
self.assertRaises(TypeError, object().__format__, 3)
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']
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')
def test_bin(self): def test_bin(self):
self.assertEqual(bin(0), '0b0') self.assertEqual(bin(0), '0b0')
self.assertEqual(bin(1), '0b1') self.assertEqual(bin(1), '0b1')
......
...@@ -609,13 +609,16 @@ class UnicodeTest(string_tests.CommonTest, ...@@ -609,13 +609,16 @@ class UnicodeTest(string_tests.CommonTest,
self.assertEqual('{0}'.format({}), '{}') self.assertEqual('{0}'.format({}), '{}')
self.assertEqual('{0}'.format([]), '[]') self.assertEqual('{0}'.format([]), '[]')
self.assertEqual('{0}'.format([1]), '[1]') self.assertEqual('{0}'.format([1]), '[1]')
self.assertEqual('{0}'.format(E('data')), 'E(data)')
self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ')
self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ')
self.assertEqual('{0:d}'.format(G('data')), 'G(data)') self.assertEqual('{0:d}'.format(G('data')), 'G(data)')
self.assertEqual('{0:>15s}'.format(G('data')), ' string is data')
self.assertEqual('{0!s}'.format(G('data')), 'string is data') self.assertEqual('{0!s}'.format(G('data')), 'string is data')
msg = 'object.__format__ with a non-empty format string is deprecated'
with support.check_warnings((msg, PendingDeprecationWarning)):
self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ')
self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ')
self.assertEqual('{0:>15s}'.format(G('data')), ' string is data')
self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007, self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007,
month=8, month=8,
day=27)), day=27)),
......
...@@ -10,6 +10,14 @@ What's New in Python 3.2 Alpha 3? ...@@ -10,6 +10,14 @@ What's New in Python 3.2 Alpha 3?
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 #9828: Destroy the GIL in Py_Finalize(), so that it gets properly - Issue #9828: Destroy the GIL in Py_Finalize(), so that it gets properly
re-created on a subsequent call to Py_Initialize(). The problem (a crash) re-created on a subsequent call to Py_Initialize(). The problem (a crash)
wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial. wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial.
......
...@@ -3315,9 +3315,26 @@ object_format(PyObject *self, PyObject *args) ...@@ -3315,9 +3315,26 @@ object_format(PyObject *self, PyObject *args)
return NULL; return NULL;
self_as_str = PyObject_Str(self); self_as_str = PyObject_Str(self);
if (self_as_str != NULL) if (self_as_str != NULL) {
result = PyObject_Format(self_as_str, format_spec); /* Issue 7994: If we're converting to a string, we
should reject format specifications */
if (PyUnicode_GET_SIZE(format_spec) > 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;
*/
}
result = PyObject_Format(self_as_str, format_spec);
}
done:
Py_XDECREF(self_as_str); Py_XDECREF(self_as_str);
return result; return result;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment