ext.py (12816B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\jinja2\testsuite\ext.py 2 import re 3 import unittest 4 from jinja2.testsuite import JinjaTestCase 5 from jinja2 import Environment, DictLoader, contextfunction, nodes 6 from jinja2.exceptions import TemplateAssertionError 7 from jinja2.ext import Extension 8 from jinja2.lexer import Token, count_newlines 9 from jinja2.utils import next 10 try: 11 from io import BytesIO 12 except ImportError: 13 from StringIO import StringIO as BytesIO 14 15 importable_object = 23 16 _gettext_re = re.compile('_\\((.*?)\\)(?s)') 17 i18n_templates = {'master.html': '<title>{{ page_title|default(_("missing")) }}</title>{% block body %}{% endblock %}', 18 'child.html': '{% extends "master.html" %}{% block body %}{% trans %}watch out{% endtrans %}{% endblock %}', 19 'plural.html': '{% trans user_count %}One user online{% pluralize %}{{ user_count }} users online{% endtrans %}', 20 'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}'} 21 newstyle_i18n_templates = {'master.html': '<title>{{ page_title|default(_("missing")) }}</title>{% block body %}{% endblock %}', 22 'child.html': '{% extends "master.html" %}{% block body %}{% trans %}watch out{% endtrans %}{% endblock %}', 23 'plural.html': '{% trans user_count %}One user online{% pluralize %}{{ user_count }} users online{% endtrans %}', 24 'stringformat.html': '{{ _("User: %(num)s", num=user_count) }}', 25 'ngettext.html': '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}', 26 'ngettext_long.html': '{% trans num=apples %}{{ num }} apple{% pluralize %}{{ num }} apples{% endtrans %}', 27 'transvars1.html': '{% trans %}User: {{ num }}{% endtrans %}', 28 'transvars2.html': '{% trans num=count %}User: {{ num }}{% endtrans %}', 29 'transvars3.html': '{% trans count=num %}User: {{ count }}{% endtrans %}', 30 'novars.html': '{% trans %}%(hello)s{% endtrans %}', 31 'vars.html': '{% trans %}{{ foo }}%(foo)s{% endtrans %}', 32 'explicitvars.html': '{% trans foo="42" %}%(foo)s{% endtrans %}'} 33 languages = {'de': {'missing': u'fehlend', 34 'watch out': u'pass auf', 35 'One user online': u'Ein Benutzer online', 36 '%(user_count)s users online': u'%(user_count)s Benutzer online', 37 'User: %(num)s': u'Benutzer: %(num)s', 38 'User: %(count)s': u'Benutzer: %(count)s', 39 '%(num)s apple': u'%(num)s Apfel', 40 '%(num)s apples': u'%(num)s \xc4pfel'}} 41 42 @contextfunction 43 def gettext(context, string): 44 language = context.get('LANGUAGE', 'en') 45 return languages.get(language, {}).get(string, string) 46 47 48 @contextfunction 49 def ngettext(context, s, p, n): 50 language = context.get('LANGUAGE', 'en') 51 if n != 1: 52 return languages.get(language, {}).get(p, p) 53 return languages.get(language, {}).get(s, s) 54 55 56 i18n_env = Environment(loader=DictLoader(i18n_templates), extensions=['jinja2.ext.i18n']) 57 i18n_env.globals.update({'_': gettext, 58 'gettext': gettext, 59 'ngettext': ngettext}) 60 newstyle_i18n_env = Environment(loader=DictLoader(newstyle_i18n_templates), extensions=['jinja2.ext.i18n']) 61 newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True) 62 63 class TestExtension(Extension): 64 tags = set(['test']) 65 ext_attr = 42 66 67 def parse(self, parser): 68 return nodes.Output([self.call_method('_dump', [nodes.EnvironmentAttribute('sandboxed'), 69 self.attr('ext_attr'), 70 nodes.ImportedName(__name__ + '.importable_object'), 71 nodes.ContextReference()])]).set_lineno(next(parser.stream).lineno) 72 73 def _dump(self, sandboxed, ext_attr, imported_object, context): 74 return '%s|%s|%s|%s' % (sandboxed, 75 ext_attr, 76 imported_object, 77 context.blocks) 78 79 80 class PreprocessorExtension(Extension): 81 82 def preprocess(self, source, name, filename = None): 83 return source.replace('[[TEST]]', '({{ foo }})') 84 85 86 class StreamFilterExtension(Extension): 87 88 def filter_stream(self, stream): 89 for token in stream: 90 if token.type == 'data': 91 for t in self.interpolate(token): 92 yield t 93 94 else: 95 yield token 96 97 def interpolate(self, token): 98 pos = 0 99 end = len(token.value) 100 lineno = token.lineno 101 while 1: 102 match = _gettext_re.search(token.value, pos) 103 if match is None: 104 break 105 value = token.value[pos:match.start()] 106 if value: 107 yield Token(lineno, 'data', value) 108 lineno += count_newlines(token.value) 109 yield Token(lineno, 'variable_begin', None) 110 yield Token(lineno, 'name', 'gettext') 111 yield Token(lineno, 'lparen', None) 112 yield Token(lineno, 'string', match.group(1)) 113 yield Token(lineno, 'rparen', None) 114 yield Token(lineno, 'variable_end', None) 115 pos = match.end() 116 117 if pos < end: 118 yield Token(lineno, 'data', token.value[pos:]) 119 120 121 class ExtensionsTestCase(JinjaTestCase): 122 123 def test_extend_late(self): 124 env = Environment() 125 env.add_extension('jinja2.ext.autoescape') 126 t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}') 127 128 def test_loop_controls(self): 129 env = Environment(extensions=['jinja2.ext.loopcontrols']) 130 tmpl = env.from_string('\n {%- for item in [1, 2, 3, 4] %}\n {%- if item % 2 == 0 %}{% continue %}{% endif -%}\n {{ item }}\n {%- endfor %}') 131 tmpl = env.from_string('\n {%- for item in [1, 2, 3, 4] %}\n {%- if item > 2 %}{% break %}{% endif -%}\n {{ item }}\n {%- endfor %}') 132 133 def test_do(self): 134 env = Environment(extensions=['jinja2.ext.do']) 135 tmpl = env.from_string('\n {%- set items = [] %}\n {%- for char in "foo" %}\n {%- do items.append(loop.index0 ~ char) %}\n {%- endfor %}{{ items|join(\', \') }}') 136 137 def test_with(self): 138 env = Environment(extensions=['jinja2.ext.with_']) 139 tmpl = env.from_string(' {% with a=42, b=23 -%}\n {{ a }} = {{ b }}\n {% endwith -%}\n {{ a }} = {{ b }} ') 140 141 def test_extension_nodes(self): 142 env = Environment(extensions=[TestExtension]) 143 tmpl = env.from_string('{% test %}') 144 145 def test_identifier(self): 146 pass 147 148 def test_rebinding(self): 149 original = Environment(extensions=[TestExtension]) 150 overlay = original.overlay() 151 for env in (original, overlay): 152 for ext in env.extensions.itervalues(): 153 pass 154 155 def test_preprocessor_extension(self): 156 env = Environment(extensions=[PreprocessorExtension]) 157 tmpl = env.from_string('{[[TEST]]}') 158 159 def test_streamfilter_extension(self): 160 env = Environment(extensions=[StreamFilterExtension]) 161 env.globals['gettext'] = lambda x: x.upper() 162 tmpl = env.from_string('Foo _(bar) Baz') 163 out = tmpl.render() 164 165 def test_extension_ordering(self): 166 167 class T1(Extension): 168 priority = 1 169 170 class T2(Extension): 171 priority = 2 172 173 env = Environment(extensions=[T1, T2]) 174 ext = list(env.iter_extensions()) 175 176 177 class InternationalizationTestCase(JinjaTestCase): 178 179 def test_trans(self): 180 tmpl = i18n_env.get_template('child.html') 181 182 def test_trans_plural(self): 183 tmpl = i18n_env.get_template('plural.html') 184 185 def test_complex_plural(self): 186 tmpl = i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% pluralize count %}{{ count }} items{% endtrans %}') 187 self.assert_raises(TemplateAssertionError, i18n_env.from_string, '{% trans foo %}...{% pluralize bar %}...{% endtrans %}') 188 189 def test_trans_stringformatting(self): 190 tmpl = i18n_env.get_template('stringformat.html') 191 192 def test_extract(self): 193 from jinja2.ext import babel_extract 194 source = BytesIO("\n {{ gettext('Hello World') }}\n {% trans %}Hello World{% endtrans %}\n {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %}\n ".encode('ascii')) 195 196 def test_comment_extract(self): 197 from jinja2.ext import babel_extract 198 source = BytesIO("\n {# trans first #}\n {{ gettext('Hello World') }}\n {% trans %}Hello World{% endtrans %}{# trans second #}\n {#: third #}\n {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %}\n ".encode('utf-8')) 199 200 201 class NewstyleInternationalizationTestCase(JinjaTestCase): 202 203 def test_trans(self): 204 tmpl = newstyle_i18n_env.get_template('child.html') 205 206 def test_trans_plural(self): 207 tmpl = newstyle_i18n_env.get_template('plural.html') 208 209 def test_complex_plural(self): 210 tmpl = newstyle_i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% pluralize count %}{{ count }} items{% endtrans %}') 211 self.assert_raises(TemplateAssertionError, i18n_env.from_string, '{% trans foo %}...{% pluralize bar %}...{% endtrans %}') 212 213 def test_trans_stringformatting(self): 214 tmpl = newstyle_i18n_env.get_template('stringformat.html') 215 216 def test_newstyle_plural(self): 217 tmpl = newstyle_i18n_env.get_template('ngettext.html') 218 219 def test_autoescape_support(self): 220 env = Environment(extensions=['jinja2.ext.autoescape', 'jinja2.ext.i18n']) 221 env.install_gettext_callables(lambda x: u'<strong>Wert: %(name)s</strong>', lambda s, p, n: s, newstyle=True) 222 t = env.from_string('{% autoescape ae %}{{ gettext("foo", name="<test>") }}{% endautoescape %}') 223 224 def test_num_used_twice(self): 225 tmpl = newstyle_i18n_env.get_template('ngettext_long.html') 226 227 def test_num_called_num(self): 228 source = newstyle_i18n_env.compile('\n {% trans num=3 %}{{ num }} apple{% pluralize\n %}{{ num }} apples{% endtrans %}\n ', raw=True) 229 230 def test_trans_vars(self): 231 t1 = newstyle_i18n_env.get_template('transvars1.html') 232 t2 = newstyle_i18n_env.get_template('transvars2.html') 233 t3 = newstyle_i18n_env.get_template('transvars3.html') 234 235 def test_novars_vars_escaping(self): 236 t = newstyle_i18n_env.get_template('novars.html') 237 t = newstyle_i18n_env.get_template('vars.html') 238 t = newstyle_i18n_env.get_template('explicitvars.html') 239 240 241 class AutoEscapeTestCase(JinjaTestCase): 242 243 def test_scoped_setting(self): 244 env = Environment(extensions=['jinja2.ext.autoescape'], autoescape=True) 245 tmpl = env.from_string('\n {{ "<HelloWorld>" }}\n {% autoescape false %}\n {{ "<HelloWorld>" }}\n {% endautoescape %}\n {{ "<HelloWorld>" }}\n ') 246 env = Environment(extensions=['jinja2.ext.autoescape'], autoescape=False) 247 tmpl = env.from_string('\n {{ "<HelloWorld>" }}\n {% autoescape true %}\n {{ "<HelloWorld>" }}\n {% endautoescape %}\n {{ "<HelloWorld>" }}\n ') 248 249 def test_nonvolatile(self): 250 env = Environment(extensions=['jinja2.ext.autoescape'], autoescape=True) 251 tmpl = env.from_string('{{ {"foo": "<test>"}|xmlattr|escape }}') 252 tmpl = env.from_string('{% autoescape false %}{{ {"foo": "<test>"}|xmlattr|escape }}{% endautoescape %}') 253 254 def test_volatile(self): 255 env = Environment(extensions=['jinja2.ext.autoescape'], autoescape=True) 256 tmpl = env.from_string('{% autoescape foo %}{{ {"foo": "<test>"}|xmlattr|escape }}{% endautoescape %}') 257 258 def test_scoping(self): 259 env = Environment(extensions=['jinja2.ext.autoescape']) 260 tmpl = env.from_string('{% autoescape true %}{% set x = "<x>" %}{{ x }}{% endautoescape %}{{ x }}{{ "<y>" }}') 261 262 def test_volatile_scoping(self): 263 env = Environment(extensions=['jinja2.ext.autoescape']) 264 tmplsource = "\n {% autoescape val %}\n {% macro foo(x) %}\n [{{ x }}]\n {% endmacro %}\n {{ foo().__class__.__name__ }}\n {% endautoescape %}\n {{ '<testing>' }}\n " 265 tmpl = env.from_string(tmplsource) 266 env = Environment(extensions=['jinja2.ext.autoescape']) 267 pysource = env.compile(tmplsource, raw=True) 268 env = Environment(extensions=['jinja2.ext.autoescape'], autoescape=True) 269 pysource = env.compile(tmplsource, raw=True) 270 271 272 def suite(): 273 suite = unittest.TestSuite() 274 suite.addTest(unittest.makeSuite(ExtensionsTestCase)) 275 suite.addTest(unittest.makeSuite(InternationalizationTestCase)) 276 suite.addTest(unittest.makeSuite(NewstyleInternationalizationTestCase)) 277 suite.addTest(unittest.makeSuite(AutoEscapeTestCase)) 278 return suite