Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e4d6317

Browse files
committed
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.
1 parent af9d10a commit e4d6317

4 files changed

Lines changed: 145 additions & 7 deletions

File tree

Lib/test/test_builtin.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,116 @@ def __getitem__(self, i):
12791279
return i
12801280
self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq()))
12811281

1282+
def test_format(self):
1283+
# Test the basic machinery of the format() builtin. Don't test
1284+
# the specifics of the various formatters
1285+
self.assertEqual(format(3, ''), '3')
1286+
1287+
# Returns some classes to use for various tests. There's
1288+
# an old-style version, and a new-style version
1289+
def classes_new():
1290+
class A(object):
1291+
def __init__(self, x):
1292+
self.x = x
1293+
def __format__(self, format_spec):
1294+
return str(self.x) + format_spec
1295+
class DerivedFromA(A):
1296+
pass
1297+
1298+
class Simple(object): pass
1299+
class DerivedFromSimple(Simple):
1300+
def __init__(self, x):
1301+
self.x = x
1302+
def __format__(self, format_spec):
1303+
return str(self.x) + format_spec
1304+
class DerivedFromSimple2(DerivedFromSimple): pass
1305+
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
1306+
1307+
def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2):
1308+
self.assertEqual(format(A(3), 'spec'), '3spec')
1309+
self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec')
1310+
self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc')
1311+
self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'),
1312+
'10abcdef')
1313+
1314+
class_test(*classes_new())
1315+
1316+
def empty_format_spec(value):
1317+
# test that:
1318+
# format(x, '') == str(x)
1319+
# format(x) == str(x)
1320+
self.assertEqual(format(value, ""), str(value))
1321+
self.assertEqual(format(value), str(value))
1322+
1323+
# for builtin types, format(x, "") == str(x)
1324+
empty_format_spec(17**13)
1325+
empty_format_spec(1.0)
1326+
empty_format_spec(3.1415e104)
1327+
empty_format_spec(-3.1415e104)
1328+
empty_format_spec(3.1415e-104)
1329+
empty_format_spec(-3.1415e-104)
1330+
empty_format_spec(object)
1331+
empty_format_spec(None)
1332+
1333+
# TypeError because self.__format__ returns the wrong type
1334+
class BadFormatResult:
1335+
def __format__(self, format_spec):
1336+
return 1.0
1337+
self.assertRaises(TypeError, format, BadFormatResult(), "")
1338+
1339+
# TypeError because format_spec is not unicode or str
1340+
self.assertRaises(TypeError, format, object(), 4)
1341+
self.assertRaises(TypeError, format, object(), object())
1342+
1343+
# tests for object.__format__ really belong elsewhere, but
1344+
# there's no good place to put them
1345+
x = object().__format__('')
1346+
self.assertTrue(x.startswith('<object object at'))
1347+
1348+
# first argument to object.__format__ must be string
1349+
self.assertRaises(TypeError, object().__format__, 3)
1350+
self.assertRaises(TypeError, object().__format__, object())
1351+
self.assertRaises(TypeError, object().__format__, None)
1352+
1353+
# --------------------------------------------------------------------
1354+
# Issue #7994: object.__format__ with a non-empty format string is
1355+
# pending deprecated
1356+
def test_deprecated_format_string(obj, fmt_str, should_raise_warning):
1357+
with warnings.catch_warnings(record=True) as w:
1358+
warnings.simplefilter("always", PendingDeprecationWarning)
1359+
format(obj, fmt_str)
1360+
if should_raise_warning:
1361+
self.assertEqual(len(w), 1)
1362+
self.assertIsInstance(w[0].message, PendingDeprecationWarning)
1363+
self.assertIn('object.__format__ with a non-empty format '
1364+
'string', str(w[0].message))
1365+
else:
1366+
self.assertEqual(len(w), 0)
1367+
1368+
fmt_strs = ['', 's']
1369+
1370+
class A:
1371+
def __format__(self, fmt_str):
1372+
return format('', fmt_str)
1373+
1374+
for fmt_str in fmt_strs:
1375+
test_deprecated_format_string(A(), fmt_str, False)
1376+
1377+
class B:
1378+
pass
1379+
1380+
class C(object):
1381+
pass
1382+
1383+
for cls in [object, B, C]:
1384+
for fmt_str in fmt_strs:
1385+
test_deprecated_format_string(cls(), fmt_str, len(fmt_str) != 0)
1386+
# --------------------------------------------------------------------
1387+
1388+
# make sure we can take a subclass of str as a format spec
1389+
class DerivedFromStr(str): pass
1390+
self.assertEqual(format(0, DerivedFromStr('10')), ' 0')
1391+
12821392
def test_bin(self):
12831393
self.assertEqual(bin(0), '0b0')
12841394
self.assertEqual(bin(1), '0b1')

Lib/test/test_unicode.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -609,13 +609,16 @@ def __format__(self, format_spec):
609609
self.assertEqual('{0}'.format({}), '{}')
610610
self.assertEqual('{0}'.format([]), '[]')
611611
self.assertEqual('{0}'.format([1]), '[1]')
612-
self.assertEqual('{0}'.format(E('data')), 'E(data)')
613-
self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ')
614-
self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ')
612+
615613
self.assertEqual('{0:d}'.format(G('data')), 'G(data)')
616-
self.assertEqual('{0:>15s}'.format(G('data')), ' string is data')
617614
self.assertEqual('{0!s}'.format(G('data')), 'string is data')
618615

616+
msg = 'object.__format__ with a non-empty format string is deprecated'
617+
with support.check_warnings((msg, PendingDeprecationWarning)):
618+
self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ')
619+
self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ')
620+
self.assertEqual('{0:>15s}'.format(G('data')), ' string is data')
621+
619622
self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007,
620623
month=8,
621624
day=27)),

Misc/NEWS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ What's New in Python 3.2 Alpha 3?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #7994: Issue a PendingDeprecationWarning if object.__format__
14+
is called with a non-empty format string. This is an effort to
15+
future-proof user code. If a derived class does not currently
16+
implement __format__ but later adds its own __format__, it would
17+
most likely break user code that had supplied a format string. This
18+
will be changed to a DeprecationWaring in Python 3.3 and it will be
19+
an error in Python 3.4.
20+
1321
- Issue #9828: Destroy the GIL in Py_Finalize(), so that it gets properly
1422
re-created on a subsequent call to Py_Initialize(). The problem (a crash)
1523
wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial.

Objects/typeobject.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3315,9 +3315,26 @@ object_format(PyObject *self, PyObject *args)
33153315
return NULL;
33163316

33173317
self_as_str = PyObject_Str(self);
3318-
if (self_as_str != NULL)
3319-
result = PyObject_Format(self_as_str, format_spec);
3320-
3318+
if (self_as_str != NULL) {
3319+
/* Issue 7994: If we're converting to a string, we
3320+
should reject format specifications */
3321+
if (PyUnicode_GET_SIZE(format_spec) > 0) {
3322+
if (PyErr_WarnEx(PyExc_PendingDeprecationWarning,
3323+
"object.__format__ with a non-empty format "
3324+
"string is deprecated", 1) < 0) {
3325+
goto done;
3326+
}
3327+
/* Eventually this will become an error:
3328+
PyErr_Format(PyExc_TypeError,
3329+
"non-empty format string passed to object.__format__");
3330+
goto done;
3331+
*/
3332+
}
3333+
3334+
result = PyObject_Format(self_as_str, format_spec);
3335+
}
3336+
3337+
done:
33213338
Py_XDECREF(self_as_str);
33223339

33233340
return result;

0 commit comments

Comments
 (0)