17db96d56Sopenharmony_ciimport unittest 27db96d56Sopenharmony_ciimport string 37db96d56Sopenharmony_cifrom string import Template 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ciclass ModuleTest(unittest.TestCase): 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ci def test_attrs(self): 97db96d56Sopenharmony_ci # While the exact order of the items in these attributes is not 107db96d56Sopenharmony_ci # technically part of the "language spec", in practice there is almost 117db96d56Sopenharmony_ci # certainly user code that depends on the order, so de-facto it *is* 127db96d56Sopenharmony_ci # part of the spec. 137db96d56Sopenharmony_ci self.assertEqual(string.whitespace, ' \t\n\r\x0b\x0c') 147db96d56Sopenharmony_ci self.assertEqual(string.ascii_lowercase, 'abcdefghijklmnopqrstuvwxyz') 157db96d56Sopenharmony_ci self.assertEqual(string.ascii_uppercase, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') 167db96d56Sopenharmony_ci self.assertEqual(string.ascii_letters, string.ascii_lowercase + string.ascii_uppercase) 177db96d56Sopenharmony_ci self.assertEqual(string.digits, '0123456789') 187db96d56Sopenharmony_ci self.assertEqual(string.hexdigits, string.digits + 'abcdefABCDEF') 197db96d56Sopenharmony_ci self.assertEqual(string.octdigits, '01234567') 207db96d56Sopenharmony_ci self.assertEqual(string.punctuation, '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~') 217db96d56Sopenharmony_ci self.assertEqual(string.printable, string.digits + string.ascii_lowercase + string.ascii_uppercase + string.punctuation + string.whitespace) 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci def test_capwords(self): 247db96d56Sopenharmony_ci self.assertEqual(string.capwords('abc def ghi'), 'Abc Def Ghi') 257db96d56Sopenharmony_ci self.assertEqual(string.capwords('abc\tdef\nghi'), 'Abc Def Ghi') 267db96d56Sopenharmony_ci self.assertEqual(string.capwords('abc\t def \nghi'), 'Abc Def Ghi') 277db96d56Sopenharmony_ci self.assertEqual(string.capwords('ABC DEF GHI'), 'Abc Def Ghi') 287db96d56Sopenharmony_ci self.assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi') 297db96d56Sopenharmony_ci self.assertEqual(string.capwords('ABC-def DEF-ghi GHI'), 'Abc-def Def-ghi Ghi') 307db96d56Sopenharmony_ci self.assertEqual(string.capwords(' aBc DeF '), 'Abc Def') 317db96d56Sopenharmony_ci self.assertEqual(string.capwords('\taBc\tDeF\t'), 'Abc Def') 327db96d56Sopenharmony_ci self.assertEqual(string.capwords('\taBc\tDeF\t', '\t'), '\tAbc\tDef\t') 337db96d56Sopenharmony_ci 347db96d56Sopenharmony_ci def test_basic_formatter(self): 357db96d56Sopenharmony_ci fmt = string.Formatter() 367db96d56Sopenharmony_ci self.assertEqual(fmt.format("foo"), "foo") 377db96d56Sopenharmony_ci self.assertEqual(fmt.format("foo{0}", "bar"), "foobar") 387db96d56Sopenharmony_ci self.assertEqual(fmt.format("foo{1}{0}-{1}", "bar", 6), "foo6bar-6") 397db96d56Sopenharmony_ci self.assertRaises(TypeError, fmt.format) 407db96d56Sopenharmony_ci self.assertRaises(TypeError, string.Formatter.format) 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_ci def test_format_keyword_arguments(self): 437db96d56Sopenharmony_ci fmt = string.Formatter() 447db96d56Sopenharmony_ci self.assertEqual(fmt.format("-{arg}-", arg='test'), '-test-') 457db96d56Sopenharmony_ci self.assertRaises(KeyError, fmt.format, "-{arg}-") 467db96d56Sopenharmony_ci self.assertEqual(fmt.format("-{self}-", self='test'), '-test-') 477db96d56Sopenharmony_ci self.assertRaises(KeyError, fmt.format, "-{self}-") 487db96d56Sopenharmony_ci self.assertEqual(fmt.format("-{format_string}-", format_string='test'), 497db96d56Sopenharmony_ci '-test-') 507db96d56Sopenharmony_ci self.assertRaises(KeyError, fmt.format, "-{format_string}-") 517db96d56Sopenharmony_ci with self.assertRaisesRegex(TypeError, "format_string"): 527db96d56Sopenharmony_ci fmt.format(format_string="-{arg}-", arg='test') 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci def test_auto_numbering(self): 557db96d56Sopenharmony_ci fmt = string.Formatter() 567db96d56Sopenharmony_ci self.assertEqual(fmt.format('foo{}{}', 'bar', 6), 577db96d56Sopenharmony_ci 'foo{}{}'.format('bar', 6)) 587db96d56Sopenharmony_ci self.assertEqual(fmt.format('foo{1}{num}{1}', None, 'bar', num=6), 597db96d56Sopenharmony_ci 'foo{1}{num}{1}'.format(None, 'bar', num=6)) 607db96d56Sopenharmony_ci self.assertEqual(fmt.format('{:^{}}', 'bar', 6), 617db96d56Sopenharmony_ci '{:^{}}'.format('bar', 6)) 627db96d56Sopenharmony_ci self.assertEqual(fmt.format('{:^{}} {}', 'bar', 6, 'X'), 637db96d56Sopenharmony_ci '{:^{}} {}'.format('bar', 6, 'X')) 647db96d56Sopenharmony_ci self.assertEqual(fmt.format('{:^{pad}}{}', 'foo', 'bar', pad=6), 657db96d56Sopenharmony_ci '{:^{pad}}{}'.format('foo', 'bar', pad=6)) 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci with self.assertRaises(ValueError): 687db96d56Sopenharmony_ci fmt.format('foo{1}{}', 'bar', 6) 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_ci with self.assertRaises(ValueError): 717db96d56Sopenharmony_ci fmt.format('foo{}{1}', 'bar', 6) 727db96d56Sopenharmony_ci 737db96d56Sopenharmony_ci def test_conversion_specifiers(self): 747db96d56Sopenharmony_ci fmt = string.Formatter() 757db96d56Sopenharmony_ci self.assertEqual(fmt.format("-{arg!r}-", arg='test'), "-'test'-") 767db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0!s}", 'test'), 'test') 777db96d56Sopenharmony_ci self.assertRaises(ValueError, fmt.format, "{0!h}", 'test') 787db96d56Sopenharmony_ci # issue13579 797db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0!a}", 42), '42') 807db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0!a}", string.ascii_letters), 817db96d56Sopenharmony_ci "'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'") 827db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0!a}", chr(255)), "'\\xff'") 837db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0!a}", chr(256)), "'\\u0100'") 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci def test_name_lookup(self): 867db96d56Sopenharmony_ci fmt = string.Formatter() 877db96d56Sopenharmony_ci class AnyAttr: 887db96d56Sopenharmony_ci def __getattr__(self, attr): 897db96d56Sopenharmony_ci return attr 907db96d56Sopenharmony_ci x = AnyAttr() 917db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0.lumber}{0.jack}", x), 'lumberjack') 927db96d56Sopenharmony_ci with self.assertRaises(AttributeError): 937db96d56Sopenharmony_ci fmt.format("{0.lumber}{0.jack}", '') 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci def test_index_lookup(self): 967db96d56Sopenharmony_ci fmt = string.Formatter() 977db96d56Sopenharmony_ci lookup = ["eggs", "and", "spam"] 987db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0[2]}{0[0]}", lookup), 'spameggs') 997db96d56Sopenharmony_ci with self.assertRaises(IndexError): 1007db96d56Sopenharmony_ci fmt.format("{0[2]}{0[0]}", []) 1017db96d56Sopenharmony_ci with self.assertRaises(KeyError): 1027db96d56Sopenharmony_ci fmt.format("{0[2]}{0[0]}", {}) 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci def test_override_get_value(self): 1057db96d56Sopenharmony_ci class NamespaceFormatter(string.Formatter): 1067db96d56Sopenharmony_ci def __init__(self, namespace={}): 1077db96d56Sopenharmony_ci string.Formatter.__init__(self) 1087db96d56Sopenharmony_ci self.namespace = namespace 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_ci def get_value(self, key, args, kwds): 1117db96d56Sopenharmony_ci if isinstance(key, str): 1127db96d56Sopenharmony_ci try: 1137db96d56Sopenharmony_ci # Check explicitly passed arguments first 1147db96d56Sopenharmony_ci return kwds[key] 1157db96d56Sopenharmony_ci except KeyError: 1167db96d56Sopenharmony_ci return self.namespace[key] 1177db96d56Sopenharmony_ci else: 1187db96d56Sopenharmony_ci string.Formatter.get_value(key, args, kwds) 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci fmt = NamespaceFormatter({'greeting':'hello'}) 1217db96d56Sopenharmony_ci self.assertEqual(fmt.format("{greeting}, world!"), 'hello, world!') 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci def test_override_format_field(self): 1257db96d56Sopenharmony_ci class CallFormatter(string.Formatter): 1267db96d56Sopenharmony_ci def format_field(self, value, format_spec): 1277db96d56Sopenharmony_ci return format(value(), format_spec) 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ci fmt = CallFormatter() 1307db96d56Sopenharmony_ci self.assertEqual(fmt.format('*{0}*', lambda : 'result'), '*result*') 1317db96d56Sopenharmony_ci 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci def test_override_convert_field(self): 1347db96d56Sopenharmony_ci class XFormatter(string.Formatter): 1357db96d56Sopenharmony_ci def convert_field(self, value, conversion): 1367db96d56Sopenharmony_ci if conversion == 'x': 1377db96d56Sopenharmony_ci return None 1387db96d56Sopenharmony_ci return super().convert_field(value, conversion) 1397db96d56Sopenharmony_ci 1407db96d56Sopenharmony_ci fmt = XFormatter() 1417db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0!r}:{0!x}", 'foo', 'foo'), "'foo':None") 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ci def test_override_parse(self): 1457db96d56Sopenharmony_ci class BarFormatter(string.Formatter): 1467db96d56Sopenharmony_ci # returns an iterable that contains tuples of the form: 1477db96d56Sopenharmony_ci # (literal_text, field_name, format_spec, conversion) 1487db96d56Sopenharmony_ci def parse(self, format_string): 1497db96d56Sopenharmony_ci for field in format_string.split('|'): 1507db96d56Sopenharmony_ci if field[0] == '+': 1517db96d56Sopenharmony_ci # it's markup 1527db96d56Sopenharmony_ci field_name, _, format_spec = field[1:].partition(':') 1537db96d56Sopenharmony_ci yield '', field_name, format_spec, None 1547db96d56Sopenharmony_ci else: 1557db96d56Sopenharmony_ci yield field, None, None, None 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci fmt = BarFormatter() 1587db96d56Sopenharmony_ci self.assertEqual(fmt.format('*|+0:^10s|*', 'foo'), '* foo *') 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci def test_check_unused_args(self): 1617db96d56Sopenharmony_ci class CheckAllUsedFormatter(string.Formatter): 1627db96d56Sopenharmony_ci def check_unused_args(self, used_args, args, kwargs): 1637db96d56Sopenharmony_ci # Track which arguments actually got used 1647db96d56Sopenharmony_ci unused_args = set(kwargs.keys()) 1657db96d56Sopenharmony_ci unused_args.update(range(0, len(args))) 1667db96d56Sopenharmony_ci 1677db96d56Sopenharmony_ci for arg in used_args: 1687db96d56Sopenharmony_ci unused_args.remove(arg) 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci if unused_args: 1717db96d56Sopenharmony_ci raise ValueError("unused arguments") 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci fmt = CheckAllUsedFormatter() 1747db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0}", 10), "10") 1757db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0}{i}", 10, i=100), "10100") 1767db96d56Sopenharmony_ci self.assertEqual(fmt.format("{0}{i}{1}", 10, 20, i=100), "1010020") 1777db96d56Sopenharmony_ci self.assertRaises(ValueError, fmt.format, "{0}{i}{1}", 10, 20, i=100, j=0) 1787db96d56Sopenharmony_ci self.assertRaises(ValueError, fmt.format, "{0}", 10, 20) 1797db96d56Sopenharmony_ci self.assertRaises(ValueError, fmt.format, "{0}", 10, 20, i=100) 1807db96d56Sopenharmony_ci self.assertRaises(ValueError, fmt.format, "{i}", 10, 20, i=100) 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci def test_vformat_recursion_limit(self): 1837db96d56Sopenharmony_ci fmt = string.Formatter() 1847db96d56Sopenharmony_ci args = () 1857db96d56Sopenharmony_ci kwargs = dict(i=100) 1867db96d56Sopenharmony_ci with self.assertRaises(ValueError) as err: 1877db96d56Sopenharmony_ci fmt._vformat("{i}", args, kwargs, set(), -1) 1887db96d56Sopenharmony_ci self.assertIn("recursion", str(err.exception)) 1897db96d56Sopenharmony_ci 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci# Template tests (formerly housed in test_pep292.py) 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ciclass Bag: 1947db96d56Sopenharmony_ci pass 1957db96d56Sopenharmony_ci 1967db96d56Sopenharmony_ciclass Mapping: 1977db96d56Sopenharmony_ci def __getitem__(self, name): 1987db96d56Sopenharmony_ci obj = self 1997db96d56Sopenharmony_ci for part in name.split('.'): 2007db96d56Sopenharmony_ci try: 2017db96d56Sopenharmony_ci obj = getattr(obj, part) 2027db96d56Sopenharmony_ci except AttributeError: 2037db96d56Sopenharmony_ci raise KeyError(name) 2047db96d56Sopenharmony_ci return obj 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci 2077db96d56Sopenharmony_ciclass TestTemplate(unittest.TestCase): 2087db96d56Sopenharmony_ci def test_regular_templates(self): 2097db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of $what worth $$100') 2107db96d56Sopenharmony_ci self.assertEqual(s.substitute(dict(who='tim', what='ham')), 2117db96d56Sopenharmony_ci 'tim likes to eat a bag of ham worth $100') 2127db96d56Sopenharmony_ci self.assertRaises(KeyError, s.substitute, dict(who='tim')) 2137db96d56Sopenharmony_ci self.assertRaises(TypeError, Template.substitute) 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ci def test_regular_templates_with_braces(self): 2167db96d56Sopenharmony_ci s = Template('$who likes ${what} for ${meal}') 2177db96d56Sopenharmony_ci d = dict(who='tim', what='ham', meal='dinner') 2187db96d56Sopenharmony_ci self.assertEqual(s.substitute(d), 'tim likes ham for dinner') 2197db96d56Sopenharmony_ci self.assertRaises(KeyError, s.substitute, 2207db96d56Sopenharmony_ci dict(who='tim', what='ham')) 2217db96d56Sopenharmony_ci 2227db96d56Sopenharmony_ci def test_regular_templates_with_upper_case(self): 2237db96d56Sopenharmony_ci s = Template('$WHO likes ${WHAT} for ${MEAL}') 2247db96d56Sopenharmony_ci d = dict(WHO='tim', WHAT='ham', MEAL='dinner') 2257db96d56Sopenharmony_ci self.assertEqual(s.substitute(d), 'tim likes ham for dinner') 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci def test_regular_templates_with_non_letters(self): 2287db96d56Sopenharmony_ci s = Template('$_wh0_ likes ${_w_h_a_t_} for ${mea1}') 2297db96d56Sopenharmony_ci d = dict(_wh0_='tim', _w_h_a_t_='ham', mea1='dinner') 2307db96d56Sopenharmony_ci self.assertEqual(s.substitute(d), 'tim likes ham for dinner') 2317db96d56Sopenharmony_ci 2327db96d56Sopenharmony_ci def test_escapes(self): 2337db96d56Sopenharmony_ci eq = self.assertEqual 2347db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of $$what worth $$100') 2357db96d56Sopenharmony_ci eq(s.substitute(dict(who='tim', what='ham')), 2367db96d56Sopenharmony_ci 'tim likes to eat a bag of $what worth $100') 2377db96d56Sopenharmony_ci s = Template('$who likes $$') 2387db96d56Sopenharmony_ci eq(s.substitute(dict(who='tim', what='ham')), 'tim likes $') 2397db96d56Sopenharmony_ci 2407db96d56Sopenharmony_ci def test_percents(self): 2417db96d56Sopenharmony_ci eq = self.assertEqual 2427db96d56Sopenharmony_ci s = Template('%(foo)s $foo ${foo}') 2437db96d56Sopenharmony_ci d = dict(foo='baz') 2447db96d56Sopenharmony_ci eq(s.substitute(d), '%(foo)s baz baz') 2457db96d56Sopenharmony_ci eq(s.safe_substitute(d), '%(foo)s baz baz') 2467db96d56Sopenharmony_ci 2477db96d56Sopenharmony_ci def test_stringification(self): 2487db96d56Sopenharmony_ci eq = self.assertEqual 2497db96d56Sopenharmony_ci s = Template('tim has eaten $count bags of ham today') 2507db96d56Sopenharmony_ci d = dict(count=7) 2517db96d56Sopenharmony_ci eq(s.substitute(d), 'tim has eaten 7 bags of ham today') 2527db96d56Sopenharmony_ci eq(s.safe_substitute(d), 'tim has eaten 7 bags of ham today') 2537db96d56Sopenharmony_ci s = Template('tim has eaten ${count} bags of ham today') 2547db96d56Sopenharmony_ci eq(s.substitute(d), 'tim has eaten 7 bags of ham today') 2557db96d56Sopenharmony_ci 2567db96d56Sopenharmony_ci def test_tupleargs(self): 2577db96d56Sopenharmony_ci eq = self.assertEqual 2587db96d56Sopenharmony_ci s = Template('$who ate ${meal}') 2597db96d56Sopenharmony_ci d = dict(who=('tim', 'fred'), meal=('ham', 'kung pao')) 2607db96d56Sopenharmony_ci eq(s.substitute(d), "('tim', 'fred') ate ('ham', 'kung pao')") 2617db96d56Sopenharmony_ci eq(s.safe_substitute(d), "('tim', 'fred') ate ('ham', 'kung pao')") 2627db96d56Sopenharmony_ci 2637db96d56Sopenharmony_ci def test_SafeTemplate(self): 2647db96d56Sopenharmony_ci eq = self.assertEqual 2657db96d56Sopenharmony_ci s = Template('$who likes ${what} for ${meal}') 2667db96d56Sopenharmony_ci eq(s.safe_substitute(dict(who='tim')), 'tim likes ${what} for ${meal}') 2677db96d56Sopenharmony_ci eq(s.safe_substitute(dict(what='ham')), '$who likes ham for ${meal}') 2687db96d56Sopenharmony_ci eq(s.safe_substitute(dict(what='ham', meal='dinner')), 2697db96d56Sopenharmony_ci '$who likes ham for dinner') 2707db96d56Sopenharmony_ci eq(s.safe_substitute(dict(who='tim', what='ham')), 2717db96d56Sopenharmony_ci 'tim likes ham for ${meal}') 2727db96d56Sopenharmony_ci eq(s.safe_substitute(dict(who='tim', what='ham', meal='dinner')), 2737db96d56Sopenharmony_ci 'tim likes ham for dinner') 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci def test_invalid_placeholders(self): 2767db96d56Sopenharmony_ci raises = self.assertRaises 2777db96d56Sopenharmony_ci s = Template('$who likes $') 2787db96d56Sopenharmony_ci raises(ValueError, s.substitute, dict(who='tim')) 2797db96d56Sopenharmony_ci s = Template('$who likes ${what)') 2807db96d56Sopenharmony_ci raises(ValueError, s.substitute, dict(who='tim')) 2817db96d56Sopenharmony_ci s = Template('$who likes $100') 2827db96d56Sopenharmony_ci raises(ValueError, s.substitute, dict(who='tim')) 2837db96d56Sopenharmony_ci # Template.idpattern should match to only ASCII characters. 2847db96d56Sopenharmony_ci # https://bugs.python.org/issue31672 2857db96d56Sopenharmony_ci s = Template("$who likes $\u0131") # (DOTLESS I) 2867db96d56Sopenharmony_ci raises(ValueError, s.substitute, dict(who='tim')) 2877db96d56Sopenharmony_ci s = Template("$who likes $\u0130") # (LATIN CAPITAL LETTER I WITH DOT ABOVE) 2887db96d56Sopenharmony_ci raises(ValueError, s.substitute, dict(who='tim')) 2897db96d56Sopenharmony_ci 2907db96d56Sopenharmony_ci def test_idpattern_override(self): 2917db96d56Sopenharmony_ci class PathPattern(Template): 2927db96d56Sopenharmony_ci idpattern = r'[_a-z][._a-z0-9]*' 2937db96d56Sopenharmony_ci m = Mapping() 2947db96d56Sopenharmony_ci m.bag = Bag() 2957db96d56Sopenharmony_ci m.bag.foo = Bag() 2967db96d56Sopenharmony_ci m.bag.foo.who = 'tim' 2977db96d56Sopenharmony_ci m.bag.what = 'ham' 2987db96d56Sopenharmony_ci s = PathPattern('$bag.foo.who likes to eat a bag of $bag.what') 2997db96d56Sopenharmony_ci self.assertEqual(s.substitute(m), 'tim likes to eat a bag of ham') 3007db96d56Sopenharmony_ci 3017db96d56Sopenharmony_ci def test_flags_override(self): 3027db96d56Sopenharmony_ci class MyPattern(Template): 3037db96d56Sopenharmony_ci flags = 0 3047db96d56Sopenharmony_ci s = MyPattern('$wHO likes ${WHAT} for ${meal}') 3057db96d56Sopenharmony_ci d = dict(wHO='tim', WHAT='ham', meal='dinner', w='fred') 3067db96d56Sopenharmony_ci self.assertRaises(ValueError, s.substitute, d) 3077db96d56Sopenharmony_ci self.assertEqual(s.safe_substitute(d), 'fredHO likes ${WHAT} for dinner') 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ci def test_idpattern_override_inside_outside(self): 3107db96d56Sopenharmony_ci # bpo-1198569: Allow the regexp inside and outside braces to be 3117db96d56Sopenharmony_ci # different when deriving from Template. 3127db96d56Sopenharmony_ci class MyPattern(Template): 3137db96d56Sopenharmony_ci idpattern = r'[a-z]+' 3147db96d56Sopenharmony_ci braceidpattern = r'[A-Z]+' 3157db96d56Sopenharmony_ci flags = 0 3167db96d56Sopenharmony_ci m = dict(foo='foo', BAR='BAR') 3177db96d56Sopenharmony_ci s = MyPattern('$foo ${BAR}') 3187db96d56Sopenharmony_ci self.assertEqual(s.substitute(m), 'foo BAR') 3197db96d56Sopenharmony_ci 3207db96d56Sopenharmony_ci def test_idpattern_override_inside_outside_invalid_unbraced(self): 3217db96d56Sopenharmony_ci # bpo-1198569: Allow the regexp inside and outside braces to be 3227db96d56Sopenharmony_ci # different when deriving from Template. 3237db96d56Sopenharmony_ci class MyPattern(Template): 3247db96d56Sopenharmony_ci idpattern = r'[a-z]+' 3257db96d56Sopenharmony_ci braceidpattern = r'[A-Z]+' 3267db96d56Sopenharmony_ci flags = 0 3277db96d56Sopenharmony_ci m = dict(foo='foo', BAR='BAR') 3287db96d56Sopenharmony_ci s = MyPattern('$FOO') 3297db96d56Sopenharmony_ci self.assertRaises(ValueError, s.substitute, m) 3307db96d56Sopenharmony_ci s = MyPattern('${bar}') 3317db96d56Sopenharmony_ci self.assertRaises(ValueError, s.substitute, m) 3327db96d56Sopenharmony_ci 3337db96d56Sopenharmony_ci def test_pattern_override(self): 3347db96d56Sopenharmony_ci class MyPattern(Template): 3357db96d56Sopenharmony_ci pattern = r""" 3367db96d56Sopenharmony_ci (?P<escaped>@{2}) | 3377db96d56Sopenharmony_ci @(?P<named>[_a-z][._a-z0-9]*) | 3387db96d56Sopenharmony_ci @{(?P<braced>[_a-z][._a-z0-9]*)} | 3397db96d56Sopenharmony_ci (?P<invalid>@) 3407db96d56Sopenharmony_ci """ 3417db96d56Sopenharmony_ci m = Mapping() 3427db96d56Sopenharmony_ci m.bag = Bag() 3437db96d56Sopenharmony_ci m.bag.foo = Bag() 3447db96d56Sopenharmony_ci m.bag.foo.who = 'tim' 3457db96d56Sopenharmony_ci m.bag.what = 'ham' 3467db96d56Sopenharmony_ci s = MyPattern('@bag.foo.who likes to eat a bag of @bag.what') 3477db96d56Sopenharmony_ci self.assertEqual(s.substitute(m), 'tim likes to eat a bag of ham') 3487db96d56Sopenharmony_ci 3497db96d56Sopenharmony_ci class BadPattern(Template): 3507db96d56Sopenharmony_ci pattern = r""" 3517db96d56Sopenharmony_ci (?P<badname>.*) | 3527db96d56Sopenharmony_ci (?P<escaped>@{2}) | 3537db96d56Sopenharmony_ci @(?P<named>[_a-z][._a-z0-9]*) | 3547db96d56Sopenharmony_ci @{(?P<braced>[_a-z][._a-z0-9]*)} | 3557db96d56Sopenharmony_ci (?P<invalid>@) | 3567db96d56Sopenharmony_ci """ 3577db96d56Sopenharmony_ci s = BadPattern('@bag.foo.who likes to eat a bag of @bag.what') 3587db96d56Sopenharmony_ci self.assertRaises(ValueError, s.substitute, {}) 3597db96d56Sopenharmony_ci self.assertRaises(ValueError, s.safe_substitute, {}) 3607db96d56Sopenharmony_ci 3617db96d56Sopenharmony_ci def test_braced_override(self): 3627db96d56Sopenharmony_ci class MyTemplate(Template): 3637db96d56Sopenharmony_ci pattern = r""" 3647db96d56Sopenharmony_ci \$(?: 3657db96d56Sopenharmony_ci (?P<escaped>$) | 3667db96d56Sopenharmony_ci (?P<named>[_a-z][_a-z0-9]*) | 3677db96d56Sopenharmony_ci @@(?P<braced>[_a-z][_a-z0-9]*)@@ | 3687db96d56Sopenharmony_ci (?P<invalid>) | 3697db96d56Sopenharmony_ci ) 3707db96d56Sopenharmony_ci """ 3717db96d56Sopenharmony_ci 3727db96d56Sopenharmony_ci tmpl = 'PyCon in $@@location@@' 3737db96d56Sopenharmony_ci t = MyTemplate(tmpl) 3747db96d56Sopenharmony_ci self.assertRaises(KeyError, t.substitute, {}) 3757db96d56Sopenharmony_ci val = t.substitute({'location': 'Cleveland'}) 3767db96d56Sopenharmony_ci self.assertEqual(val, 'PyCon in Cleveland') 3777db96d56Sopenharmony_ci 3787db96d56Sopenharmony_ci def test_braced_override_safe(self): 3797db96d56Sopenharmony_ci class MyTemplate(Template): 3807db96d56Sopenharmony_ci pattern = r""" 3817db96d56Sopenharmony_ci \$(?: 3827db96d56Sopenharmony_ci (?P<escaped>$) | 3837db96d56Sopenharmony_ci (?P<named>[_a-z][_a-z0-9]*) | 3847db96d56Sopenharmony_ci @@(?P<braced>[_a-z][_a-z0-9]*)@@ | 3857db96d56Sopenharmony_ci (?P<invalid>) | 3867db96d56Sopenharmony_ci ) 3877db96d56Sopenharmony_ci """ 3887db96d56Sopenharmony_ci 3897db96d56Sopenharmony_ci tmpl = 'PyCon in $@@location@@' 3907db96d56Sopenharmony_ci t = MyTemplate(tmpl) 3917db96d56Sopenharmony_ci self.assertEqual(t.safe_substitute(), tmpl) 3927db96d56Sopenharmony_ci val = t.safe_substitute({'location': 'Cleveland'}) 3937db96d56Sopenharmony_ci self.assertEqual(val, 'PyCon in Cleveland') 3947db96d56Sopenharmony_ci 3957db96d56Sopenharmony_ci def test_invalid_with_no_lines(self): 3967db96d56Sopenharmony_ci # The error formatting for invalid templates 3977db96d56Sopenharmony_ci # has a special case for no data that the default 3987db96d56Sopenharmony_ci # pattern can't trigger (always has at least '$') 3997db96d56Sopenharmony_ci # So we craft a pattern that is always invalid 4007db96d56Sopenharmony_ci # with no leading data. 4017db96d56Sopenharmony_ci class MyTemplate(Template): 4027db96d56Sopenharmony_ci pattern = r""" 4037db96d56Sopenharmony_ci (?P<invalid>) | 4047db96d56Sopenharmony_ci unreachable( 4057db96d56Sopenharmony_ci (?P<named>) | 4067db96d56Sopenharmony_ci (?P<braced>) | 4077db96d56Sopenharmony_ci (?P<escaped>) 4087db96d56Sopenharmony_ci ) 4097db96d56Sopenharmony_ci """ 4107db96d56Sopenharmony_ci s = MyTemplate('') 4117db96d56Sopenharmony_ci with self.assertRaises(ValueError) as err: 4127db96d56Sopenharmony_ci s.substitute({}) 4137db96d56Sopenharmony_ci self.assertIn('line 1, col 1', str(err.exception)) 4147db96d56Sopenharmony_ci 4157db96d56Sopenharmony_ci def test_unicode_values(self): 4167db96d56Sopenharmony_ci s = Template('$who likes $what') 4177db96d56Sopenharmony_ci d = dict(who='t\xffm', what='f\xfe\fed') 4187db96d56Sopenharmony_ci self.assertEqual(s.substitute(d), 't\xffm likes f\xfe\x0ced') 4197db96d56Sopenharmony_ci 4207db96d56Sopenharmony_ci def test_keyword_arguments(self): 4217db96d56Sopenharmony_ci eq = self.assertEqual 4227db96d56Sopenharmony_ci s = Template('$who likes $what') 4237db96d56Sopenharmony_ci eq(s.substitute(who='tim', what='ham'), 'tim likes ham') 4247db96d56Sopenharmony_ci eq(s.substitute(dict(who='tim'), what='ham'), 'tim likes ham') 4257db96d56Sopenharmony_ci eq(s.substitute(dict(who='fred', what='kung pao'), 4267db96d56Sopenharmony_ci who='tim', what='ham'), 4277db96d56Sopenharmony_ci 'tim likes ham') 4287db96d56Sopenharmony_ci s = Template('the mapping is $mapping') 4297db96d56Sopenharmony_ci eq(s.substitute(dict(foo='none'), mapping='bozo'), 4307db96d56Sopenharmony_ci 'the mapping is bozo') 4317db96d56Sopenharmony_ci eq(s.substitute(dict(mapping='one'), mapping='two'), 4327db96d56Sopenharmony_ci 'the mapping is two') 4337db96d56Sopenharmony_ci 4347db96d56Sopenharmony_ci s = Template('the self is $self') 4357db96d56Sopenharmony_ci eq(s.substitute(self='bozo'), 'the self is bozo') 4367db96d56Sopenharmony_ci 4377db96d56Sopenharmony_ci def test_keyword_arguments_safe(self): 4387db96d56Sopenharmony_ci eq = self.assertEqual 4397db96d56Sopenharmony_ci raises = self.assertRaises 4407db96d56Sopenharmony_ci s = Template('$who likes $what') 4417db96d56Sopenharmony_ci eq(s.safe_substitute(who='tim', what='ham'), 'tim likes ham') 4427db96d56Sopenharmony_ci eq(s.safe_substitute(dict(who='tim'), what='ham'), 'tim likes ham') 4437db96d56Sopenharmony_ci eq(s.safe_substitute(dict(who='fred', what='kung pao'), 4447db96d56Sopenharmony_ci who='tim', what='ham'), 4457db96d56Sopenharmony_ci 'tim likes ham') 4467db96d56Sopenharmony_ci s = Template('the mapping is $mapping') 4477db96d56Sopenharmony_ci eq(s.safe_substitute(dict(foo='none'), mapping='bozo'), 4487db96d56Sopenharmony_ci 'the mapping is bozo') 4497db96d56Sopenharmony_ci eq(s.safe_substitute(dict(mapping='one'), mapping='two'), 4507db96d56Sopenharmony_ci 'the mapping is two') 4517db96d56Sopenharmony_ci d = dict(mapping='one') 4527db96d56Sopenharmony_ci raises(TypeError, s.substitute, d, {}) 4537db96d56Sopenharmony_ci raises(TypeError, s.safe_substitute, d, {}) 4547db96d56Sopenharmony_ci 4557db96d56Sopenharmony_ci s = Template('the self is $self') 4567db96d56Sopenharmony_ci eq(s.safe_substitute(self='bozo'), 'the self is bozo') 4577db96d56Sopenharmony_ci 4587db96d56Sopenharmony_ci def test_delimiter_override(self): 4597db96d56Sopenharmony_ci eq = self.assertEqual 4607db96d56Sopenharmony_ci raises = self.assertRaises 4617db96d56Sopenharmony_ci class AmpersandTemplate(Template): 4627db96d56Sopenharmony_ci delimiter = '&' 4637db96d56Sopenharmony_ci s = AmpersandTemplate('this &gift is for &{who} &&') 4647db96d56Sopenharmony_ci eq(s.substitute(gift='bud', who='you'), 'this bud is for you &') 4657db96d56Sopenharmony_ci raises(KeyError, s.substitute) 4667db96d56Sopenharmony_ci eq(s.safe_substitute(gift='bud', who='you'), 'this bud is for you &') 4677db96d56Sopenharmony_ci eq(s.safe_substitute(), 'this &gift is for &{who} &') 4687db96d56Sopenharmony_ci s = AmpersandTemplate('this &gift is for &{who} &') 4697db96d56Sopenharmony_ci raises(ValueError, s.substitute, dict(gift='bud', who='you')) 4707db96d56Sopenharmony_ci eq(s.safe_substitute(), 'this &gift is for &{who} &') 4717db96d56Sopenharmony_ci 4727db96d56Sopenharmony_ci class PieDelims(Template): 4737db96d56Sopenharmony_ci delimiter = '@' 4747db96d56Sopenharmony_ci s = PieDelims('@who likes to eat a bag of @{what} worth $100') 4757db96d56Sopenharmony_ci self.assertEqual(s.substitute(dict(who='tim', what='ham')), 4767db96d56Sopenharmony_ci 'tim likes to eat a bag of ham worth $100') 4777db96d56Sopenharmony_ci 4787db96d56Sopenharmony_ci def test_is_valid(self): 4797db96d56Sopenharmony_ci eq = self.assertEqual 4807db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of ${what} worth $$100') 4817db96d56Sopenharmony_ci self.assertTrue(s.is_valid()) 4827db96d56Sopenharmony_ci 4837db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of ${what} worth $100') 4847db96d56Sopenharmony_ci self.assertFalse(s.is_valid()) 4857db96d56Sopenharmony_ci 4867db96d56Sopenharmony_ci # if the pattern has an unrecognized capture group, 4877db96d56Sopenharmony_ci # it should raise ValueError like substitute and safe_substitute do 4887db96d56Sopenharmony_ci class BadPattern(Template): 4897db96d56Sopenharmony_ci pattern = r""" 4907db96d56Sopenharmony_ci (?P<badname>.*) | 4917db96d56Sopenharmony_ci (?P<escaped>@{2}) | 4927db96d56Sopenharmony_ci @(?P<named>[_a-z][._a-z0-9]*) | 4937db96d56Sopenharmony_ci @{(?P<braced>[_a-z][._a-z0-9]*)} | 4947db96d56Sopenharmony_ci (?P<invalid>@) | 4957db96d56Sopenharmony_ci """ 4967db96d56Sopenharmony_ci s = BadPattern('@bag.foo.who likes to eat a bag of @bag.what') 4977db96d56Sopenharmony_ci self.assertRaises(ValueError, s.is_valid) 4987db96d56Sopenharmony_ci 4997db96d56Sopenharmony_ci def test_get_identifiers(self): 5007db96d56Sopenharmony_ci eq = self.assertEqual 5017db96d56Sopenharmony_ci raises = self.assertRaises 5027db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of ${what} worth $$100') 5037db96d56Sopenharmony_ci ids = s.get_identifiers() 5047db96d56Sopenharmony_ci eq(ids, ['who', 'what']) 5057db96d56Sopenharmony_ci 5067db96d56Sopenharmony_ci # repeated identifiers only included once 5077db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of ${what} worth $$100; ${who} likes to eat a bag of $what worth $$100') 5087db96d56Sopenharmony_ci ids = s.get_identifiers() 5097db96d56Sopenharmony_ci eq(ids, ['who', 'what']) 5107db96d56Sopenharmony_ci 5117db96d56Sopenharmony_ci # invalid identifiers are ignored 5127db96d56Sopenharmony_ci s = Template('$who likes to eat a bag of ${what} worth $100') 5137db96d56Sopenharmony_ci ids = s.get_identifiers() 5147db96d56Sopenharmony_ci eq(ids, ['who', 'what']) 5157db96d56Sopenharmony_ci 5167db96d56Sopenharmony_ci # if the pattern has an unrecognized capture group, 5177db96d56Sopenharmony_ci # it should raise ValueError like substitute and safe_substitute do 5187db96d56Sopenharmony_ci class BadPattern(Template): 5197db96d56Sopenharmony_ci pattern = r""" 5207db96d56Sopenharmony_ci (?P<badname>.*) | 5217db96d56Sopenharmony_ci (?P<escaped>@{2}) | 5227db96d56Sopenharmony_ci @(?P<named>[_a-z][._a-z0-9]*) | 5237db96d56Sopenharmony_ci @{(?P<braced>[_a-z][._a-z0-9]*)} | 5247db96d56Sopenharmony_ci (?P<invalid>@) | 5257db96d56Sopenharmony_ci """ 5267db96d56Sopenharmony_ci s = BadPattern('@bag.foo.who likes to eat a bag of @bag.what') 5277db96d56Sopenharmony_ci self.assertRaises(ValueError, s.get_identifiers) 5287db96d56Sopenharmony_ci 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ciif __name__ == '__main__': 5317db96d56Sopenharmony_ci unittest.main() 532