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 | |
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')
-rw-r--r-- | Lib/string.py | 112 | ||||
-rw-r--r-- | Lib/test/test_builtin.py | 95 | ||||
-rw-r--r-- | Lib/test/test_datetime.py | 76 | ||||
-rw-r--r-- | Lib/test/test_descrtut.py | 1 | ||||
-rw-r--r-- | Lib/test/test_str.py | 258 | ||||
-rw-r--r-- | Lib/test/test_string.py | 86 | ||||
-rw-r--r-- | Lib/test/test_types.py | 251 | ||||
-rw-r--r-- | Lib/test/test_unicode.py | 262 |
8 files changed, 1141 insertions, 0 deletions
diff --git a/Lib/string.py b/Lib/string.py index 9e3cc41..eb680c2 100644 --- a/Lib/string.py +++ b/Lib/string.py @@ -527,3 +527,115 @@ try: letters = lowercase + uppercase except ImportError: pass # Use the original versions + +######################################################################## +# the Formatter class +# see PEP 3101 for details and purpose of this class + +# The hard parts are reused from the C implementation. They're +# exposed here via the sys module. sys was chosen because it's always +# available and doesn't have to be dynamically loaded. + +# The overall parser is implemented in str._formatter_parser. +# The field name parser is implemented in str._formatter_field_name_split + +class Formatter(object): + def format(self, format_string, *args, **kwargs): + return self.vformat(format_string, args, kwargs) + + def vformat(self, format_string, args, kwargs): + used_args = set() + result = self._vformat(format_string, args, kwargs, used_args, 2) + self.check_unused_args(used_args, args, kwargs) + return result + + def _vformat(self, format_string, args, kwargs, used_args, recursion_depth): + if recursion_depth < 0: + raise ValueError('Max string recursion exceeded') + result = [] + for literal_text, field_name, format_spec, conversion in \ + self.parse(format_string): + + # output the literal text + if literal_text: + result.append(literal_text) + + # if there's a field, output it + if field_name is not None: + # this is some markup, find the object and do + # the formatting + + # given the field_name, find the object it references + # and the argument it came from + obj, arg_used = self.get_field(field_name, args, kwargs) + used_args.add(arg_used) + + # do any conversion on the resulting object + obj = self.convert_field(obj, conversion) + + # expand the format spec, if needed + format_spec = self._vformat(format_spec, args, kwargs, + used_args, recursion_depth-1) + + # format the object and append to the result + result.append(self.format_field(obj, format_spec)) + + return ''.join(result) + + + def get_value(self, key, args, kwargs): + if isinstance(key, (int, long)): + return args[key] + else: + return kwargs[key] + + + def check_unused_args(self, used_args, args, kwargs): + pass + + + def format_field(self, value, format_spec): + return format(value, format_spec) + + + def convert_field(self, value, conversion): + # do any conversion on the resulting object + if conversion == 'r': + return repr(value) + elif conversion == 's': + return str(value) + elif conversion is None: + return value + raise ValueError("Unknown converion specifier {0!s}".format(conversion)) + + + # returns an iterable that contains tuples of the form: + # (literal_text, field_name, format_spec, conversion) + # literal_text can be zero length + # field_name can be None, in which case there's no + # object to format and output + # if field_name is not None, it is looked up, formatted + # with format_spec and conversion and then used + def parse(self, format_string): + return format_string._formatter_parser() + + + # given a field_name, find the object it references. + # field_name: the field being looked up, e.g. "0.name" + # or "lookup[3]" + # used_args: a set of which args have been used + # args, kwargs: as passed in to vformat + def get_field(self, field_name, args, kwargs): + first, rest = field_name._formatter_field_name_split() + + obj = self.get_value(first, args, kwargs) + + # loop through the rest of the field_name, doing + # getattr or getitem as needed + for is_attr, i in rest: + if is_attr: + obj = getattr(obj, i) + else: + obj = obj[i] + + return obj, first diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index ddc5842..a5d1f92 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2012,6 +2012,101 @@ class TestSorted(unittest.TestCase): data = 'The quick Brown fox Jumped over The lazy Dog'.split() self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) + 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 + + # In 3.0, classes_classic has the same meaning as classes_new + def classes_classic(): + class A: + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromA(A): + pass + + class Simple: 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()) + class_test(*classes_classic()) + + 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.assert_(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) + + # 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_main(verbose=None): test_classes = (BuiltinTest, TestSorted) diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 21b8890..cb639b4 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -854,6 +854,32 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase): # A naive object replaces %z and %Z w/ empty strings. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") + def test_format(self): + dt = self.theclass(2007, 9, 10) + self.assertEqual(dt.__format__(''), str(dt)) + + # check that a derived class's __str__() gets called + class A(self.theclass): + def __str__(self): + return 'A' + a = A(2007, 9, 10) + self.assertEqual(a.__format__(''), 'A') + + # check that a derived class's strftime gets called + class B(self.theclass): + def strftime(self, format_spec): + return 'B' + b = B(2007, 9, 10) + self.assertEqual(b.__format__(''), str(dt)) + + for fmt in ["m:%m d:%d y:%y", + "m:%m d:%d y:%y H:%H M:%M S:%S", + "%z %Z", + ]: + self.assertEqual(dt.__format__(fmt), dt.strftime(fmt)) + self.assertEqual(a.__format__(fmt), dt.strftime(fmt)) + self.assertEqual(b.__format__(fmt), 'B') + def test_resolution_info(self): self.assert_(isinstance(self.theclass.min, self.theclass)) self.assert_(isinstance(self.theclass.max, self.theclass)) @@ -1136,6 +1162,32 @@ class TestDateTime(TestDate): # str is ISO format with the separator forced to a blank. self.assertEqual(str(t), "0002-03-02 00:00:00") + def test_format(self): + dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) + self.assertEqual(dt.__format__(''), str(dt)) + + # check that a derived class's __str__() gets called + class A(self.theclass): + def __str__(self): + return 'A' + a = A(2007, 9, 10, 4, 5, 1, 123) + self.assertEqual(a.__format__(''), 'A') + + # check that a derived class's strftime gets called + class B(self.theclass): + def strftime(self, format_spec): + return 'B' + b = B(2007, 9, 10, 4, 5, 1, 123) + self.assertEqual(b.__format__(''), str(dt)) + + for fmt in ["m:%m d:%d y:%y", + "m:%m d:%d y:%y H:%H M:%M S:%S", + "%z %Z", + ]: + self.assertEqual(dt.__format__(fmt), dt.strftime(fmt)) + self.assertEqual(a.__format__(fmt), dt.strftime(fmt)) + self.assertEqual(b.__format__(fmt), 'B') + def test_more_ctime(self): # Test fields that TestDate doesn't touch. import time @@ -1767,6 +1819,30 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase): # A naive object replaces %z and %Z with empty strings. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") + def test_format(self): + t = self.theclass(1, 2, 3, 4) + self.assertEqual(t.__format__(''), str(t)) + + # check that a derived class's __str__() gets called + class A(self.theclass): + def __str__(self): + return 'A' + a = A(1, 2, 3, 4) + self.assertEqual(a.__format__(''), 'A') + + # check that a derived class's strftime gets called + class B(self.theclass): + def strftime(self, format_spec): + return 'B' + b = B(1, 2, 3, 4) + self.assertEqual(b.__format__(''), str(t)) + + for fmt in ['%H %M %S', + ]: + self.assertEqual(t.__format__(fmt), t.strftime(fmt)) + self.assertEqual(a.__format__(fmt), t.strftime(fmt)) + self.assertEqual(b.__format__(fmt), 'B') + def test_str(self): self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004") self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000") diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 9dcfca1..94e9845 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -183,6 +183,7 @@ Instead, you can get the same information from the list type: '__delslice__', '__doc__', '__eq__', + '__format__', '__ge__', '__getattribute__', '__getitem__', 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) diff --git a/Lib/test/test_string.py b/Lib/test/test_string.py index 9b22333..5c20a2b 100644 --- a/Lib/test/test_string.py +++ b/Lib/test/test_string.py @@ -106,6 +106,92 @@ class ModuleTest(unittest.TestCase): self.assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi') self.assertEqual(string.capwords('ABC-def DEF-ghi GHI'), 'Abc-def Def-ghi Ghi') + def test_formatter(self): + fmt = string.Formatter() + self.assertEqual(fmt.format("foo"), "foo") + + self.assertEqual(fmt.format("foo{0}", "bar"), "foobar") + self.assertEqual(fmt.format("foo{1}{0}-{1}", "bar", 6), "foo6bar-6") + self.assertEqual(fmt.format("-{arg!r}-", arg='test'), "-'test'-") + + # override get_value ############################################ + class NamespaceFormatter(string.Formatter): + def __init__(self, namespace={}): + string.Formatter.__init__(self) + self.namespace = namespace + + def get_value(self, key, args, kwds): + if isinstance(key, str): + try: + # Check explicitly passed arguments first + return kwds[key] + except KeyError: + return self.namespace[key] + else: + string.Formatter.get_value(key, args, kwds) + + fmt = NamespaceFormatter({'greeting':'hello'}) + self.assertEqual(fmt.format("{greeting}, world!"), 'hello, world!') + + + # override format_field ######################################### + class CallFormatter(string.Formatter): + def format_field(self, value, format_spec): + return format(value(), format_spec) + + fmt = CallFormatter() + self.assertEqual(fmt.format('*{0}*', lambda : 'result'), '*result*') + + + # override convert_field ######################################## + class XFormatter(string.Formatter): + def convert_field(self, value, conversion): + if conversion == 'x': + return None + return super(XFormatter, self).convert_field(value, conversion) + + fmt = XFormatter() + self.assertEqual(fmt.format("{0!r}:{0!x}", 'foo', 'foo'), "'foo':None") + + + # override parse ################################################ + class BarFormatter(string.Formatter): + # returns an iterable that contains tuples of the form: + # (literal_text, field_name, format_spec, conversion) + def parse(self, format_string): + for field in format_string.split('|'): + if field[0] == '+': + # it's markup + field_name, _, format_spec = field[1:].partition(':') + yield '', field_name, format_spec, None + else: + yield field, None, None, None + + fmt = BarFormatter() + self.assertEqual(fmt.format('*|+0:^10s|*', 'foo'), '* foo *') + + # test all parameters used + class CheckAllUsedFormatter(string.Formatter): + def check_unused_args(self, used_args, args, kwargs): + # Track which arguments actuallly got used + unused_args = set(kwargs.keys()) + unused_args.update(range(0, len(args))) + + for arg in used_args: + unused_args.remove(arg) + + if unused_args: + raise ValueError("unused arguments") + + fmt = CheckAllUsedFormatter() + self.assertEqual(fmt.format("{0}", 10), "10") + self.assertEqual(fmt.format("{0}{i}", 10, i=100), "10100") + self.assertEqual(fmt.format("{0}{i}{1}", 10, 20, i=100), "1010020") + self.assertRaises(ValueError, fmt.format, "{0}{i}{1}", 10, 20, i=100, j=0) + self.assertRaises(ValueError, fmt.format, "{0}", 10, 20) + self.assertRaises(ValueError, fmt.format, "{0}", 10, 20, i=100) + self.assertRaises(ValueError, fmt.format, "{i}", 10, 20, i=100) + class BytesAliasTest(unittest.TestCase): def test_builtin(self): diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index bded0d7..ae1bfe1 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -266,6 +266,257 @@ class TypesTests(unittest.TestCase): except TypeError: pass else: self.fail("char buffer (at C level) not working") + def test_int__format__(self): + def test(i, format_spec, result): + # just make sure I'm not accidentally checking longs + assert type(i) == int + assert type(format_spec) == str + self.assertEqual(i.__format__(format_spec), result) + self.assertEqual(i.__format__(unicode(format_spec)), result) + + test(123456789, 'd', '123456789') + test(123456789, 'd', '123456789') + + test(1, 'c', '\01') + + # sign and aligning are interdependent + test(1, "-", '1') + test(-1, "-", '-1') + test(1, "-3", ' 1') + test(-1, "-3", ' -1') + test(1, "+3", ' +1') + test(-1, "+3", ' -1') + test(1, " 3", ' 1') + test(-1, " 3", ' -1') + test(1, " ", ' 1') + test(-1, " ", '-1') + + # hex + test(3, "x", "3") + test(3, "X", "3") + test(1234, "x", "4d2") + test(-1234, "x", "-4d2") + test(1234, "8x", " 4d2") + test(-1234, "8x", " -4d2") + test(1234, "x", "4d2") + test(-1234, "x", "-4d2") + test(-3, "x", "-3") + test(-3, "X", "-3") + test(int('be', 16), "x", "be") + test(int('be', 16), "X", "BE") + test(-int('be', 16), "x", "-be") + test(-int('be', 16), "X", "-BE") + + # octal + test(3, "o", "3") + test(-3, "o", "-3") + test(65, "o", "101") + test(-65, "o", "-101") + test(1234, "o", "2322") + test(-1234, "o", "-2322") + test(1234, "-o", "2322") + test(-1234, "-o", "-2322") + test(1234, " o", " 2322") + test(-1234, " o", "-2322") + test(1234, "+o", "+2322") + test(-1234, "+o", "-2322") + + # binary + test(3, "b", "11") + test(-3, "b", "-11") + test(1234, "b", "10011010010") + test(-1234, "b", "-10011010010") + test(1234, "-b", "10011010010") + test(-1234, "-b", "-10011010010") + test(1234, " b", " 10011010010") + test(-1234, " b", "-10011010010") + test(1234, "+b", "+10011010010") + test(-1234, "+b", "-10011010010") + + # make sure these are errors + + # precision disallowed + self.assertRaises(ValueError, 3 .__format__, "1.3") + # sign not allowed with 'c' + self.assertRaises(ValueError, 3 .__format__, "+c") + # format spec must be string + self.assertRaises(TypeError, 3 .__format__, None) + self.assertRaises(TypeError, 3 .__format__, 0) + + # ensure that only int and float type specifiers work + for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + + [chr(x) for x in range(ord('A'), ord('Z')+1)]): + if not format_spec in 'bcdoxXeEfFgGn%': + self.assertRaises(ValueError, 0 .__format__, format_spec) + self.assertRaises(ValueError, 1 .__format__, format_spec) + self.assertRaises(ValueError, (-1) .__format__, format_spec) + + # ensure that float type specifiers work; format converts + # the int to a float + for format_spec in 'eEfFgGn%': + for value in [0, 1, -1, 100, -100, 1234567890, -1234567890]: + self.assertEqual(value.__format__(format_spec), + float(value).__format__(format_spec)) + + def test_long__format__(self): + def test(i, format_spec, result): + # make sure we're not accidentally checking ints + assert type(i) == long + assert type(format_spec) == str + self.assertEqual(i.__format__(format_spec), result) + self.assertEqual(i.__format__(unicode(format_spec)), result) + + test(10**100, 'd', '1' + '0' * 100) + test(10**100+100, 'd', '1' + '0' * 97 + '100') + + test(123456789L, 'd', '123456789') + test(123456789L, 'd', '123456789') + + # sign and aligning are interdependent + test(1L, "-", '1') + test(-1L, "-", '-1') + test(1L, "-3", ' 1') + test(-1L, "-3", ' -1') + test(1L, "+3", ' +1') + test(-1L, "+3", ' -1') + test(1L, " 3", ' 1') + test(-1L, " 3", ' -1') + test(1L, " ", ' 1') + test(-1L, " ", '-1') + + test(1L, 'c', '\01') + + # hex + test(3L, "x", "3") + test(3L, "X", "3") + test(1234L, "x", "4d2") + test(-1234L, "x", "-4d2") + test(1234L, "8x", " 4d2") + test(-1234L, "8x", " -4d2") + test(1234L, "x", "4d2") + test(-1234L, "x", "-4d2") + test(-3L, "x", "-3") + test(-3L, "X", "-3") + test(long('be', 16), "x", "be") + test(long('be', 16), "X", "BE") + test(-long('be', 16), "x", "-be") + test(-long('be', 16), "X", "-BE") + + # octal + test(3L, "o", "3") + test(-3L, "o", "-3") + test(65L, "o", "101") + test(-65L, "o", "-101") + test(1234L, "o", "2322") + test(-1234L, "o", "-2322") + test(1234L, "-o", "2322") + test(-1234L, "-o", "-2322") + test(1234L, " o", " 2322") + test(-1234L, " o", "-2322") + test(1234L, "+o", "+2322") + test(-1234L, "+o", "-2322") + + # binary + test(3L, "b", "11") + test(-3L, "b", "-11") + test(1234L, "b", "10011010010") + test(-1234L, "b", "-10011010010") + test(1234L, "-b", "10011010010") + test(-1234L, "-b", "-10011010010") + test(1234L, " b", " 10011010010") + test(-1234L, " b", "-10011010010") + test(1234L, "+b", "+10011010010") + test(-1234L, "+b", "-10011010010") + + # make sure these are errors + + # precision disallowed + self.assertRaises(ValueError, 3L .__format__, "1.3") + # sign not allowed with 'c' + self.assertRaises(ValueError, 3L .__format__, "+c") + # format spec must be string + self.assertRaises(TypeError, 3L .__format__, None) + self.assertRaises(TypeError, 3L .__format__, 0) + + # ensure that only int and float type specifiers work + for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + + [chr(x) for x in range(ord('A'), ord('Z')+1)]): + if not format_spec in 'bcdoxXeEfFgGn%': + self.assertRaises(ValueError, 0L .__format__, format_spec) + self.assertRaises(ValueError, 1L .__format__, format_spec) + self.assertRaises(ValueError, (-1L) .__format__, format_spec) + + # ensure that float type specifiers work; format converts + # the long to a float + for format_spec in 'eEfFgGn%': + for value in [0L, 1L, -1L, 100L, -100L, 1234567890L, -1234567890L]: + self.assertEqual(value.__format__(format_spec), + float(value).__format__(format_spec)) + + def test_float__format__(self): + # these should be rewritten to use both format(x, spec) and + # x.__format__(spec) + + def test(f, format_spec, result): + assert type(f) == float + assert type(format_spec) == str + self.assertEqual(f.__format__(format_spec), result) + self.assertEqual(f.__format__(unicode(format_spec)), result) + + test(0.0, 'f', '0.000000') + + # the default is 'g', except for empty format spec + test(0.0, '', '0.0') + test(0.01, '', '0.01') + test(0.01, 'g', '0.01') + + test( 1.0, ' g', ' 1') + test(-1.0, ' g', '-1') + test( 1.0, '+g', '+1') + test(-1.0, '+g', '-1') + test(1.1234e200, 'g', '1.1234e+200') + test(1.1234e200, 'G', '1.1234E+200') + + + test(1.0, 'f', '1.000000') + + test(-1.0, 'f', '-1.000000') + + test( 1.0, ' f', ' 1.000000') + test(-1.0, ' f', '-1.000000') + test( 1.0, '+f', '+1.000000') + test(-1.0, '+f', '-1.000000') + test(1.1234e200, 'f', '1.1234e+200') + test(1.1234e200, 'F', '1.1234e+200') + + test( 1.0, 'e', '1.000000e+00') + test(-1.0, 'e', '-1.000000e+00') + test( 1.0, 'E', '1.000000E+00') + test(-1.0, 'E', '-1.000000E+00') + test(1.1234e20, 'e', '1.123400e+20') + test(1.1234e20, 'E', '1.123400E+20') + + # % formatting + test(-1.0, '%', '-100.000000%') + + # format spec must be string + self.assertRaises(TypeError, 3.0.__format__, None) + self.assertRaises(TypeError, 3.0.__format__, 0) + + # other format specifiers shouldn't work on floats, + # in particular int specifiers + for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + + [chr(x) for x in range(ord('A'), ord('Z')+1)]): + if not format_spec in 'eEfFgGn%': + self.assertRaises(ValueError, format, 0.0, format_spec) + self.assertRaises(ValueError, format, 1.0, format_spec) + self.assertRaises(ValueError, format, -1.0, format_spec) + self.assertRaises(ValueError, format, 1e100, format_spec) + self.assertRaises(ValueError, format, -1e100, format_spec) + self.assertRaises(ValueError, format, 1e-100, format_spec) + self.assertRaises(ValueError, format, -1e-100, format_spec) + + def test_main(): run_unittest(TypesTests) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index ef6a722..26a57cc 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -825,6 +825,268 @@ class UnicodeTest( return self.assertRaises(OverflowError, u'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 + u's'), expected) + + test(u'', u'', u'') + test(u'abc', u'', u'abc') + test(u'abc', u'.3', u'abc') + test(u'ab', u'.3', u'ab') + test(u'abcdef', u'.3', u'abc') + test(u'abcdef', u'.0', u'') + test(u'abc', u'3.3', u'abc') + test(u'abc', u'2.3', u'abc') + test(u'abc', u'2.2', u'ab') + test(u'abc', u'3.2', u'ab ') + test(u'result', u'x<0', u'result') + test(u'result', u'x<5', u'result') + test(u'result', u'x<6', u'result') + test(u'result', u'x<7', u'resultx') + test(u'result', u'x<8', u'resultxx') + test(u'result', u' <7', u'result ') + test(u'result', u'<7', u'result ') + test(u'result', u'>7', u' result') + test(u'result', u'>8', u' result') + test(u'result', u'^8', u' result ') + test(u'result', u'^9', u' result ') + test(u'result', u'^10', u' result ') + test(u'a', u'10000', u'a' + u' ' * 9999) + test(u'', u'10000', u' ' * 10000) + test(u'', u'10000000', u' ' * 10000000) + + # test mixing unicode and str + self.assertEqual(u'abc'.__format__('s'), u'abc') + self.assertEqual(u'abc'.__format__('->10s'), u'-------abc') + + def test_format(self): + self.assertEqual(u''.format(), u'') + self.assertEqual(u'a'.format(), u'a') + self.assertEqual(u'ab'.format(), u'ab') + self.assertEqual(u'a{{'.format(), u'a{') + self.assertEqual(u'a}}'.format(), u'a}') + self.assertEqual(u'{{b'.format(), u'{b') + self.assertEqual(u'}}b'.format(), u'}b') + self.assertEqual(u'a{{b'.format(), u'a{b') + + # examples from the PEP: + import datetime + self.assertEqual(u"My name is {0}".format(u'Fred'), u"My name is Fred") + self.assertEqual(u"My name is {0[name]}".format(dict(name=u'Fred')), + u"My name is Fred") + self.assertEqual(u"My name is {0} :-{{}}".format(u'Fred'), + u"My name is Fred :-{}") + + # datetime.__format__ doesn't work with unicode + #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 u'E(' + self.x + u')' + + # class with __repr__, but no __format__ or __str__ + class F: + def __init__(self, x): + self.x = x + def __repr__(self): + return u'F(' + self.x + u')' + + # 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 u"string is " + self.x + def __format__(self, format_spec): + if format_spec == 'd': + return u'G(' + self.x + u')' + 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(u''.format(), u'') + self.assertEqual(u'abc'.format(), u'abc') + self.assertEqual(u'{0}'.format(u'abc'), u'abc') + self.assertEqual(u'{0:}'.format(u'abc'), u'abc') + self.assertEqual(u'X{0}'.format(u'abc'), u'Xabc') + self.assertEqual(u'{0}X'.format(u'abc'), u'abcX') + self.assertEqual(u'X{0}Y'.format(u'abc'), u'XabcY') + self.assertEqual(u'{1}'.format(1, u'abc'), u'abc') + self.assertEqual(u'X{1}'.format(1, u'abc'), u'Xabc') + self.assertEqual(u'{1}X'.format(1, u'abc'), u'abcX') + self.assertEqual(u'X{1}Y'.format(1, u'abc'), u'XabcY') + self.assertEqual(u'{0}'.format(-15), u'-15') + self.assertEqual(u'{0}{1}'.format(-15, u'abc'), u'-15abc') + self.assertEqual(u'{0}X{1}'.format(-15, u'abc'), u'-15Xabc') + self.assertEqual(u'{{'.format(), u'{') + self.assertEqual(u'}}'.format(), u'}') + self.assertEqual(u'{{}}'.format(), u'{}') + self.assertEqual(u'{{x}}'.format(), u'{x}') + self.assertEqual(u'{{{0}}}'.format(123), u'{123}') + self.assertEqual(u'{{{{0}}}}'.format(), u'{{0}}') + self.assertEqual(u'}}{{'.format(), u'}{') + self.assertEqual(u'}}x{{'.format(), u'}x{') + + # weird field names + self.assertEqual(u"{0[foo-bar]}".format({u'foo-bar':u'baz'}), u'baz') + self.assertEqual(u"{0[foo bar]}".format({u'foo bar':u'baz'}), u'baz') + self.assertEqual(u"{0[ ]}".format({u' ':3}), u'3') + + self.assertEqual(u'{foo._x}'.format(foo=C(20)), u'20') + self.assertEqual(u'{1}{0}'.format(D(10), D(20)), u'2010') + self.assertEqual(u'{0._x.x}'.format(C(D(u'abc'))), u'abc') + self.assertEqual(u'{0[0]}'.format([u'abc', u'def']), u'abc') + self.assertEqual(u'{0[1]}'.format([u'abc', u'def']), u'def') + self.assertEqual(u'{0[1][0]}'.format([u'abc', [u'def']]), u'def') + self.assertEqual(u'{0[1][0].x}'.format(['abc', [D(u'def')]]), u'def') + + # strings + self.assertEqual(u'{0:.3s}'.format(u'abc'), u'abc') + self.assertEqual(u'{0:.3s}'.format(u'ab'), u'ab') + self.assertEqual(u'{0:.3s}'.format(u'abcdef'), u'abc') + self.assertEqual(u'{0:.0s}'.format(u'abcdef'), u'') + self.assertEqual(u'{0:3.3s}'.format(u'abc'), u'abc') + self.assertEqual(u'{0:2.3s}'.format(u'abc'), u'abc') + self.assertEqual(u'{0:2.2s}'.format(u'abc'), u'ab') + self.assertEqual(u'{0:3.2s}'.format(u'abc'), u'ab ') + self.assertEqual(u'{0:x<0s}'.format(u'result'), u'result') + self.assertEqual(u'{0:x<5s}'.format(u'result'), u'result') + self.assertEqual(u'{0:x<6s}'.format(u'result'), u'result') + self.assertEqual(u'{0:x<7s}'.format(u'result'), u'resultx') + self.assertEqual(u'{0:x<8s}'.format(u'result'), u'resultxx') + self.assertEqual(u'{0: <7s}'.format(u'result'), u'result ') + self.assertEqual(u'{0:<7s}'.format(u'result'), u'result ') + self.assertEqual(u'{0:>7s}'.format(u'result'), u' result') + self.assertEqual(u'{0:>8s}'.format(u'result'), u' result') + self.assertEqual(u'{0:^8s}'.format(u'result'), u' result ') + self.assertEqual(u'{0:^9s}'.format(u'result'), u' result ') + self.assertEqual(u'{0:^10s}'.format(u'result'), u' result ') + self.assertEqual(u'{0:10000}'.format(u'a'), u'a' + u' ' * 9999) + self.assertEqual(u'{0:10000}'.format(u''), u' ' * 10000) + self.assertEqual(u'{0:10000000}'.format(u''), u' ' * 10000000) + + # format specifiers for user defined type + self.assertEqual(u'{0:abc}'.format(C()), u'abc') + + # !r and !s coersions + self.assertEqual(u'{0!s}'.format(u'Hello'), u'Hello') + self.assertEqual(u'{0!s:}'.format(u'Hello'), u'Hello') + self.assertEqual(u'{0!s:15}'.format(u'Hello'), u'Hello ') + self.assertEqual(u'{0!s:15s}'.format(u'Hello'), u'Hello ') + self.assertEqual(u'{0!r}'.format(u'Hello'), u"u'Hello'") + self.assertEqual(u'{0!r:}'.format(u'Hello'), u"u'Hello'") + self.assertEqual(u'{0!r}'.format(F(u'Hello')), u'F(Hello)') + + # test fallback to object.__format__ + self.assertEqual(u'{0}'.format({}), u'{}') + self.assertEqual(u'{0}'.format([]), u'[]') + self.assertEqual(u'{0}'.format([1]), u'[1]') + self.assertEqual(u'{0}'.format(E(u'data')), u'E(data)') + self.assertEqual(u'{0:^10}'.format(E(u'data')), u' E(data) ') + self.assertEqual(u'{0:^10s}'.format(E(u'data')), u' E(data) ') + self.assertEqual(u'{0:d}'.format(G(u'data')), u'G(data)') + self.assertEqual(u'{0:>15s}'.format(G(u'data')), u' string is data') + self.assertEqual(u'{0!s}'.format(G(u'data')), u'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(__name__) |