mirror of
https://github.com/python/cpython.git
synced 2025-07-28 05:34:31 +00:00

Highlights: - Adding PyObject_Format. - Adding string.Format class. - Adding __format__ for str, unicode, int, long, float, datetime. - Adding builtin format. - Adding ''.format and u''.format. - str/unicode fixups for formatters. The files in Objects/stringlib that implement PEP 3101 (stringdefs.h, unicodedefs.h, formatter.h, string_format.h) are identical in trunk and py3k. Any changes from here on should be made to trunk, and changes will propogate to py3k).
359 lines
14 KiB
Python
359 lines
14 KiB
Python
|
|
import unittest
|
|
import struct
|
|
import sys
|
|
from test import test_support, string_tests
|
|
|
|
|
|
class StrTest(
|
|
string_tests.CommonTest,
|
|
string_tests.MixinStrUnicodeUserStringTest,
|
|
string_tests.MixinStrUserStringTest,
|
|
string_tests.MixinStrUnicodeTest,
|
|
):
|
|
|
|
type2test = str
|
|
|
|
# We don't need to propagate to str
|
|
def fixtype(self, obj):
|
|
return obj
|
|
|
|
def test_formatting(self):
|
|
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
|
|
self.assertRaises(OverflowError, '%c'.__mod__, 0x1234)
|
|
|
|
def test_conversion(self):
|
|
# Make sure __str__() behaves properly
|
|
class Foo0:
|
|
def __unicode__(self):
|
|
return u"foo"
|
|
|
|
class Foo1:
|
|
def __str__(self):
|
|
return "foo"
|
|
|
|
class Foo2(object):
|
|
def __str__(self):
|
|
return "foo"
|
|
|
|
class Foo3(object):
|
|
def __str__(self):
|
|
return u"foo"
|
|
|
|
class Foo4(unicode):
|
|
def __str__(self):
|
|
return u"foo"
|
|
|
|
class Foo5(str):
|
|
def __str__(self):
|
|
return u"foo"
|
|
|
|
class Foo6(str):
|
|
def __str__(self):
|
|
return "foos"
|
|
|
|
def __unicode__(self):
|
|
return u"foou"
|
|
|
|
class Foo7(unicode):
|
|
def __str__(self):
|
|
return "foos"
|
|
def __unicode__(self):
|
|
return u"foou"
|
|
|
|
class Foo8(str):
|
|
def __new__(cls, content=""):
|
|
return str.__new__(cls, 2*content)
|
|
def __str__(self):
|
|
return self
|
|
|
|
class Foo9(str):
|
|
def __str__(self):
|
|
return "string"
|
|
def __unicode__(self):
|
|
return "not unicode"
|
|
|
|
self.assert_(str(Foo0()).startswith("<")) # this is different from __unicode__
|
|
self.assertEqual(str(Foo1()), "foo")
|
|
self.assertEqual(str(Foo2()), "foo")
|
|
self.assertEqual(str(Foo3()), "foo")
|
|
self.assertEqual(str(Foo4("bar")), "foo")
|
|
self.assertEqual(str(Foo5("bar")), "foo")
|
|
self.assertEqual(str(Foo6("bar")), "foos")
|
|
self.assertEqual(str(Foo7("bar")), "foos")
|
|
self.assertEqual(str(Foo8("foo")), "foofoo")
|
|
self.assertEqual(str(Foo9("foo")), "string")
|
|
self.assertEqual(unicode(Foo9("foo")), u"not unicode")
|
|
|
|
def test_expandtabs_overflows_gracefully(self):
|
|
# This test only affects 32-bit platforms because expandtabs can only take
|
|
# an int as the max value, not a 64-bit C long. If expandtabs is changed
|
|
# to take a 64-bit long, this test should apply to all platforms.
|
|
if sys.maxint > (1 << 32) or struct.calcsize('P') != 4:
|
|
return
|
|
self.assertRaises(OverflowError, 't\tt\t'.expandtabs, sys.maxint)
|
|
|
|
def test__format__(self):
|
|
def test(value, format, expected):
|
|
# test both with and without the trailing 's'
|
|
self.assertEqual(value.__format__(format), expected)
|
|
self.assertEqual(value.__format__(format + 's'), expected)
|
|
|
|
test('', '', '')
|
|
test('abc', '', 'abc')
|
|
test('abc', '.3', 'abc')
|
|
test('ab', '.3', 'ab')
|
|
test('abcdef', '.3', 'abc')
|
|
test('abcdef', '.0', '')
|
|
test('abc', '3.3', 'abc')
|
|
test('abc', '2.3', 'abc')
|
|
test('abc', '2.2', 'ab')
|
|
test('abc', '3.2', 'ab ')
|
|
test('result', 'x<0', 'result')
|
|
test('result', 'x<5', 'result')
|
|
test('result', 'x<6', 'result')
|
|
test('result', 'x<7', 'resultx')
|
|
test('result', 'x<8', 'resultxx')
|
|
test('result', ' <7', 'result ')
|
|
test('result', '<7', 'result ')
|
|
test('result', '>7', ' result')
|
|
test('result', '>8', ' result')
|
|
test('result', '^8', ' result ')
|
|
test('result', '^9', ' result ')
|
|
test('result', '^10', ' result ')
|
|
test('a', '10000', 'a' + ' ' * 9999)
|
|
test('', '10000', ' ' * 10000)
|
|
test('', '10000000', ' ' * 10000000)
|
|
|
|
def test_format(self):
|
|
self.assertEqual(''.format(), '')
|
|
self.assertEqual('a'.format(), 'a')
|
|
self.assertEqual('ab'.format(), 'ab')
|
|
self.assertEqual('a{{'.format(), 'a{')
|
|
self.assertEqual('a}}'.format(), 'a}')
|
|
self.assertEqual('{{b'.format(), '{b')
|
|
self.assertEqual('}}b'.format(), '}b')
|
|
self.assertEqual('a{{b'.format(), 'a{b')
|
|
|
|
# examples from the PEP:
|
|
import datetime
|
|
self.assertEqual("My name is {0}".format('Fred'), "My name is Fred")
|
|
self.assertEqual("My name is {0[name]}".format(dict(name='Fred')),
|
|
"My name is Fred")
|
|
self.assertEqual("My name is {0} :-{{}}".format('Fred'),
|
|
"My name is Fred :-{}")
|
|
|
|
d = datetime.date(2007, 8, 18)
|
|
self.assertEqual("The year is {0.year}".format(d),
|
|
"The year is 2007")
|
|
|
|
# classes we'll use for testing
|
|
class C:
|
|
def __init__(self, x=100):
|
|
self._x = x
|
|
def __format__(self, spec):
|
|
return spec
|
|
|
|
class D:
|
|
def __init__(self, x):
|
|
self.x = x
|
|
def __format__(self, spec):
|
|
return str(self.x)
|
|
|
|
# class with __str__, but no __format__
|
|
class E:
|
|
def __init__(self, x):
|
|
self.x = x
|
|
def __str__(self):
|
|
return 'E(' + self.x + ')'
|
|
|
|
# class with __repr__, but no __format__ or __str__
|
|
class F:
|
|
def __init__(self, x):
|
|
self.x = x
|
|
def __repr__(self):
|
|
return 'F(' + self.x + ')'
|
|
|
|
# class with __format__ that forwards to string, for some format_spec's
|
|
class G:
|
|
def __init__(self, x):
|
|
self.x = x
|
|
def __str__(self):
|
|
return "string is " + self.x
|
|
def __format__(self, format_spec):
|
|
if format_spec == 'd':
|
|
return 'G(' + self.x + ')'
|
|
return object.__format__(self, format_spec)
|
|
|
|
# class that returns a bad type from __format__
|
|
class H:
|
|
def __format__(self, format_spec):
|
|
return 1.0
|
|
|
|
class I(datetime.date):
|
|
def __format__(self, format_spec):
|
|
return self.strftime(format_spec)
|
|
|
|
class J(int):
|
|
def __format__(self, format_spec):
|
|
return int.__format__(self * 2, format_spec)
|
|
|
|
|
|
self.assertEqual(''.format(), '')
|
|
self.assertEqual('abc'.format(), 'abc')
|
|
self.assertEqual('{0}'.format('abc'), 'abc')
|
|
self.assertEqual('{0:}'.format('abc'), 'abc')
|
|
self.assertEqual('X{0}'.format('abc'), 'Xabc')
|
|
self.assertEqual('{0}X'.format('abc'), 'abcX')
|
|
self.assertEqual('X{0}Y'.format('abc'), 'XabcY')
|
|
self.assertEqual('{1}'.format(1, 'abc'), 'abc')
|
|
self.assertEqual('X{1}'.format(1, 'abc'), 'Xabc')
|
|
self.assertEqual('{1}X'.format(1, 'abc'), 'abcX')
|
|
self.assertEqual('X{1}Y'.format(1, 'abc'), 'XabcY')
|
|
self.assertEqual('{0}'.format(-15), '-15')
|
|
self.assertEqual('{0}{1}'.format(-15, 'abc'), '-15abc')
|
|
self.assertEqual('{0}X{1}'.format(-15, 'abc'), '-15Xabc')
|
|
self.assertEqual('{{'.format(), '{')
|
|
self.assertEqual('}}'.format(), '}')
|
|
self.assertEqual('{{}}'.format(), '{}')
|
|
self.assertEqual('{{x}}'.format(), '{x}')
|
|
self.assertEqual('{{{0}}}'.format(123), '{123}')
|
|
self.assertEqual('{{{{0}}}}'.format(), '{{0}}')
|
|
self.assertEqual('}}{{'.format(), '}{')
|
|
self.assertEqual('}}x{{'.format(), '}x{')
|
|
|
|
# weird field names
|
|
self.assertEqual("{0[foo-bar]}".format({'foo-bar':'baz'}), 'baz')
|
|
self.assertEqual("{0[foo bar]}".format({'foo bar':'baz'}), 'baz')
|
|
self.assertEqual("{0[ ]}".format({' ':3}), '3')
|
|
|
|
self.assertEqual('{foo._x}'.format(foo=C(20)), '20')
|
|
self.assertEqual('{1}{0}'.format(D(10), D(20)), '2010')
|
|
self.assertEqual('{0._x.x}'.format(C(D('abc'))), 'abc')
|
|
self.assertEqual('{0[0]}'.format(['abc', 'def']), 'abc')
|
|
self.assertEqual('{0[1]}'.format(['abc', 'def']), 'def')
|
|
self.assertEqual('{0[1][0]}'.format(['abc', ['def']]), 'def')
|
|
self.assertEqual('{0[1][0].x}'.format(['abc', [D('def')]]), 'def')
|
|
|
|
# strings
|
|
self.assertEqual('{0:.3s}'.format('abc'), 'abc')
|
|
self.assertEqual('{0:.3s}'.format('ab'), 'ab')
|
|
self.assertEqual('{0:.3s}'.format('abcdef'), 'abc')
|
|
self.assertEqual('{0:.0s}'.format('abcdef'), '')
|
|
self.assertEqual('{0:3.3s}'.format('abc'), 'abc')
|
|
self.assertEqual('{0:2.3s}'.format('abc'), 'abc')
|
|
self.assertEqual('{0:2.2s}'.format('abc'), 'ab')
|
|
self.assertEqual('{0:3.2s}'.format('abc'), 'ab ')
|
|
self.assertEqual('{0:x<0s}'.format('result'), 'result')
|
|
self.assertEqual('{0:x<5s}'.format('result'), 'result')
|
|
self.assertEqual('{0:x<6s}'.format('result'), 'result')
|
|
self.assertEqual('{0:x<7s}'.format('result'), 'resultx')
|
|
self.assertEqual('{0:x<8s}'.format('result'), 'resultxx')
|
|
self.assertEqual('{0: <7s}'.format('result'), 'result ')
|
|
self.assertEqual('{0:<7s}'.format('result'), 'result ')
|
|
self.assertEqual('{0:>7s}'.format('result'), ' result')
|
|
self.assertEqual('{0:>8s}'.format('result'), ' result')
|
|
self.assertEqual('{0:^8s}'.format('result'), ' result ')
|
|
self.assertEqual('{0:^9s}'.format('result'), ' result ')
|
|
self.assertEqual('{0:^10s}'.format('result'), ' result ')
|
|
self.assertEqual('{0:10000}'.format('a'), 'a' + ' ' * 9999)
|
|
self.assertEqual('{0:10000}'.format(''), ' ' * 10000)
|
|
self.assertEqual('{0:10000000}'.format(''), ' ' * 10000000)
|
|
|
|
# format specifiers for user defined type
|
|
self.assertEqual('{0:abc}'.format(C()), 'abc')
|
|
|
|
# !r and !s coersions
|
|
self.assertEqual('{0!s}'.format('Hello'), 'Hello')
|
|
self.assertEqual('{0!s:}'.format('Hello'), 'Hello')
|
|
self.assertEqual('{0!s:15}'.format('Hello'), 'Hello ')
|
|
self.assertEqual('{0!s:15s}'.format('Hello'), 'Hello ')
|
|
self.assertEqual('{0!r}'.format('Hello'), "'Hello'")
|
|
self.assertEqual('{0!r:}'.format('Hello'), "'Hello'")
|
|
self.assertEqual('{0!r}'.format(F('Hello')), 'F(Hello)')
|
|
|
|
# test fallback to object.__format__
|
|
self.assertEqual('{0}'.format({}), '{}')
|
|
self.assertEqual('{0}'.format([]), '[]')
|
|
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:>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,
|
|
month=8,
|
|
day=27)),
|
|
"date: 2007-08-27")
|
|
|
|
# test deriving from a builtin type and overriding __format__
|
|
self.assertEqual("{0}".format(J(10)), "20")
|
|
|
|
|
|
# string format specifiers
|
|
self.assertEqual('{0:}'.format('a'), 'a')
|
|
|
|
# computed format specifiers
|
|
self.assertEqual("{0:.{1}}".format('hello world', 5), 'hello')
|
|
self.assertEqual("{0:.{1}s}".format('hello world', 5), 'hello')
|
|
self.assertEqual("{0:.{precision}s}".format('hello world', precision=5), 'hello')
|
|
self.assertEqual("{0:{width}.{precision}s}".format('hello world', width=10, precision=5), 'hello ')
|
|
self.assertEqual("{0:{width}.{precision}s}".format('hello world', width='10', precision='5'), 'hello ')
|
|
|
|
# test various errors
|
|
self.assertRaises(ValueError, '{'.format)
|
|
self.assertRaises(ValueError, '}'.format)
|
|
self.assertRaises(ValueError, 'a{'.format)
|
|
self.assertRaises(ValueError, 'a}'.format)
|
|
self.assertRaises(ValueError, '{a'.format)
|
|
self.assertRaises(ValueError, '}a'.format)
|
|
self.assertRaises(IndexError, '{0}'.format)
|
|
self.assertRaises(IndexError, '{1}'.format, 'abc')
|
|
self.assertRaises(KeyError, '{x}'.format)
|
|
self.assertRaises(ValueError, "}{".format)
|
|
self.assertRaises(ValueError, "{".format)
|
|
self.assertRaises(ValueError, "}".format)
|
|
self.assertRaises(ValueError, "abc{0:{}".format)
|
|
self.assertRaises(ValueError, "{0".format)
|
|
self.assertRaises(IndexError, "{0.}".format)
|
|
self.assertRaises(ValueError, "{0.}".format, 0)
|
|
self.assertRaises(IndexError, "{0[}".format)
|
|
self.assertRaises(ValueError, "{0[}".format, [])
|
|
self.assertRaises(KeyError, "{0]}".format)
|
|
self.assertRaises(ValueError, "{0.[]}".format, 0)
|
|
self.assertRaises(ValueError, "{0..foo}".format, 0)
|
|
self.assertRaises(ValueError, "{0[0}".format, 0)
|
|
self.assertRaises(ValueError, "{0[0:foo}".format, 0)
|
|
self.assertRaises(KeyError, "{c]}".format)
|
|
self.assertRaises(ValueError, "{{ {{{0}}".format, 0)
|
|
self.assertRaises(ValueError, "{0}}".format, 0)
|
|
self.assertRaises(KeyError, "{foo}".format, bar=3)
|
|
self.assertRaises(ValueError, "{0!x}".format, 3)
|
|
self.assertRaises(ValueError, "{0!}".format, 0)
|
|
self.assertRaises(ValueError, "{0!rs}".format, 0)
|
|
self.assertRaises(ValueError, "{!}".format)
|
|
self.assertRaises(ValueError, "{:}".format)
|
|
self.assertRaises(ValueError, "{:s}".format)
|
|
self.assertRaises(ValueError, "{}".format)
|
|
|
|
# can't have a replacement on the field name portion
|
|
self.assertRaises(TypeError, '{0[{1}]}'.format, 'abcdefg', 4)
|
|
|
|
# exceed maximum recursion depth
|
|
self.assertRaises(ValueError, "{0:{1:{2}}}".format, 'abc', 's', '')
|
|
self.assertRaises(ValueError, "{0:{1:{2:{3:{4:{5:{6}}}}}}}".format,
|
|
0, 1, 2, 3, 4, 5, 6, 7)
|
|
|
|
# string format spec errors
|
|
self.assertRaises(ValueError, "{0:-s}".format, '')
|
|
self.assertRaises(ValueError, format, "", "-")
|
|
self.assertRaises(ValueError, "{0:=s}".format, '')
|
|
|
|
|
|
def test_main():
|
|
test_support.run_unittest(StrTest)
|
|
|
|
if __name__ == "__main__":
|
|
test_main()
|