import unittest
from test import test_support

import locale

import pep3101

# Variables used for testing name mapper
global1 = 10
global2 = 20
global3 = 30

# The test implementation does not allow an argument
# index or keyword name to be used more than once. The
# PEP doesn't make any specification on this, but it
# seems too useful to leave as is.


class FormatTest(unittest.TestCase):
    # All tests run through these functions. They can be
    # overridden to change the class of string being tested
    # and the function being used.
    def formatEquals(self, result, text, *args, **kwargs):
        val = pep3101.format(text, *args, **kwargs)
        self.assertEquals(val, result)

    def formatEqualsWithUnicode(self, result, text, *args, **kwargs):
        val = pep3101.format(text, *args, **kwargs)
        self.assertEquals(val, result)

        # a quick check for unicode version
        val = pep3101.format(unicode(text), *args, **kwargs)
        self.assertEquals(val, unicode(result))

    def formatEqualsWithUnicodeUC(self, result, text, *args, **kwargs):
        # test both the upper and lowercase versions.  assume the
        # result and text come in as lowercase

        val = pep3101.format(text, *args, **kwargs)
        self.assertEquals(val, result)

        # a quick check for unicode version
        val = pep3101.format(unicode(text), *args, **kwargs)
        self.assertEquals(val, unicode(result))

        # test the uppercase text version
        val = pep3101.format(text.upper(), *args, **kwargs)
        self.assertEquals(val, result.upper())

        # test the uppercase unicode version
        val = pep3101.format(unicode(text.upper()), *args, **kwargs)
        self.assertEquals(val, unicode(result.upper()))

    def formatRaises(self, exc, text, *args, **kwargs):
        self.assertRaises(exc, pep3101.format, text, *args, **kwargs)

    def test_basic(self):
        # directly from the pep intro
        self.formatEquals(
           "My name is Fred",
           "My name is {0}", "Fred")
        self.formatEquals(
           "My name is Fred :-{}",
           "My name is {0} :-{{}}", "Fred")
        self.formatEquals("abc", "{0:}", "abc")  # is this allowed?

    def test_no_substitutions(self):
        self.formatEquals("", "")
        self.formatEquals("how now brown cow", "how now brown cow")

    def test_missingargs(self):
        #self.formatRaises(None, "Doesn't use all {0} args", 42, 24)
        self.formatRaises(ValueError, "There is no {4} arg", 42, 24)
        self.formatRaises(ValueError, "There question is {when}", who=True)

    def test_attributes(self):
        class Container(object):
            one, _two, four4 = 1, 2, 4
            def __getattr__(self, name):
                if name == "five": return 5
                raise TypeError("Never do this")
        self.formatEquals(
           "Count with me; 1 4",
           "Count with me; {0.one} {1.four4}",
           Container, Container, item=Container)
        self.formatRaises(ValueError,
           "Count with me; {0.one} {item._two} {1.four4}",
           Container, Container, item=Container)
        self.formatEquals(
           "Count with me; 1 2 4",
           "Count with me; {0.one} {item._two} {1.four4}",
           Container, Container, item=Container, _allow_leading_underscores=1)
        self.formatEquals(
           "Five is 5", "Five is {c.five}", c=Container())
        self.formatRaises(AttributeError,
           "Missing {0.rabbit} lookup", Container)
        self.formatRaises(TypeError,
           "Forbidden {0.secret} lookup", Container())

    def test_items(self):
        d = dict(val="value", sum=1)
        t = tuple(("apple", "ball", "cat"))
        self.formatEquals(
           "The value of apple",
           "The {0[val]} of {t[0]}", d, t=t)
        # Decided against negative indices for now
        #self.formatEquals(
        #    "The shiny red ball",
        #    "The shiny red {0[-2]}", t)

    def test_formatlookup(self):
        self.formatEquals("0032", "{0:{1}}", 32, "0>4d")
        self.formatEquals("**32", "{0:{1}{2}4{3}}", 32, "*", ">", "d")

    def test_specifiers(self):
        self.formatEquals("a", "{0:c}", ord("a"))
        self.formatEquals("00001000", "{0:08b}", 8)
        self.formatEquals("  8", "{0: >3d}", 8)
        self.formatEquals("15%", "{0:.0%}", .1515)

    def test_string_specifiers(self):
        self.formatEqualsWithUnicode("abc", "{0:.3s}", "abc")

        self.formatEqualsWithUnicode("ab", "{0:.3s}", "ab")

        self.formatEqualsWithUnicode("abc", "{0:.3s}", "abcdef")
        self.formatEqualsWithUnicode("resultx", "{0:x<7s}", "result")
        self.formatEqualsWithUnicode("resultxx", "{0:x<8s}", "result")
        self.formatEqualsWithUnicode("result ", "{0: <7s}", "result")
        self.formatEqualsWithUnicode("result ", "{0:<7s}", "result")
        self.formatEqualsWithUnicode(" result", "{0:>7s}", "result")
        self.formatEqualsWithUnicode("  result", "{0:>8s}", "result")
        self.formatEqualsWithUnicode("result  ", "{0:=8s}", "result")
        self.formatEqualsWithUnicode(" result ", "{0:^8s}", "result")
        self.formatEqualsWithUnicode(" result  ", "{0:^9s}", "result")
        self.formatEqualsWithUnicode("  result  ", "{0:^10s}", "result")

    def test_repr_specifiers(self):
        self.formatEqualsWithUnicode("3", "{0:r}", 3)
        self.formatEqualsWithUnicode("3.141", "{0:5r}", 3.141592654)

        # I'm not sure this is a good test, since the quoting might change
        self.formatEqualsWithUnicode("'abcdefg'", "{0:r}", "abcdefg")
        self.formatEqualsWithUnicode("'abcdefg", "{0:8r}", "abcdefg")

    def test_decimal_specifiers(self):
        self.formatRaises(TypeError, "{0:d}", "non-number")

        self.formatEqualsWithUnicode("0", "{0:d}", 0)
        self.formatEqualsWithUnicode("0", "{0:d}", long(0))
        self.formatEqualsWithUnicode("123", "{0:d}", 123)
        self.formatEqualsWithUnicode("-123", "{0:d}", -123)
        self.formatEqualsWithUnicode("+123", "{0:+d}", 123)
        self.formatEqualsWithUnicode("-123", "{0:+d}", -123)
        self.formatEqualsWithUnicode("123", "{0:-d}", 123)
        self.formatEqualsWithUnicode("-123", "{0:-d}", -123)
        self.formatEqualsWithUnicode("123", "{0:()d}", 123)
        self.formatEqualsWithUnicode("(123)", "{0:()d}", -123)

        # need a long padding to force a reallocation (and hopefully a
        # memory move) in 'd' handling
        self.formatEqualsWithUnicode(" " * 997 + "100", "{0:1000d}", 100)

        # now test with the 4 kinds of aligning

        # first, without a specified width
        self.formatEqualsWithUnicode("1", "{0:<d}", 1)
        self.formatEqualsWithUnicode("1", "{0:>d}", 1)
        self.formatEqualsWithUnicode("1", "{0:=d}", 1)
        self.formatEqualsWithUnicode("1", "{0:^d}", 1)
        self.formatEqualsWithUnicode("-1", "{0:<d}", -1)
        self.formatEqualsWithUnicode("-1", "{0:>d}", -1)
        self.formatEqualsWithUnicode("-1", "{0:=d}", -1)
        self.formatEqualsWithUnicode("-1", "{0:^d}", -1)

        self.formatEqualsWithUnicode("0         ", "{0:<10d}", 0)
        self.formatEqualsWithUnicode("123       ", "{0:<10d}", 123)
        self.formatEqualsWithUnicode("-123      ", "{0:<10d}", -123)
        self.formatEqualsWithUnicode("       123", "{0:>10d}", 123)
        self.formatEqualsWithUnicode("      -123", "{0:>10d}", -123)
        self.formatEqualsWithUnicode("   123    ", "{0:^10d}", 123)
        self.formatEqualsWithUnicode("   -123   ", "{0:^10d}", -123)
        self.formatEqualsWithUnicode("       123", "{0:=10d}", 123)
        self.formatEqualsWithUnicode("+      123", "{0:=+10d}", 123)
        self.formatEqualsWithUnicode("-      123", "{0:=10d}", -123)
        self.formatEqualsWithUnicode("-      123", "{0:=+10d}", -123)
        self.formatEqualsWithUnicode("       123", "{0:=()10d}", 123)

        # XXX I'm not sure this is correct, maybe it should be "     (123)"
        self.formatEqualsWithUnicode("(     123)", "{0:=()10d}", -123)
        self.formatEqualsWithUnicode("     (123)", "{0:>()10d}", -123)
        self.formatEqualsWithUnicode("(123)     ", "{0:<()10d}", -123)
        self.formatEqualsWithUnicode("  (123)   ", "{0:^()10d}", -123)
        self.formatEqualsWithUnicode("(     123)", "{0:=()10d}", long(-123))
        self.formatEqualsWithUnicode("     (123)", "{0:>()10d}", long(-123))
        self.formatEqualsWithUnicode("(123)     ", "{0:<()10d}", long(-123))
        self.formatEqualsWithUnicode("  (123)   ", "{0:^()10d}", long(-123))

        self.formatEqualsWithUnicode("1" + "0" * 100, "{0:d}", 10**100)
        self.formatEqualsWithUnicode("-1" + "0" * 100, "{0:d}", -10**100)
        self.formatEqualsWithUnicode("+1" + "0" * 100, "{0:+d}", 10**100)
        self.formatEqualsWithUnicode("(1" + "0" * 100 + ")", "{0:()d}", -10**100)
        self.formatEqualsWithUnicode("(       1" + "0" * 100 + ")", "{0:()110d}", -10**100)
        self.formatEqualsWithUnicode("(       1" + "0" * 100 + ")", "{0:()110d}", -10**100)

        for base in (0, 10**10, 10**100):
            for i in range(base+1, base+2000):
                self.formatEqualsWithUnicode(str(i), "{0:d}", i)
                self.formatEqualsWithUnicode("(" + str(i) + ")", "{0:()d}", -i)

    def test_octal_specifiers(self):
        n = int("31415", 8)

        self.formatRaises(TypeError, "{0:o}", "non-number")

        self.formatEqualsWithUnicode("0", "{0:o}", 0)
        self.formatEqualsWithUnicode("31415", "{0:o}", n)
        self.formatEqualsWithUnicode("-31415", "{0:o}", -n)
        self.formatEqualsWithUnicode(" " * 995 + "31415", "{0:1000o}", n)

        n = int("314153141531415", 8)
        self.formatEqualsWithUnicode("314153141531415", "{0:o}", n)
        self.formatEqualsWithUnicode("-314153141531415", "{0:o}", -n)

    def test_hex_specifiers(self):
        n = int("beef", 16)

        self.formatRaises(TypeError, "{0:x}", "non-number")

        self.formatEqualsWithUnicodeUC("0", "{0:x}", 0)
        self.formatEqualsWithUnicodeUC("0", "{0:x}", long(0))
        self.formatEqualsWithUnicodeUC("beef", "{0:x}", n)
        self.formatEqualsWithUnicodeUC("-beef", "{0:x}", -n)

        n = int("deadbeef", 16)
        self.formatEqualsWithUnicodeUC("deadbeef", "{0:x}", n)
        self.formatEqualsWithUnicodeUC("-deadbeef", "{0:x}", -n)
        self.formatEqualsWithUnicodeUC("(deadbeef)", "{0:()x}", -n)
        self.formatEqualsWithUnicodeUC("( deadbeef)", "{0:()11x}", -n)
        self.formatEqualsWithUnicodeUC(" (deadbeef)", "{0:>()11x}", -n)
        self.formatEqualsWithUnicodeUC(" (deadbeef)", "{0:>()11x}", long(-n))
        self.formatEqualsWithUnicodeUC("(deadbeef) ", "{0:<()11x}", -n)
        self.formatEqualsWithUnicodeUC("(deadbeef) ", "{0:<()11x}", long(-n))

    def test_char_specifiers(self):
        self.formatEqualsWithUnicode("A", "{0:c}", "A")
        self.formatEqualsWithUnicode("8", "{0:c}", "8")
        self.formatEqualsWithUnicode(";", "{0:c}", ";")
        self.formatEqualsWithUnicode(";", "{0:c}", ord(";"))
        self.formatEqualsWithUnicode(";", "{0:c}", long(ord(";")))
        self.formatEquals(u"f", u"{0:c}", u"f")

        self.formatRaises(TypeError, "{0:c}", "abcd")

        # XXX not sure why this doesn't raise
        #self.formatRaises(TypeError, "{0:c}", [1, 2, 3])

        # XXX not sure why this doesn't raise
        #self.formatRaises(TypeError, "{0:c}", 1j)

        # XXX this should raise, but instead gives a DeprecationWarning
        #self.formatRaises(TypeError, "{0:c}", 3.14)

    def test_exponent_specifiers(self):
        self.formatRaises(TypeError, "{0:e}", "a string")
        self.formatRaises(TypeError, "{0:E}", "a string")
        self.formatRaises(TypeError, "{0:e}", 1j)
        self.formatRaises(TypeError, "{0:E}", 1j)
        self.formatEqualsWithUnicodeUC("3.141500e+00", "{0:e}", 3.1415)
        self.formatEqualsWithUnicodeUC("3.1415000000e+00", "{0:.10e}", 3.1415)

    def test_fixed_specifiers(self):
        self.formatRaises(TypeError, "{0:f}", "a string")
        self.formatRaises(TypeError, "{0:F}", "a string")
        self.formatRaises(TypeError, "{0:f}", 1j)
        self.formatRaises(TypeError, "{0:F}", 1j)
        self.formatEqualsWithUnicode("3.141500", "{0:f}", 3.1415)
        self.formatEqualsWithUnicode("3.1415000000", "{0:.10f}", 3.1415)
        self.formatEqualsWithUnicode("3.1415e+200", "{0:f}", 3.1415e200)
        self.formatEqualsWithUnicode("3.1415e+200", "{0:F}", 3.1415e200)

    def test_general_specifiers(self):
        self.formatRaises(TypeError, "{0:g}", "a string")
        self.formatRaises(TypeError, "{0:G}", "a string")
        self.formatRaises(TypeError, "{0:g}", 1j)
        self.formatRaises(TypeError, "{0:G}", 1j)
        self.formatEqualsWithUnicodeUC("3.1415", "{0:g}", 3.1415)
        self.formatEqualsWithUnicodeUC("3.1415", "{0:.10g}", 3.1415)
        self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200)
        self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200)

    def test_percent_specifiers(self):
        self.formatEqualsWithUnicode("314.15%", "{0:.2%}", 3.1415)
        self.formatEqualsWithUnicode("3.14e+202%", "{0:.3%}", 3.1415e200)

    def test_binary_specifiers(self):
        self.formatRaises(TypeError, "{0:b}", "string")

        self.formatEqualsWithUnicode("0", "{0:b}", 0)
        self.formatEqualsWithUnicode("0", "{0:b}", long(0))
        self.formatEqualsWithUnicode("1", "{0:b}", 1)
        self.formatEqualsWithUnicode("1", "{0:b}", long(1))
        self.formatEqualsWithUnicode("-1", "{0:b}", -1)
        self.formatEqualsWithUnicode("-1", "{0:b}", long(-1))
        self.formatEqualsWithUnicode("0         ", "{0:<10b}", 0)
        self.formatEqualsWithUnicode("         0", "{0:>10b}", 0)
        self.formatEqualsWithUnicode("1001      ", "{0:<10b}", 9)
        self.formatEqualsWithUnicode("      1001", "{0:>10b}", 9)
        self.formatEqualsWithUnicode("   1001   ", "{0:^10b}", 9)
        self.formatEqualsWithUnicode("1" + "0" * 100, "{0:b}", 2**100)
        self.formatEqualsWithUnicode("-1" + "0" * 100, "{0:b}", -2**100)
        self.formatEqualsWithUnicode("1" + "0" * 98 + "11", "{0:b}", 2**100 + 3)
        self.formatEqualsWithUnicode("1" * 100, "{0:b}", 2**100 - 1)
        self.formatEqualsWithUnicode("-" + "1" * 100, "{0:b}", -(2**100 - 1))
        self.formatEqualsWithUnicode("(" + "1" * 100 + ")", "{0:()b}", -(2**100 - 1))
        self.formatEqualsWithUnicode("(" + " " * 98 + "1" * 100 + ")", "{0:=()200b}", -(2**100 - 1))
        for n in range(1, 1000):
            # build an n bit integer, alternating ones and zeros
            # as n gets large, these will become longs
            s = "10" * (n / 2) + (n % 2 and "1" or "")
            i = int(s, 2)
            self.formatEqualsWithUnicode(s, "{0:b}", i)

            # make sure negative also works, hoping to catch an
            # overflow off the end of the digits.  an internal
            # implementation detail is that the parens are inserted
            # into the output buffer before the digits are, so any
            # error in writing the digits might write over the parens
            self.formatEqualsWithUnicode("(" + s + ")", "{0:()b}", -i)

        def test_from_str(s):
            n = int(s, 2)
            self.formatEqualsWithUnicode(s, "{0:b}", n)
            self.formatEqualsWithUnicode("-" + s, "{0:b}", -n)
            self.formatEqualsWithUnicode("(" + s + ")", "{0:()b}", -n)

        # test the 2's compliment algorithm
        # it has a special test for all ones inside a byte, so test
        # byte aligned strings of ones
        test_from_str("1" * 8)
        test_from_str("1" + "1" * 8)
        test_from_str("10" + "1" * 8)
        test_from_str("1" * 8 + "0" * 8)
        test_from_str("1" + "1" * 8 + "0" * 8)
        test_from_str("10" + "1" * 8 + "0" * 8)
        test_from_str("1" * 8 + "0" * 8 + "1" * 8)
        test_from_str("1" + "1" * 8 + "0" * 8 + "1" * 8)
        test_from_str("10" + "1" * 8 + "0" * 8 + "1" * 8)
        test_from_str("1" * 16)
        test_from_str("1" + "1" * 16)
        test_from_str("10" + "1" * 16)
        test_from_str("1" * 16 + "0" * 8)
        test_from_str("1" + "1" * 16 + "0" * 8)
        test_from_str("10" + "1" * 16 + "0" * 8)


    def test_number_specifier(self):
        def test(value):
            self.formatEqualsWithUnicode(locale.format("%f", value), "{0:n}", value)

        test(0)
        test(1)
        test(10**100)

    def test_missing_type_specifier(self):
        # make sure floats use 'g', ints and longs 'd', and everything else 's'

        self.formatEqualsWithUnicode("3.5", "{0}", 3.5)
        self.formatEqualsWithUnicode("5e+99", "{0}", 0.5e100)
        self.formatEqualsWithUnicode("5e-101", "{0}", 0.5e-100)
        self.formatEqualsWithUnicode("0", "{0}", 0)
        self.formatEqualsWithUnicode("1", "{0}", 1)
        self.formatEqualsWithUnicode("-1", "{0}", -1)
        self.formatEqualsWithUnicode("(1)", "{0:()}", -1)
        self.formatEqualsWithUnicode("1" + "0" * 100, "{0}", 10**100)
        self.formatEqualsWithUnicode("hello, world", "{0}", "hello, world")
        self.formatEqualsWithUnicode("        hello, world", "{0:>20}", "hello, world")

    def test_custom_format(self):
        class Custom(object):
            def __format__(self, specifiers):
                return specifiers
        custom = Custom()
        self.formatEquals("magic", "{0:magic}", custom)
        self.formatEquals("custom", "{0:{1}}", custom, "custom")

    def test_syntaxerror(self):
        self.formatRaises(ValueError, "}{", True)
        self.formatRaises(ValueError, "{0", True)
        self.formatRaises(ValueError, "{0.[]}", True)
        self.formatRaises(ValueError, "{0[0}", [1])
        self.formatRaises(ValueError, "{0[0:foo}", [1])
        self.formatRaises(ValueError, "{c]}", True)
        self.formatRaises(ValueError, "{{ {{{0}}", True)
        self.formatRaises(ValueError, "{0}}", True)

    def test_invalid_type_specifier(self):
        self.formatRaises(ValueError, "{0::}", 0)
        self.formatRaises(ValueError, "{0:&}", 0)
        self.formatRaises(ValueError, "{0:k}", 0)
        self.formatRaises(ValueError, "{0:4<10.20K}", 0)

    def test_derive_from_long(self):
        # make sure classes that inherit from long and don't specify
        # __format__ get long's default format
        class SimpleLong(long):
            pass

        l = SimpleLong(2**100)
        self.formatEquals("1" + "0" * 100, "{0:b}", l)

        # make sure that classes that inherit from long and do specify
        # __format__ get it called
        class FormatLong(long):
            def __format__(self, specifiers):
                return "how now brown cow"

        l = FormatLong(2**100)
        self.formatEquals("how now brown cow", "{0:b}", l)

    def test_name_mapper(self):
        mydict = dict(foo=1, bar=2)
        dict2 = mydict, dict(foobar=3)
        foo = 27
        global3 = 50
        self.formatRaises(ValueError, "{foo}")
        self.formatRaises(ValueError, "{foo} {foobar}", _dict=mydict)
        self.formatEquals("1", "{foo}", _dict=mydict)
        self.formatEquals("1 2", "{foo} {bar}", _dict=mydict)
        self.formatRaises(ValueError, "{foo} {bar} {global3}", _dict=mydict)
        self.formatEquals("1 2 3", "{foo} {bar} {foobar}", _dict=dict2)
        self.formatEquals("27 2", "{foo} {bar}", _dict=mydict, foo=foo)
        self.assertEquals(
            pep3101.format("{foo} {global3} {global1}"),
            "27 50 10")

    def test_check_unused(self):
        mydict = dict(foo=1, foo2=1)
        bar = 3
        bar2 = 4
        result = ' a b 1 3'
        s = ' {0} {1} {foo} {bar}'
        s2 = '{!useall}' + s
        s3 = '{!useall}\r\n' + s
        s4 = '{!useall}\n' + s
        s5 = '{!useall}\r\n' + s
        self.formatEquals(result, s, 'a', 'b', bar=bar, _dict=mydict)
        self.formatEquals(result, s2, 'a', 'b', bar=bar, _dict=mydict)
        self.formatEquals(result, s3, 'a', 'b', bar=bar, _dict=mydict)
        self.formatEquals(result, s4, 'a', 'b', bar=bar, _dict=mydict)
        self.formatEquals(result, s5, 'a', 'b', bar=bar, _dict=mydict)
        self.formatEquals(result, s, 'a', 'b', 3, bar=bar, _dict=mydict)
        self.formatRaises(ValueError, s2, 'a', 'b', 3, bar=bar, _dict=mydict)
        self.formatEquals(result, s, 'a', 'b', bar=bar, _dict=mydict, sam=27)
        self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, _dict=mydict, sam=27)
        self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1)
        self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1)
        self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1, sam=27)
        self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, foo=1, sam=27)
        self.formatRaises(ValueError, '{!useall}', 1)
        self.formatRaises(ValueError, '{!useall}', foo=1)

    def test_comments(self):
        self.formatEquals('Hi there','''{#Comment}Hi{#
           This is a comment}{#Another} th{#comment}ere{#}''')

    def test_format_hook(self):
        def hookfunc(obj, spec):
            if obj > 100:
                return None
            return '_%s_' % (obj, spec)[obj==3]
        self.formatEquals('_a_ _4_       123',
                          '{!hook}{0:a} {1:b}{2:>10d}',
                          3, 4, 123, _hook=hookfunc)
        self.formatEquals('_ah_ _4_       123',
                          '{0:ah} {1:bh}{2:>10d}',
                          3, 4, 123, _hook=hookfunc)
        self.formatRaises(ValueError, '{0:ah}')
        self.formatEquals('_ah_', '{0:ah}', 3,  _hook=hookfunc)
        self.formatRaises(ValueError, '{0:ah}', 123, _hook=hookfunc)

    def test_alt_syntax(self):
        self.formatEquals('{}1', '{!syntax1}{{}{0}', 1)
        self.formatEquals('{  ${  1  $1  $${0}  ${',
                          '{!syntax2}{  $${  ${0}  $$${0}  $$$${0}  $${',
                           1)
        self.formatEquals('1 {0} {\n0}',
                          '{!syntax3}{0} { 0} {\n0}', 1)
        self.formatRaises(ValueError, '}')
        self.formatRaises(ValueError, '{')
        self.formatEquals('}', '{!syntax1}}')
        self.formatRaises(ValueError, '{!syntax1}{')
        self.formatEquals('}', '{!syntax2}}')
        self.formatEquals('{', '{!syntax2}{')
        self.formatRaises(ValueError, '{!syntax1}${')
        self.formatEquals('${', '{!syntax2}$${')
        self.formatEquals('}', '{!syntax3}}')
        self.formatRaises(ValueError, '{!syntax3}{')
        self.formatEquals('{', '{!syntax3}{ ')

    def test_computed_lookups(self):
        class Custom(object):
            fred = 'Hi there'
        self.formatEquals('e', '{0[{1}]}', 'abcdefg', 4)
        self.formatEquals('e', '{foo[{bar}]}', foo='abcdefg', bar=4)
        self.formatEquals(Custom.fred, '{0.{1}}', Custom, 'fred')
        self.formatEquals(Custom.fred, '{x.{y}}', x=Custom, y='fred')
        self.formatEquals('t', '{x.{y}[{0}]}', 3, x=Custom, y='fred')

def test_main():
    test_support.run_unittest(FormatTest)

if __name__ == "__main__":
    test_main()
