mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
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.
This commit is contained in:
parent
af9d10aa30
commit
e4d6317c87
4 changed files with 144 additions and 6 deletions
|
@ -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,12 +609,15 @@ 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:d}'.format(G('data')), 'G(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:^10}'.format(E('data')), ' E(data) ')
|
||||||
self.assertEqual('{0:^10s}'.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:>15s}'.format(G('data')), ' string is data')
|
self.assertEqual('{0:>15s}'.format(G('data')), ' string is data')
|
||||||
self.assertEqual('{0!s}'.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,
|
||||||
|
|
|
@ -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)
|
||||||
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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue