• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import string
3from string import Template
4from test import test_support, string_tests
5from UserList import UserList
6
7class StringTest(
8    string_tests.CommonTest,
9    string_tests.MixinStrStringUserStringTest
10    ):
11
12    type2test = str
13
14    def checkequal(self, result, object, methodname, *args):
15        realresult = getattr(string, methodname)(object, *args)
16        self.assertEqual(
17            result,
18            realresult
19        )
20
21    def checkraises(self, exc, obj, methodname, *args):
22        with self.assertRaises(exc) as cm:
23            getattr(string, methodname)(obj, *args)
24        self.assertNotEqual(cm.exception.args[0], '')
25
26    def checkcall(self, object, methodname, *args):
27        getattr(string, methodname)(object, *args)
28
29    def test_join(self):
30        # These are the same checks as in string_test.ObjectTest.test_join
31        # but the argument order ist different
32        self.checkequal('a b c d', ['a', 'b', 'c', 'd'], 'join', ' ')
33        self.checkequal('abcd', ('a', 'b', 'c', 'd'), 'join', '')
34        self.checkequal('w x y z', string_tests.Sequence(), 'join', ' ')
35        self.checkequal('abc', ('abc',), 'join', 'a')
36        self.checkequal('z', UserList(['z']), 'join', 'a')
37        if test_support.have_unicode:
38            self.checkequal(unicode('a.b.c'), ['a', 'b', 'c'], 'join', unicode('.'))
39            self.checkequal(unicode('a.b.c'), [unicode('a'), 'b', 'c'], 'join', '.')
40            self.checkequal(unicode('a.b.c'), ['a', unicode('b'), 'c'], 'join', '.')
41            self.checkequal(unicode('a.b.c'), ['a', 'b', unicode('c')], 'join', '.')
42            self.checkraises(TypeError, ['a', unicode('b'), 3], 'join', '.')
43        for i in [5, 25, 125]:
44            self.checkequal(
45                ((('a' * i) + '-') * i)[:-1],
46                ['a' * i] * i, 'join', '-')
47            self.checkequal(
48                ((('a' * i) + '-') * i)[:-1],
49                ('a' * i,) * i, 'join', '-')
50
51        self.checkraises(TypeError, string_tests.BadSeq1(), 'join', ' ')
52        self.checkequal('a b c', string_tests.BadSeq2(), 'join', ' ')
53        try:
54            def f():
55                yield 4 + ""
56            self.fixtype(' ').join(f())
57        except TypeError, e:
58            if '+' not in str(e):
59                self.fail('join() ate exception message')
60        else:
61            self.fail('exception not raised')
62
63
64class ModuleTest(unittest.TestCase):
65
66    def test_attrs(self):
67        string.whitespace
68        string.lowercase
69        string.uppercase
70        string.letters
71        string.digits
72        string.hexdigits
73        string.octdigits
74        string.punctuation
75        string.printable
76
77    def test_atoi(self):
78        self.assertEqual(string.atoi(" 1 "), 1)
79        self.assertRaises(ValueError, string.atoi, " 1x")
80        self.assertRaises(ValueError, string.atoi, " x1 ")
81
82    def test_atol(self):
83        self.assertEqual(string.atol("  1  "), 1L)
84        self.assertRaises(ValueError, string.atol, "  1x ")
85        self.assertRaises(ValueError, string.atol, "  x1 ")
86
87    def test_atof(self):
88        self.assertAlmostEqual(string.atof("  1  "), 1.0)
89        self.assertRaises(ValueError, string.atof, "  1x ")
90        self.assertRaises(ValueError, string.atof, "  x1 ")
91
92    def test_maketrans(self):
93        transtable = '\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037 !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`xyzdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377'
94
95        self.assertEqual(string.maketrans('abc', 'xyz'), transtable)
96        self.assertRaises(ValueError, string.maketrans, 'abc', 'xyzq')
97
98    def test_capwords(self):
99        self.assertEqual(string.capwords('abc def ghi'), 'Abc Def Ghi')
100        self.assertEqual(string.capwords('abc\tdef\nghi'), 'Abc Def Ghi')
101        self.assertEqual(string.capwords('abc\t   def  \nghi'), 'Abc Def Ghi')
102        self.assertEqual(string.capwords('ABC DEF GHI'), 'Abc Def Ghi')
103        self.assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi')
104        self.assertEqual(string.capwords('ABC-def DEF-ghi GHI'), 'Abc-def Def-ghi Ghi')
105        self.assertEqual(string.capwords('   aBc  DeF   '), 'Abc Def')
106        self.assertEqual(string.capwords('\taBc\tDeF\t'), 'Abc Def')
107        self.assertEqual(string.capwords('\taBc\tDeF\t', '\t'), '\tAbc\tDef\t')
108
109    def test_formatter(self):
110        fmt = string.Formatter()
111        self.assertEqual(fmt.format("foo"), "foo")
112
113        self.assertEqual(fmt.format("foo{0}", "bar"), "foobar")
114        self.assertEqual(fmt.format("foo{1}{0}-{1}", "bar", 6), "foo6bar-6")
115        self.assertEqual(fmt.format("-{arg!r}-", arg='test'), "-'test'-")
116
117        # override get_value ############################################
118        class NamespaceFormatter(string.Formatter):
119            def __init__(self, namespace={}):
120                string.Formatter.__init__(self)
121                self.namespace = namespace
122
123            def get_value(self, key, args, kwds):
124                if isinstance(key, str):
125                    try:
126                        # Check explicitly passed arguments first
127                        return kwds[key]
128                    except KeyError:
129                        return self.namespace[key]
130                else:
131                    string.Formatter.get_value(key, args, kwds)
132
133        fmt = NamespaceFormatter({'greeting':'hello'})
134        self.assertEqual(fmt.format("{greeting}, world!"), 'hello, world!')
135
136
137        # override format_field #########################################
138        class CallFormatter(string.Formatter):
139            def format_field(self, value, format_spec):
140                return format(value(), format_spec)
141
142        fmt = CallFormatter()
143        self.assertEqual(fmt.format('*{0}*', lambda : 'result'), '*result*')
144
145
146        # override convert_field ########################################
147        class XFormatter(string.Formatter):
148            def convert_field(self, value, conversion):
149                if conversion == 'x':
150                    return None
151                return super(XFormatter, self).convert_field(value, conversion)
152
153        fmt = XFormatter()
154        self.assertEqual(fmt.format("{0!r}:{0!x}", 'foo', 'foo'), "'foo':None")
155
156
157        # override parse ################################################
158        class BarFormatter(string.Formatter):
159            # returns an iterable that contains tuples of the form:
160            # (literal_text, field_name, format_spec, conversion)
161            def parse(self, format_string):
162                for field in format_string.split('|'):
163                    if field[0] == '+':
164                        # it's markup
165                        field_name, _, format_spec = field[1:].partition(':')
166                        yield '', field_name, format_spec, None
167                    else:
168                        yield field, None, None, None
169
170        fmt = BarFormatter()
171        self.assertEqual(fmt.format('*|+0:^10s|*', 'foo'), '*   foo    *')
172
173        # test all parameters used
174        class CheckAllUsedFormatter(string.Formatter):
175            def check_unused_args(self, used_args, args, kwargs):
176                # Track which arguments actually got used
177                unused_args = set(kwargs.keys())
178                unused_args.update(range(0, len(args)))
179
180                for arg in used_args:
181                    unused_args.remove(arg)
182
183                if unused_args:
184                    raise ValueError("unused arguments")
185
186        fmt = CheckAllUsedFormatter()
187        self.assertEqual(fmt.format("{0}", 10), "10")
188        self.assertEqual(fmt.format("{0}{i}", 10, i=100), "10100")
189        self.assertEqual(fmt.format("{0}{i}{1}", 10, 20, i=100), "1010020")
190        self.assertRaises(ValueError, fmt.format, "{0}{i}{1}", 10, 20, i=100, j=0)
191        self.assertRaises(ValueError, fmt.format, "{0}", 10, 20)
192        self.assertRaises(ValueError, fmt.format, "{0}", 10, 20, i=100)
193        self.assertRaises(ValueError, fmt.format, "{i}", 10, 20, i=100)
194
195        # Alternate formatting is not supported
196        self.assertRaises(ValueError, format, '', '#')
197        self.assertRaises(ValueError, format, '', '#20')
198
199    def test_format_keyword_arguments(self):
200        fmt = string.Formatter()
201        self.assertEqual(fmt.format("-{arg}-", arg='test'), '-test-')
202        self.assertRaises(KeyError, fmt.format, "-{arg}-")
203        self.assertEqual(fmt.format("-{self}-", self='test'), '-test-')
204        self.assertRaises(KeyError, fmt.format, "-{self}-")
205        self.assertEqual(fmt.format("-{format_string}-", format_string='test'),
206                         '-test-')
207        self.assertRaises(KeyError, fmt.format, "-{format_string}-")
208        self.assertEqual(fmt.format(arg='test', format_string="-{arg}-"),
209                         '-test-')
210
211class BytesAliasTest(unittest.TestCase):
212
213    def test_builtin(self):
214        self.assertTrue(str is bytes)
215
216    def test_syntax(self):
217        self.assertEqual(b"spam", "spam")
218        self.assertEqual(br"egg\foo", "egg\\foo")
219        self.assertTrue(type(b""), str)
220        self.assertTrue(type(br""), str)
221
222
223# Template tests (formerly housed in test_pep292.py)
224
225class Bag:
226    pass
227
228class Mapping:
229    def __getitem__(self, name):
230        obj = self
231        for part in name.split('.'):
232            try:
233                obj = getattr(obj, part)
234            except AttributeError:
235                raise KeyError(name)
236        return obj
237
238
239class TestTemplate(unittest.TestCase):
240    def test_regular_templates(self):
241        s = Template('$who likes to eat a bag of $what worth $$100')
242        self.assertEqual(s.substitute(dict(who='tim', what='ham')),
243                         'tim likes to eat a bag of ham worth $100')
244        self.assertRaises(KeyError, s.substitute, dict(who='tim'))
245        self.assertRaises(TypeError, Template.substitute)
246
247    def test_regular_templates_with_braces(self):
248        s = Template('$who likes ${what} for ${meal}')
249        d = dict(who='tim', what='ham', meal='dinner')
250        self.assertEqual(s.substitute(d), 'tim likes ham for dinner')
251        self.assertRaises(KeyError, s.substitute,
252                          dict(who='tim', what='ham'))
253
254    def test_escapes(self):
255        eq = self.assertEqual
256        s = Template('$who likes to eat a bag of $$what worth $$100')
257        eq(s.substitute(dict(who='tim', what='ham')),
258           'tim likes to eat a bag of $what worth $100')
259        s = Template('$who likes $$')
260        eq(s.substitute(dict(who='tim', what='ham')), 'tim likes $')
261
262    def test_percents(self):
263        eq = self.assertEqual
264        s = Template('%(foo)s $foo ${foo}')
265        d = dict(foo='baz')
266        eq(s.substitute(d), '%(foo)s baz baz')
267        eq(s.safe_substitute(d), '%(foo)s baz baz')
268
269    def test_stringification(self):
270        eq = self.assertEqual
271        s = Template('tim has eaten $count bags of ham today')
272        d = dict(count=7)
273        eq(s.substitute(d), 'tim has eaten 7 bags of ham today')
274        eq(s.safe_substitute(d), 'tim has eaten 7 bags of ham today')
275        s = Template('tim has eaten ${count} bags of ham today')
276        eq(s.substitute(d), 'tim has eaten 7 bags of ham today')
277
278    def test_tupleargs(self):
279        eq = self.assertEqual
280        s = Template('$who ate ${meal}')
281        d = dict(who=('tim', 'fred'), meal=('ham', 'kung pao'))
282        eq(s.substitute(d), "('tim', 'fred') ate ('ham', 'kung pao')")
283        eq(s.safe_substitute(d), "('tim', 'fred') ate ('ham', 'kung pao')")
284
285    def test_SafeTemplate(self):
286        eq = self.assertEqual
287        s = Template('$who likes ${what} for ${meal}')
288        eq(s.safe_substitute(dict(who='tim')), 'tim likes ${what} for ${meal}')
289        eq(s.safe_substitute(dict(what='ham')), '$who likes ham for ${meal}')
290        eq(s.safe_substitute(dict(what='ham', meal='dinner')),
291           '$who likes ham for dinner')
292        eq(s.safe_substitute(dict(who='tim', what='ham')),
293           'tim likes ham for ${meal}')
294        eq(s.safe_substitute(dict(who='tim', what='ham', meal='dinner')),
295           'tim likes ham for dinner')
296
297    def test_invalid_placeholders(self):
298        raises = self.assertRaises
299        s = Template('$who likes $')
300        raises(ValueError, s.substitute, dict(who='tim'))
301        s = Template('$who likes ${what)')
302        raises(ValueError, s.substitute, dict(who='tim'))
303        s = Template('$who likes $100')
304        raises(ValueError, s.substitute, dict(who='tim'))
305
306    def test_idpattern_override(self):
307        class PathPattern(Template):
308            idpattern = r'[_a-z][._a-z0-9]*'
309        m = Mapping()
310        m.bag = Bag()
311        m.bag.foo = Bag()
312        m.bag.foo.who = 'tim'
313        m.bag.what = 'ham'
314        s = PathPattern('$bag.foo.who likes to eat a bag of $bag.what')
315        self.assertEqual(s.substitute(m), 'tim likes to eat a bag of ham')
316
317    def test_pattern_override(self):
318        class MyPattern(Template):
319            pattern = r"""
320            (?P<escaped>@{2})                   |
321            @(?P<named>[_a-z][._a-z0-9]*)       |
322            @{(?P<braced>[_a-z][._a-z0-9]*)}    |
323            (?P<invalid>@)
324            """
325        m = Mapping()
326        m.bag = Bag()
327        m.bag.foo = Bag()
328        m.bag.foo.who = 'tim'
329        m.bag.what = 'ham'
330        s = MyPattern('@bag.foo.who likes to eat a bag of @bag.what')
331        self.assertEqual(s.substitute(m), 'tim likes to eat a bag of ham')
332
333        class BadPattern(Template):
334            pattern = r"""
335            (?P<badname>.*)                     |
336            (?P<escaped>@{2})                   |
337            @(?P<named>[_a-z][._a-z0-9]*)       |
338            @{(?P<braced>[_a-z][._a-z0-9]*)}    |
339            (?P<invalid>@)                      |
340            """
341        s = BadPattern('@bag.foo.who likes to eat a bag of @bag.what')
342        self.assertRaises(ValueError, s.substitute, {})
343        self.assertRaises(ValueError, s.safe_substitute, {})
344
345    def test_braced_override(self):
346        class MyTemplate(Template):
347            pattern = r"""
348            \$(?:
349              (?P<escaped>$)                     |
350              (?P<named>[_a-z][_a-z0-9]*)        |
351              @@(?P<braced>[_a-z][_a-z0-9]*)@@   |
352              (?P<invalid>)                      |
353           )
354           """
355
356        tmpl = 'PyCon in $@@location@@'
357        t = MyTemplate(tmpl)
358        self.assertRaises(KeyError, t.substitute, {})
359        val = t.substitute({'location': 'Cleveland'})
360        self.assertEqual(val, 'PyCon in Cleveland')
361
362    def test_braced_override_safe(self):
363        class MyTemplate(Template):
364            pattern = r"""
365            \$(?:
366              (?P<escaped>$)                     |
367              (?P<named>[_a-z][_a-z0-9]*)        |
368              @@(?P<braced>[_a-z][_a-z0-9]*)@@   |
369              (?P<invalid>)                      |
370           )
371           """
372
373        tmpl = 'PyCon in $@@location@@'
374        t = MyTemplate(tmpl)
375        self.assertEqual(t.safe_substitute(), tmpl)
376        val = t.safe_substitute({'location': 'Cleveland'})
377        self.assertEqual(val, 'PyCon in Cleveland')
378
379    def test_unicode_values(self):
380        s = Template('$who likes $what')
381        d = dict(who=u't\xffm', what=u'f\xfe\fed')
382        self.assertEqual(s.substitute(d), u't\xffm likes f\xfe\x0ced')
383
384    def test_keyword_arguments(self):
385        eq = self.assertEqual
386        s = Template('$who likes $what')
387        eq(s.substitute(who='tim', what='ham'), 'tim likes ham')
388        eq(s.substitute(dict(who='tim'), what='ham'), 'tim likes ham')
389        eq(s.substitute(dict(who='fred', what='kung pao'),
390                        who='tim', what='ham'),
391           'tim likes ham')
392        s = Template('the mapping is $mapping')
393        eq(s.substitute(dict(foo='none'), mapping='bozo'),
394           'the mapping is bozo')
395        eq(s.substitute(dict(mapping='one'), mapping='two'),
396           'the mapping is two')
397
398        s = Template('the self is $self')
399        eq(s.substitute(self='bozo'), 'the self is bozo')
400
401    def test_keyword_arguments_safe(self):
402        eq = self.assertEqual
403        raises = self.assertRaises
404        s = Template('$who likes $what')
405        eq(s.safe_substitute(who='tim', what='ham'), 'tim likes ham')
406        eq(s.safe_substitute(dict(who='tim'), what='ham'), 'tim likes ham')
407        eq(s.safe_substitute(dict(who='fred', what='kung pao'),
408                        who='tim', what='ham'),
409           'tim likes ham')
410        s = Template('the mapping is $mapping')
411        eq(s.safe_substitute(dict(foo='none'), mapping='bozo'),
412           'the mapping is bozo')
413        eq(s.safe_substitute(dict(mapping='one'), mapping='two'),
414           'the mapping is two')
415        d = dict(mapping='one')
416        raises(TypeError, s.substitute, d, {})
417        raises(TypeError, s.safe_substitute, d, {})
418
419        s = Template('the self is $self')
420        eq(s.safe_substitute(self='bozo'), 'the self is bozo')
421
422    def test_delimiter_override(self):
423        eq = self.assertEqual
424        raises = self.assertRaises
425        class AmpersandTemplate(Template):
426            delimiter = '&'
427        s = AmpersandTemplate('this &gift is for &{who} &&')
428        eq(s.substitute(gift='bud', who='you'), 'this bud is for you &')
429        raises(KeyError, s.substitute)
430        eq(s.safe_substitute(gift='bud', who='you'), 'this bud is for you &')
431        eq(s.safe_substitute(), 'this &gift is for &{who} &')
432        s = AmpersandTemplate('this &gift is for &{who} &')
433        raises(ValueError, s.substitute, dict(gift='bud', who='you'))
434        eq(s.safe_substitute(), 'this &gift is for &{who} &')
435
436        class PieDelims(Template):
437            delimiter = '@'
438        s = PieDelims('@who likes to eat a bag of @{what} worth $100')
439        self.assertEqual(s.substitute(dict(who='tim', what='ham')),
440                         'tim likes to eat a bag of ham worth $100')
441
442
443def test_main():
444    test_support.run_unittest(StringTest, ModuleTest, BytesAliasTest, TestTemplate)
445
446if __name__ == '__main__':
447    test_main()
448