diff options
author | Eric Smith <eric@trueblade.com> | 2008-02-17 19:46:49 (GMT) |
---|---|---|
committer | Eric Smith <eric@trueblade.com> | 2008-02-17 19:46:49 (GMT) |
commit | a9f7d6248032c9572b4d2024a1be8bd2823af09f (patch) | |
tree | 5465a1051312055678248db0076d314924ee4ebc /Lib/test/test_str.py | |
parent | e139688d34cc12b23d3a310f10d4f440f75f7d08 (diff) | |
download | cpython-a9f7d6248032c9572b4d2024a1be8bd2823af09f.zip cpython-a9f7d6248032c9572b4d2024a1be8bd2823af09f.tar.gz cpython-a9f7d6248032c9572b4d2024a1be8bd2823af09f.tar.bz2 |
Backport of PEP 3101, Advanced String Formatting, from py3k.
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).
Diffstat (limited to 'Lib/test/test_str.py')
-rw-r--r-- | Lib/test/test_str.py | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 3acb6d1..7ea46bb 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -93,6 +93,264 @@ class StrTest( 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) |