xref: /aosp_15_r20/external/libchrome/third_party/jinja2/ext.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2*635a8641SAndroid Build Coastguard Worker"""
3*635a8641SAndroid Build Coastguard Worker    jinja2.ext
4*635a8641SAndroid Build Coastguard Worker    ~~~~~~~~~~
5*635a8641SAndroid Build Coastguard Worker
6*635a8641SAndroid Build Coastguard Worker    Jinja extensions allow to add custom tags similar to the way django custom
7*635a8641SAndroid Build Coastguard Worker    tags work.  By default two example extensions exist: an i18n and a cache
8*635a8641SAndroid Build Coastguard Worker    extension.
9*635a8641SAndroid Build Coastguard Worker
10*635a8641SAndroid Build Coastguard Worker    :copyright: (c) 2017 by the Jinja Team.
11*635a8641SAndroid Build Coastguard Worker    :license: BSD.
12*635a8641SAndroid Build Coastguard Worker"""
13*635a8641SAndroid Build Coastguard Workerimport re
14*635a8641SAndroid Build Coastguard Worker
15*635a8641SAndroid Build Coastguard Workerfrom jinja2 import nodes
16*635a8641SAndroid Build Coastguard Workerfrom jinja2.defaults import BLOCK_START_STRING, \
17*635a8641SAndroid Build Coastguard Worker     BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
18*635a8641SAndroid Build Coastguard Worker     COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
19*635a8641SAndroid Build Coastguard Worker     LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
20*635a8641SAndroid Build Coastguard Worker     KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
21*635a8641SAndroid Build Coastguard Workerfrom jinja2.environment import Environment
22*635a8641SAndroid Build Coastguard Workerfrom jinja2.runtime import concat
23*635a8641SAndroid Build Coastguard Workerfrom jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
24*635a8641SAndroid Build Coastguard Workerfrom jinja2.utils import contextfunction, import_string, Markup
25*635a8641SAndroid Build Coastguard Workerfrom jinja2._compat import with_metaclass, string_types, iteritems
26*635a8641SAndroid Build Coastguard Worker
27*635a8641SAndroid Build Coastguard Worker
28*635a8641SAndroid Build Coastguard Worker# the only real useful gettext functions for a Jinja template.  Note
29*635a8641SAndroid Build Coastguard Worker# that ugettext must be assigned to gettext as Jinja doesn't support
30*635a8641SAndroid Build Coastguard Worker# non unicode strings.
31*635a8641SAndroid Build Coastguard WorkerGETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
32*635a8641SAndroid Build Coastguard Worker
33*635a8641SAndroid Build Coastguard Worker
34*635a8641SAndroid Build Coastguard Workerclass ExtensionRegistry(type):
35*635a8641SAndroid Build Coastguard Worker    """Gives the extension an unique identifier."""
36*635a8641SAndroid Build Coastguard Worker
37*635a8641SAndroid Build Coastguard Worker    def __new__(cls, name, bases, d):
38*635a8641SAndroid Build Coastguard Worker        rv = type.__new__(cls, name, bases, d)
39*635a8641SAndroid Build Coastguard Worker        rv.identifier = rv.__module__ + '.' + rv.__name__
40*635a8641SAndroid Build Coastguard Worker        return rv
41*635a8641SAndroid Build Coastguard Worker
42*635a8641SAndroid Build Coastguard Worker
43*635a8641SAndroid Build Coastguard Workerclass Extension(with_metaclass(ExtensionRegistry, object)):
44*635a8641SAndroid Build Coastguard Worker    """Extensions can be used to add extra functionality to the Jinja template
45*635a8641SAndroid Build Coastguard Worker    system at the parser level.  Custom extensions are bound to an environment
46*635a8641SAndroid Build Coastguard Worker    but may not store environment specific data on `self`.  The reason for
47*635a8641SAndroid Build Coastguard Worker    this is that an extension can be bound to another environment (for
48*635a8641SAndroid Build Coastguard Worker    overlays) by creating a copy and reassigning the `environment` attribute.
49*635a8641SAndroid Build Coastguard Worker
50*635a8641SAndroid Build Coastguard Worker    As extensions are created by the environment they cannot accept any
51*635a8641SAndroid Build Coastguard Worker    arguments for configuration.  One may want to work around that by using
52*635a8641SAndroid Build Coastguard Worker    a factory function, but that is not possible as extensions are identified
53*635a8641SAndroid Build Coastguard Worker    by their import name.  The correct way to configure the extension is
54*635a8641SAndroid Build Coastguard Worker    storing the configuration values on the environment.  Because this way the
55*635a8641SAndroid Build Coastguard Worker    environment ends up acting as central configuration storage the
56*635a8641SAndroid Build Coastguard Worker    attributes may clash which is why extensions have to ensure that the names
57*635a8641SAndroid Build Coastguard Worker    they choose for configuration are not too generic.  ``prefix`` for example
58*635a8641SAndroid Build Coastguard Worker    is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
59*635a8641SAndroid Build Coastguard Worker    name as includes the name of the extension (fragment cache).
60*635a8641SAndroid Build Coastguard Worker    """
61*635a8641SAndroid Build Coastguard Worker
62*635a8641SAndroid Build Coastguard Worker    #: if this extension parses this is the list of tags it's listening to.
63*635a8641SAndroid Build Coastguard Worker    tags = set()
64*635a8641SAndroid Build Coastguard Worker
65*635a8641SAndroid Build Coastguard Worker    #: the priority of that extension.  This is especially useful for
66*635a8641SAndroid Build Coastguard Worker    #: extensions that preprocess values.  A lower value means higher
67*635a8641SAndroid Build Coastguard Worker    #: priority.
68*635a8641SAndroid Build Coastguard Worker    #:
69*635a8641SAndroid Build Coastguard Worker    #: .. versionadded:: 2.4
70*635a8641SAndroid Build Coastguard Worker    priority = 100
71*635a8641SAndroid Build Coastguard Worker
72*635a8641SAndroid Build Coastguard Worker    def __init__(self, environment):
73*635a8641SAndroid Build Coastguard Worker        self.environment = environment
74*635a8641SAndroid Build Coastguard Worker
75*635a8641SAndroid Build Coastguard Worker    def bind(self, environment):
76*635a8641SAndroid Build Coastguard Worker        """Create a copy of this extension bound to another environment."""
77*635a8641SAndroid Build Coastguard Worker        rv = object.__new__(self.__class__)
78*635a8641SAndroid Build Coastguard Worker        rv.__dict__.update(self.__dict__)
79*635a8641SAndroid Build Coastguard Worker        rv.environment = environment
80*635a8641SAndroid Build Coastguard Worker        return rv
81*635a8641SAndroid Build Coastguard Worker
82*635a8641SAndroid Build Coastguard Worker    def preprocess(self, source, name, filename=None):
83*635a8641SAndroid Build Coastguard Worker        """This method is called before the actual lexing and can be used to
84*635a8641SAndroid Build Coastguard Worker        preprocess the source.  The `filename` is optional.  The return value
85*635a8641SAndroid Build Coastguard Worker        must be the preprocessed source.
86*635a8641SAndroid Build Coastguard Worker        """
87*635a8641SAndroid Build Coastguard Worker        return source
88*635a8641SAndroid Build Coastguard Worker
89*635a8641SAndroid Build Coastguard Worker    def filter_stream(self, stream):
90*635a8641SAndroid Build Coastguard Worker        """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
91*635a8641SAndroid Build Coastguard Worker        to filter tokens returned.  This method has to return an iterable of
92*635a8641SAndroid Build Coastguard Worker        :class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
93*635a8641SAndroid Build Coastguard Worker        :class:`~jinja2.lexer.TokenStream`.
94*635a8641SAndroid Build Coastguard Worker
95*635a8641SAndroid Build Coastguard Worker        In the `ext` folder of the Jinja2 source distribution there is a file
96*635a8641SAndroid Build Coastguard Worker        called `inlinegettext.py` which implements a filter that utilizes this
97*635a8641SAndroid Build Coastguard Worker        method.
98*635a8641SAndroid Build Coastguard Worker        """
99*635a8641SAndroid Build Coastguard Worker        return stream
100*635a8641SAndroid Build Coastguard Worker
101*635a8641SAndroid Build Coastguard Worker    def parse(self, parser):
102*635a8641SAndroid Build Coastguard Worker        """If any of the :attr:`tags` matched this method is called with the
103*635a8641SAndroid Build Coastguard Worker        parser as first argument.  The token the parser stream is pointing at
104*635a8641SAndroid Build Coastguard Worker        is the name token that matched.  This method has to return one or a
105*635a8641SAndroid Build Coastguard Worker        list of multiple nodes.
106*635a8641SAndroid Build Coastguard Worker        """
107*635a8641SAndroid Build Coastguard Worker        raise NotImplementedError()
108*635a8641SAndroid Build Coastguard Worker
109*635a8641SAndroid Build Coastguard Worker    def attr(self, name, lineno=None):
110*635a8641SAndroid Build Coastguard Worker        """Return an attribute node for the current extension.  This is useful
111*635a8641SAndroid Build Coastguard Worker        to pass constants on extensions to generated template code.
112*635a8641SAndroid Build Coastguard Worker
113*635a8641SAndroid Build Coastguard Worker        ::
114*635a8641SAndroid Build Coastguard Worker
115*635a8641SAndroid Build Coastguard Worker            self.attr('_my_attribute', lineno=lineno)
116*635a8641SAndroid Build Coastguard Worker        """
117*635a8641SAndroid Build Coastguard Worker        return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
118*635a8641SAndroid Build Coastguard Worker
119*635a8641SAndroid Build Coastguard Worker    def call_method(self, name, args=None, kwargs=None, dyn_args=None,
120*635a8641SAndroid Build Coastguard Worker                    dyn_kwargs=None, lineno=None):
121*635a8641SAndroid Build Coastguard Worker        """Call a method of the extension.  This is a shortcut for
122*635a8641SAndroid Build Coastguard Worker        :meth:`attr` + :class:`jinja2.nodes.Call`.
123*635a8641SAndroid Build Coastguard Worker        """
124*635a8641SAndroid Build Coastguard Worker        if args is None:
125*635a8641SAndroid Build Coastguard Worker            args = []
126*635a8641SAndroid Build Coastguard Worker        if kwargs is None:
127*635a8641SAndroid Build Coastguard Worker            kwargs = []
128*635a8641SAndroid Build Coastguard Worker        return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
129*635a8641SAndroid Build Coastguard Worker                          dyn_args, dyn_kwargs, lineno=lineno)
130*635a8641SAndroid Build Coastguard Worker
131*635a8641SAndroid Build Coastguard Worker
132*635a8641SAndroid Build Coastguard Worker@contextfunction
133*635a8641SAndroid Build Coastguard Workerdef _gettext_alias(__context, *args, **kwargs):
134*635a8641SAndroid Build Coastguard Worker    return __context.call(__context.resolve('gettext'), *args, **kwargs)
135*635a8641SAndroid Build Coastguard Worker
136*635a8641SAndroid Build Coastguard Worker
137*635a8641SAndroid Build Coastguard Workerdef _make_new_gettext(func):
138*635a8641SAndroid Build Coastguard Worker    @contextfunction
139*635a8641SAndroid Build Coastguard Worker    def gettext(__context, __string, **variables):
140*635a8641SAndroid Build Coastguard Worker        rv = __context.call(func, __string)
141*635a8641SAndroid Build Coastguard Worker        if __context.eval_ctx.autoescape:
142*635a8641SAndroid Build Coastguard Worker            rv = Markup(rv)
143*635a8641SAndroid Build Coastguard Worker        return rv % variables
144*635a8641SAndroid Build Coastguard Worker    return gettext
145*635a8641SAndroid Build Coastguard Worker
146*635a8641SAndroid Build Coastguard Worker
147*635a8641SAndroid Build Coastguard Workerdef _make_new_ngettext(func):
148*635a8641SAndroid Build Coastguard Worker    @contextfunction
149*635a8641SAndroid Build Coastguard Worker    def ngettext(__context, __singular, __plural, __num, **variables):
150*635a8641SAndroid Build Coastguard Worker        variables.setdefault('num', __num)
151*635a8641SAndroid Build Coastguard Worker        rv = __context.call(func, __singular, __plural, __num)
152*635a8641SAndroid Build Coastguard Worker        if __context.eval_ctx.autoescape:
153*635a8641SAndroid Build Coastguard Worker            rv = Markup(rv)
154*635a8641SAndroid Build Coastguard Worker        return rv % variables
155*635a8641SAndroid Build Coastguard Worker    return ngettext
156*635a8641SAndroid Build Coastguard Worker
157*635a8641SAndroid Build Coastguard Worker
158*635a8641SAndroid Build Coastguard Workerclass InternationalizationExtension(Extension):
159*635a8641SAndroid Build Coastguard Worker    """This extension adds gettext support to Jinja2."""
160*635a8641SAndroid Build Coastguard Worker    tags = set(['trans'])
161*635a8641SAndroid Build Coastguard Worker
162*635a8641SAndroid Build Coastguard Worker    # TODO: the i18n extension is currently reevaluating values in a few
163*635a8641SAndroid Build Coastguard Worker    # situations.  Take this example:
164*635a8641SAndroid Build Coastguard Worker    #   {% trans count=something() %}{{ count }} foo{% pluralize
165*635a8641SAndroid Build Coastguard Worker    #     %}{{ count }} fooss{% endtrans %}
166*635a8641SAndroid Build Coastguard Worker    # something is called twice here.  One time for the gettext value and
167*635a8641SAndroid Build Coastguard Worker    # the other time for the n-parameter of the ngettext function.
168*635a8641SAndroid Build Coastguard Worker
169*635a8641SAndroid Build Coastguard Worker    def __init__(self, environment):
170*635a8641SAndroid Build Coastguard Worker        Extension.__init__(self, environment)
171*635a8641SAndroid Build Coastguard Worker        environment.globals['_'] = _gettext_alias
172*635a8641SAndroid Build Coastguard Worker        environment.extend(
173*635a8641SAndroid Build Coastguard Worker            install_gettext_translations=self._install,
174*635a8641SAndroid Build Coastguard Worker            install_null_translations=self._install_null,
175*635a8641SAndroid Build Coastguard Worker            install_gettext_callables=self._install_callables,
176*635a8641SAndroid Build Coastguard Worker            uninstall_gettext_translations=self._uninstall,
177*635a8641SAndroid Build Coastguard Worker            extract_translations=self._extract,
178*635a8641SAndroid Build Coastguard Worker            newstyle_gettext=False
179*635a8641SAndroid Build Coastguard Worker        )
180*635a8641SAndroid Build Coastguard Worker
181*635a8641SAndroid Build Coastguard Worker    def _install(self, translations, newstyle=None):
182*635a8641SAndroid Build Coastguard Worker        gettext = getattr(translations, 'ugettext', None)
183*635a8641SAndroid Build Coastguard Worker        if gettext is None:
184*635a8641SAndroid Build Coastguard Worker            gettext = translations.gettext
185*635a8641SAndroid Build Coastguard Worker        ngettext = getattr(translations, 'ungettext', None)
186*635a8641SAndroid Build Coastguard Worker        if ngettext is None:
187*635a8641SAndroid Build Coastguard Worker            ngettext = translations.ngettext
188*635a8641SAndroid Build Coastguard Worker        self._install_callables(gettext, ngettext, newstyle)
189*635a8641SAndroid Build Coastguard Worker
190*635a8641SAndroid Build Coastguard Worker    def _install_null(self, newstyle=None):
191*635a8641SAndroid Build Coastguard Worker        self._install_callables(
192*635a8641SAndroid Build Coastguard Worker            lambda x: x,
193*635a8641SAndroid Build Coastguard Worker            lambda s, p, n: (n != 1 and (p,) or (s,))[0],
194*635a8641SAndroid Build Coastguard Worker            newstyle
195*635a8641SAndroid Build Coastguard Worker        )
196*635a8641SAndroid Build Coastguard Worker
197*635a8641SAndroid Build Coastguard Worker    def _install_callables(self, gettext, ngettext, newstyle=None):
198*635a8641SAndroid Build Coastguard Worker        if newstyle is not None:
199*635a8641SAndroid Build Coastguard Worker            self.environment.newstyle_gettext = newstyle
200*635a8641SAndroid Build Coastguard Worker        if self.environment.newstyle_gettext:
201*635a8641SAndroid Build Coastguard Worker            gettext = _make_new_gettext(gettext)
202*635a8641SAndroid Build Coastguard Worker            ngettext = _make_new_ngettext(ngettext)
203*635a8641SAndroid Build Coastguard Worker        self.environment.globals.update(
204*635a8641SAndroid Build Coastguard Worker            gettext=gettext,
205*635a8641SAndroid Build Coastguard Worker            ngettext=ngettext
206*635a8641SAndroid Build Coastguard Worker        )
207*635a8641SAndroid Build Coastguard Worker
208*635a8641SAndroid Build Coastguard Worker    def _uninstall(self, translations):
209*635a8641SAndroid Build Coastguard Worker        for key in 'gettext', 'ngettext':
210*635a8641SAndroid Build Coastguard Worker            self.environment.globals.pop(key, None)
211*635a8641SAndroid Build Coastguard Worker
212*635a8641SAndroid Build Coastguard Worker    def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
213*635a8641SAndroid Build Coastguard Worker        if isinstance(source, string_types):
214*635a8641SAndroid Build Coastguard Worker            source = self.environment.parse(source)
215*635a8641SAndroid Build Coastguard Worker        return extract_from_ast(source, gettext_functions)
216*635a8641SAndroid Build Coastguard Worker
217*635a8641SAndroid Build Coastguard Worker    def parse(self, parser):
218*635a8641SAndroid Build Coastguard Worker        """Parse a translatable tag."""
219*635a8641SAndroid Build Coastguard Worker        lineno = next(parser.stream).lineno
220*635a8641SAndroid Build Coastguard Worker        num_called_num = False
221*635a8641SAndroid Build Coastguard Worker
222*635a8641SAndroid Build Coastguard Worker        # find all the variables referenced.  Additionally a variable can be
223*635a8641SAndroid Build Coastguard Worker        # defined in the body of the trans block too, but this is checked at
224*635a8641SAndroid Build Coastguard Worker        # a later state.
225*635a8641SAndroid Build Coastguard Worker        plural_expr = None
226*635a8641SAndroid Build Coastguard Worker        plural_expr_assignment = None
227*635a8641SAndroid Build Coastguard Worker        variables = {}
228*635a8641SAndroid Build Coastguard Worker        trimmed = None
229*635a8641SAndroid Build Coastguard Worker        while parser.stream.current.type != 'block_end':
230*635a8641SAndroid Build Coastguard Worker            if variables:
231*635a8641SAndroid Build Coastguard Worker                parser.stream.expect('comma')
232*635a8641SAndroid Build Coastguard Worker
233*635a8641SAndroid Build Coastguard Worker            # skip colon for python compatibility
234*635a8641SAndroid Build Coastguard Worker            if parser.stream.skip_if('colon'):
235*635a8641SAndroid Build Coastguard Worker                break
236*635a8641SAndroid Build Coastguard Worker
237*635a8641SAndroid Build Coastguard Worker            name = parser.stream.expect('name')
238*635a8641SAndroid Build Coastguard Worker            if name.value in variables:
239*635a8641SAndroid Build Coastguard Worker                parser.fail('translatable variable %r defined twice.' %
240*635a8641SAndroid Build Coastguard Worker                            name.value, name.lineno,
241*635a8641SAndroid Build Coastguard Worker                            exc=TemplateAssertionError)
242*635a8641SAndroid Build Coastguard Worker
243*635a8641SAndroid Build Coastguard Worker            # expressions
244*635a8641SAndroid Build Coastguard Worker            if parser.stream.current.type == 'assign':
245*635a8641SAndroid Build Coastguard Worker                next(parser.stream)
246*635a8641SAndroid Build Coastguard Worker                variables[name.value] = var = parser.parse_expression()
247*635a8641SAndroid Build Coastguard Worker            elif trimmed is None and name.value in ('trimmed', 'notrimmed'):
248*635a8641SAndroid Build Coastguard Worker                trimmed = name.value == 'trimmed'
249*635a8641SAndroid Build Coastguard Worker                continue
250*635a8641SAndroid Build Coastguard Worker            else:
251*635a8641SAndroid Build Coastguard Worker                variables[name.value] = var = nodes.Name(name.value, 'load')
252*635a8641SAndroid Build Coastguard Worker
253*635a8641SAndroid Build Coastguard Worker            if plural_expr is None:
254*635a8641SAndroid Build Coastguard Worker                if isinstance(var, nodes.Call):
255*635a8641SAndroid Build Coastguard Worker                    plural_expr = nodes.Name('_trans', 'load')
256*635a8641SAndroid Build Coastguard Worker                    variables[name.value] = plural_expr
257*635a8641SAndroid Build Coastguard Worker                    plural_expr_assignment = nodes.Assign(
258*635a8641SAndroid Build Coastguard Worker                        nodes.Name('_trans', 'store'), var)
259*635a8641SAndroid Build Coastguard Worker                else:
260*635a8641SAndroid Build Coastguard Worker                    plural_expr = var
261*635a8641SAndroid Build Coastguard Worker                num_called_num = name.value == 'num'
262*635a8641SAndroid Build Coastguard Worker
263*635a8641SAndroid Build Coastguard Worker        parser.stream.expect('block_end')
264*635a8641SAndroid Build Coastguard Worker
265*635a8641SAndroid Build Coastguard Worker        plural = None
266*635a8641SAndroid Build Coastguard Worker        have_plural = False
267*635a8641SAndroid Build Coastguard Worker        referenced = set()
268*635a8641SAndroid Build Coastguard Worker
269*635a8641SAndroid Build Coastguard Worker        # now parse until endtrans or pluralize
270*635a8641SAndroid Build Coastguard Worker        singular_names, singular = self._parse_block(parser, True)
271*635a8641SAndroid Build Coastguard Worker        if singular_names:
272*635a8641SAndroid Build Coastguard Worker            referenced.update(singular_names)
273*635a8641SAndroid Build Coastguard Worker            if plural_expr is None:
274*635a8641SAndroid Build Coastguard Worker                plural_expr = nodes.Name(singular_names[0], 'load')
275*635a8641SAndroid Build Coastguard Worker                num_called_num = singular_names[0] == 'num'
276*635a8641SAndroid Build Coastguard Worker
277*635a8641SAndroid Build Coastguard Worker        # if we have a pluralize block, we parse that too
278*635a8641SAndroid Build Coastguard Worker        if parser.stream.current.test('name:pluralize'):
279*635a8641SAndroid Build Coastguard Worker            have_plural = True
280*635a8641SAndroid Build Coastguard Worker            next(parser.stream)
281*635a8641SAndroid Build Coastguard Worker            if parser.stream.current.type != 'block_end':
282*635a8641SAndroid Build Coastguard Worker                name = parser.stream.expect('name')
283*635a8641SAndroid Build Coastguard Worker                if name.value not in variables:
284*635a8641SAndroid Build Coastguard Worker                    parser.fail('unknown variable %r for pluralization' %
285*635a8641SAndroid Build Coastguard Worker                                name.value, name.lineno,
286*635a8641SAndroid Build Coastguard Worker                                exc=TemplateAssertionError)
287*635a8641SAndroid Build Coastguard Worker                plural_expr = variables[name.value]
288*635a8641SAndroid Build Coastguard Worker                num_called_num = name.value == 'num'
289*635a8641SAndroid Build Coastguard Worker            parser.stream.expect('block_end')
290*635a8641SAndroid Build Coastguard Worker            plural_names, plural = self._parse_block(parser, False)
291*635a8641SAndroid Build Coastguard Worker            next(parser.stream)
292*635a8641SAndroid Build Coastguard Worker            referenced.update(plural_names)
293*635a8641SAndroid Build Coastguard Worker        else:
294*635a8641SAndroid Build Coastguard Worker            next(parser.stream)
295*635a8641SAndroid Build Coastguard Worker
296*635a8641SAndroid Build Coastguard Worker        # register free names as simple name expressions
297*635a8641SAndroid Build Coastguard Worker        for var in referenced:
298*635a8641SAndroid Build Coastguard Worker            if var not in variables:
299*635a8641SAndroid Build Coastguard Worker                variables[var] = nodes.Name(var, 'load')
300*635a8641SAndroid Build Coastguard Worker
301*635a8641SAndroid Build Coastguard Worker        if not have_plural:
302*635a8641SAndroid Build Coastguard Worker            plural_expr = None
303*635a8641SAndroid Build Coastguard Worker        elif plural_expr is None:
304*635a8641SAndroid Build Coastguard Worker            parser.fail('pluralize without variables', lineno)
305*635a8641SAndroid Build Coastguard Worker
306*635a8641SAndroid Build Coastguard Worker        if trimmed is None:
307*635a8641SAndroid Build Coastguard Worker            trimmed = self.environment.policies['ext.i18n.trimmed']
308*635a8641SAndroid Build Coastguard Worker        if trimmed:
309*635a8641SAndroid Build Coastguard Worker            singular = self._trim_whitespace(singular)
310*635a8641SAndroid Build Coastguard Worker            if plural:
311*635a8641SAndroid Build Coastguard Worker                plural = self._trim_whitespace(plural)
312*635a8641SAndroid Build Coastguard Worker
313*635a8641SAndroid Build Coastguard Worker        node = self._make_node(singular, plural, variables, plural_expr,
314*635a8641SAndroid Build Coastguard Worker                               bool(referenced),
315*635a8641SAndroid Build Coastguard Worker                               num_called_num and have_plural)
316*635a8641SAndroid Build Coastguard Worker        node.set_lineno(lineno)
317*635a8641SAndroid Build Coastguard Worker        if plural_expr_assignment is not None:
318*635a8641SAndroid Build Coastguard Worker            return [plural_expr_assignment, node]
319*635a8641SAndroid Build Coastguard Worker        else:
320*635a8641SAndroid Build Coastguard Worker            return node
321*635a8641SAndroid Build Coastguard Worker
322*635a8641SAndroid Build Coastguard Worker    def _trim_whitespace(self, string, _ws_re=re.compile(r'\s*\n\s*')):
323*635a8641SAndroid Build Coastguard Worker        return _ws_re.sub(' ', string.strip())
324*635a8641SAndroid Build Coastguard Worker
325*635a8641SAndroid Build Coastguard Worker    def _parse_block(self, parser, allow_pluralize):
326*635a8641SAndroid Build Coastguard Worker        """Parse until the next block tag with a given name."""
327*635a8641SAndroid Build Coastguard Worker        referenced = []
328*635a8641SAndroid Build Coastguard Worker        buf = []
329*635a8641SAndroid Build Coastguard Worker        while 1:
330*635a8641SAndroid Build Coastguard Worker            if parser.stream.current.type == 'data':
331*635a8641SAndroid Build Coastguard Worker                buf.append(parser.stream.current.value.replace('%', '%%'))
332*635a8641SAndroid Build Coastguard Worker                next(parser.stream)
333*635a8641SAndroid Build Coastguard Worker            elif parser.stream.current.type == 'variable_begin':
334*635a8641SAndroid Build Coastguard Worker                next(parser.stream)
335*635a8641SAndroid Build Coastguard Worker                name = parser.stream.expect('name').value
336*635a8641SAndroid Build Coastguard Worker                referenced.append(name)
337*635a8641SAndroid Build Coastguard Worker                buf.append('%%(%s)s' % name)
338*635a8641SAndroid Build Coastguard Worker                parser.stream.expect('variable_end')
339*635a8641SAndroid Build Coastguard Worker            elif parser.stream.current.type == 'block_begin':
340*635a8641SAndroid Build Coastguard Worker                next(parser.stream)
341*635a8641SAndroid Build Coastguard Worker                if parser.stream.current.test('name:endtrans'):
342*635a8641SAndroid Build Coastguard Worker                    break
343*635a8641SAndroid Build Coastguard Worker                elif parser.stream.current.test('name:pluralize'):
344*635a8641SAndroid Build Coastguard Worker                    if allow_pluralize:
345*635a8641SAndroid Build Coastguard Worker                        break
346*635a8641SAndroid Build Coastguard Worker                    parser.fail('a translatable section can have only one '
347*635a8641SAndroid Build Coastguard Worker                                'pluralize section')
348*635a8641SAndroid Build Coastguard Worker                parser.fail('control structures in translatable sections are '
349*635a8641SAndroid Build Coastguard Worker                            'not allowed')
350*635a8641SAndroid Build Coastguard Worker            elif parser.stream.eos:
351*635a8641SAndroid Build Coastguard Worker                parser.fail('unclosed translation block')
352*635a8641SAndroid Build Coastguard Worker            else:
353*635a8641SAndroid Build Coastguard Worker                assert False, 'internal parser error'
354*635a8641SAndroid Build Coastguard Worker
355*635a8641SAndroid Build Coastguard Worker        return referenced, concat(buf)
356*635a8641SAndroid Build Coastguard Worker
357*635a8641SAndroid Build Coastguard Worker    def _make_node(self, singular, plural, variables, plural_expr,
358*635a8641SAndroid Build Coastguard Worker                   vars_referenced, num_called_num):
359*635a8641SAndroid Build Coastguard Worker        """Generates a useful node from the data provided."""
360*635a8641SAndroid Build Coastguard Worker        # no variables referenced?  no need to escape for old style
361*635a8641SAndroid Build Coastguard Worker        # gettext invocations only if there are vars.
362*635a8641SAndroid Build Coastguard Worker        if not vars_referenced and not self.environment.newstyle_gettext:
363*635a8641SAndroid Build Coastguard Worker            singular = singular.replace('%%', '%')
364*635a8641SAndroid Build Coastguard Worker            if plural:
365*635a8641SAndroid Build Coastguard Worker                plural = plural.replace('%%', '%')
366*635a8641SAndroid Build Coastguard Worker
367*635a8641SAndroid Build Coastguard Worker        # singular only:
368*635a8641SAndroid Build Coastguard Worker        if plural_expr is None:
369*635a8641SAndroid Build Coastguard Worker            gettext = nodes.Name('gettext', 'load')
370*635a8641SAndroid Build Coastguard Worker            node = nodes.Call(gettext, [nodes.Const(singular)],
371*635a8641SAndroid Build Coastguard Worker                              [], None, None)
372*635a8641SAndroid Build Coastguard Worker
373*635a8641SAndroid Build Coastguard Worker        # singular and plural
374*635a8641SAndroid Build Coastguard Worker        else:
375*635a8641SAndroid Build Coastguard Worker            ngettext = nodes.Name('ngettext', 'load')
376*635a8641SAndroid Build Coastguard Worker            node = nodes.Call(ngettext, [
377*635a8641SAndroid Build Coastguard Worker                nodes.Const(singular),
378*635a8641SAndroid Build Coastguard Worker                nodes.Const(plural),
379*635a8641SAndroid Build Coastguard Worker                plural_expr
380*635a8641SAndroid Build Coastguard Worker            ], [], None, None)
381*635a8641SAndroid Build Coastguard Worker
382*635a8641SAndroid Build Coastguard Worker        # in case newstyle gettext is used, the method is powerful
383*635a8641SAndroid Build Coastguard Worker        # enough to handle the variable expansion and autoescape
384*635a8641SAndroid Build Coastguard Worker        # handling itself
385*635a8641SAndroid Build Coastguard Worker        if self.environment.newstyle_gettext:
386*635a8641SAndroid Build Coastguard Worker            for key, value in iteritems(variables):
387*635a8641SAndroid Build Coastguard Worker                # the function adds that later anyways in case num was
388*635a8641SAndroid Build Coastguard Worker                # called num, so just skip it.
389*635a8641SAndroid Build Coastguard Worker                if num_called_num and key == 'num':
390*635a8641SAndroid Build Coastguard Worker                    continue
391*635a8641SAndroid Build Coastguard Worker                node.kwargs.append(nodes.Keyword(key, value))
392*635a8641SAndroid Build Coastguard Worker
393*635a8641SAndroid Build Coastguard Worker        # otherwise do that here
394*635a8641SAndroid Build Coastguard Worker        else:
395*635a8641SAndroid Build Coastguard Worker            # mark the return value as safe if we are in an
396*635a8641SAndroid Build Coastguard Worker            # environment with autoescaping turned on
397*635a8641SAndroid Build Coastguard Worker            node = nodes.MarkSafeIfAutoescape(node)
398*635a8641SAndroid Build Coastguard Worker            if variables:
399*635a8641SAndroid Build Coastguard Worker                node = nodes.Mod(node, nodes.Dict([
400*635a8641SAndroid Build Coastguard Worker                    nodes.Pair(nodes.Const(key), value)
401*635a8641SAndroid Build Coastguard Worker                    for key, value in variables.items()
402*635a8641SAndroid Build Coastguard Worker                ]))
403*635a8641SAndroid Build Coastguard Worker        return nodes.Output([node])
404*635a8641SAndroid Build Coastguard Worker
405*635a8641SAndroid Build Coastguard Worker
406*635a8641SAndroid Build Coastguard Workerclass ExprStmtExtension(Extension):
407*635a8641SAndroid Build Coastguard Worker    """Adds a `do` tag to Jinja2 that works like the print statement just
408*635a8641SAndroid Build Coastguard Worker    that it doesn't print the return value.
409*635a8641SAndroid Build Coastguard Worker    """
410*635a8641SAndroid Build Coastguard Worker    tags = set(['do'])
411*635a8641SAndroid Build Coastguard Worker
412*635a8641SAndroid Build Coastguard Worker    def parse(self, parser):
413*635a8641SAndroid Build Coastguard Worker        node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
414*635a8641SAndroid Build Coastguard Worker        node.node = parser.parse_tuple()
415*635a8641SAndroid Build Coastguard Worker        return node
416*635a8641SAndroid Build Coastguard Worker
417*635a8641SAndroid Build Coastguard Worker
418*635a8641SAndroid Build Coastguard Workerclass LoopControlExtension(Extension):
419*635a8641SAndroid Build Coastguard Worker    """Adds break and continue to the template engine."""
420*635a8641SAndroid Build Coastguard Worker    tags = set(['break', 'continue'])
421*635a8641SAndroid Build Coastguard Worker
422*635a8641SAndroid Build Coastguard Worker    def parse(self, parser):
423*635a8641SAndroid Build Coastguard Worker        token = next(parser.stream)
424*635a8641SAndroid Build Coastguard Worker        if token.value == 'break':
425*635a8641SAndroid Build Coastguard Worker            return nodes.Break(lineno=token.lineno)
426*635a8641SAndroid Build Coastguard Worker        return nodes.Continue(lineno=token.lineno)
427*635a8641SAndroid Build Coastguard Worker
428*635a8641SAndroid Build Coastguard Worker
429*635a8641SAndroid Build Coastguard Workerclass WithExtension(Extension):
430*635a8641SAndroid Build Coastguard Worker    pass
431*635a8641SAndroid Build Coastguard Worker
432*635a8641SAndroid Build Coastguard Worker
433*635a8641SAndroid Build Coastguard Workerclass AutoEscapeExtension(Extension):
434*635a8641SAndroid Build Coastguard Worker    pass
435*635a8641SAndroid Build Coastguard Worker
436*635a8641SAndroid Build Coastguard Worker
437*635a8641SAndroid Build Coastguard Workerdef extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
438*635a8641SAndroid Build Coastguard Worker                     babel_style=True):
439*635a8641SAndroid Build Coastguard Worker    """Extract localizable strings from the given template node.  Per
440*635a8641SAndroid Build Coastguard Worker    default this function returns matches in babel style that means non string
441*635a8641SAndroid Build Coastguard Worker    parameters as well as keyword arguments are returned as `None`.  This
442*635a8641SAndroid Build Coastguard Worker    allows Babel to figure out what you really meant if you are using
443*635a8641SAndroid Build Coastguard Worker    gettext functions that allow keyword arguments for placeholder expansion.
444*635a8641SAndroid Build Coastguard Worker    If you don't want that behavior set the `babel_style` parameter to `False`
445*635a8641SAndroid Build Coastguard Worker    which causes only strings to be returned and parameters are always stored
446*635a8641SAndroid Build Coastguard Worker    in tuples.  As a consequence invalid gettext calls (calls without a single
447*635a8641SAndroid Build Coastguard Worker    string parameter or string parameters after non-string parameters) are
448*635a8641SAndroid Build Coastguard Worker    skipped.
449*635a8641SAndroid Build Coastguard Worker
450*635a8641SAndroid Build Coastguard Worker    This example explains the behavior:
451*635a8641SAndroid Build Coastguard Worker
452*635a8641SAndroid Build Coastguard Worker    >>> from jinja2 import Environment
453*635a8641SAndroid Build Coastguard Worker    >>> env = Environment()
454*635a8641SAndroid Build Coastguard Worker    >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
455*635a8641SAndroid Build Coastguard Worker    >>> list(extract_from_ast(node))
456*635a8641SAndroid Build Coastguard Worker    [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
457*635a8641SAndroid Build Coastguard Worker    >>> list(extract_from_ast(node, babel_style=False))
458*635a8641SAndroid Build Coastguard Worker    [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
459*635a8641SAndroid Build Coastguard Worker
460*635a8641SAndroid Build Coastguard Worker    For every string found this function yields a ``(lineno, function,
461*635a8641SAndroid Build Coastguard Worker    message)`` tuple, where:
462*635a8641SAndroid Build Coastguard Worker
463*635a8641SAndroid Build Coastguard Worker    * ``lineno`` is the number of the line on which the string was found,
464*635a8641SAndroid Build Coastguard Worker    * ``function`` is the name of the ``gettext`` function used (if the
465*635a8641SAndroid Build Coastguard Worker      string was extracted from embedded Python code), and
466*635a8641SAndroid Build Coastguard Worker    *  ``message`` is the string itself (a ``unicode`` object, or a tuple
467*635a8641SAndroid Build Coastguard Worker       of ``unicode`` objects for functions with multiple string arguments).
468*635a8641SAndroid Build Coastguard Worker
469*635a8641SAndroid Build Coastguard Worker    This extraction function operates on the AST and is because of that unable
470*635a8641SAndroid Build Coastguard Worker    to extract any comments.  For comment support you have to use the babel
471*635a8641SAndroid Build Coastguard Worker    extraction interface or extract comments yourself.
472*635a8641SAndroid Build Coastguard Worker    """
473*635a8641SAndroid Build Coastguard Worker    for node in node.find_all(nodes.Call):
474*635a8641SAndroid Build Coastguard Worker        if not isinstance(node.node, nodes.Name) or \
475*635a8641SAndroid Build Coastguard Worker           node.node.name not in gettext_functions:
476*635a8641SAndroid Build Coastguard Worker            continue
477*635a8641SAndroid Build Coastguard Worker
478*635a8641SAndroid Build Coastguard Worker        strings = []
479*635a8641SAndroid Build Coastguard Worker        for arg in node.args:
480*635a8641SAndroid Build Coastguard Worker            if isinstance(arg, nodes.Const) and \
481*635a8641SAndroid Build Coastguard Worker               isinstance(arg.value, string_types):
482*635a8641SAndroid Build Coastguard Worker                strings.append(arg.value)
483*635a8641SAndroid Build Coastguard Worker            else:
484*635a8641SAndroid Build Coastguard Worker                strings.append(None)
485*635a8641SAndroid Build Coastguard Worker
486*635a8641SAndroid Build Coastguard Worker        for arg in node.kwargs:
487*635a8641SAndroid Build Coastguard Worker            strings.append(None)
488*635a8641SAndroid Build Coastguard Worker        if node.dyn_args is not None:
489*635a8641SAndroid Build Coastguard Worker            strings.append(None)
490*635a8641SAndroid Build Coastguard Worker        if node.dyn_kwargs is not None:
491*635a8641SAndroid Build Coastguard Worker            strings.append(None)
492*635a8641SAndroid Build Coastguard Worker
493*635a8641SAndroid Build Coastguard Worker        if not babel_style:
494*635a8641SAndroid Build Coastguard Worker            strings = tuple(x for x in strings if x is not None)
495*635a8641SAndroid Build Coastguard Worker            if not strings:
496*635a8641SAndroid Build Coastguard Worker                continue
497*635a8641SAndroid Build Coastguard Worker        else:
498*635a8641SAndroid Build Coastguard Worker            if len(strings) == 1:
499*635a8641SAndroid Build Coastguard Worker                strings = strings[0]
500*635a8641SAndroid Build Coastguard Worker            else:
501*635a8641SAndroid Build Coastguard Worker                strings = tuple(strings)
502*635a8641SAndroid Build Coastguard Worker        yield node.lineno, node.node.name, strings
503*635a8641SAndroid Build Coastguard Worker
504*635a8641SAndroid Build Coastguard Worker
505*635a8641SAndroid Build Coastguard Workerclass _CommentFinder(object):
506*635a8641SAndroid Build Coastguard Worker    """Helper class to find comments in a token stream.  Can only
507*635a8641SAndroid Build Coastguard Worker    find comments for gettext calls forwards.  Once the comment
508*635a8641SAndroid Build Coastguard Worker    from line 4 is found, a comment for line 1 will not return a
509*635a8641SAndroid Build Coastguard Worker    usable value.
510*635a8641SAndroid Build Coastguard Worker    """
511*635a8641SAndroid Build Coastguard Worker
512*635a8641SAndroid Build Coastguard Worker    def __init__(self, tokens, comment_tags):
513*635a8641SAndroid Build Coastguard Worker        self.tokens = tokens
514*635a8641SAndroid Build Coastguard Worker        self.comment_tags = comment_tags
515*635a8641SAndroid Build Coastguard Worker        self.offset = 0
516*635a8641SAndroid Build Coastguard Worker        self.last_lineno = 0
517*635a8641SAndroid Build Coastguard Worker
518*635a8641SAndroid Build Coastguard Worker    def find_backwards(self, offset):
519*635a8641SAndroid Build Coastguard Worker        try:
520*635a8641SAndroid Build Coastguard Worker            for _, token_type, token_value in \
521*635a8641SAndroid Build Coastguard Worker                    reversed(self.tokens[self.offset:offset]):
522*635a8641SAndroid Build Coastguard Worker                if token_type in ('comment', 'linecomment'):
523*635a8641SAndroid Build Coastguard Worker                    try:
524*635a8641SAndroid Build Coastguard Worker                        prefix, comment = token_value.split(None, 1)
525*635a8641SAndroid Build Coastguard Worker                    except ValueError:
526*635a8641SAndroid Build Coastguard Worker                        continue
527*635a8641SAndroid Build Coastguard Worker                    if prefix in self.comment_tags:
528*635a8641SAndroid Build Coastguard Worker                        return [comment.rstrip()]
529*635a8641SAndroid Build Coastguard Worker            return []
530*635a8641SAndroid Build Coastguard Worker        finally:
531*635a8641SAndroid Build Coastguard Worker            self.offset = offset
532*635a8641SAndroid Build Coastguard Worker
533*635a8641SAndroid Build Coastguard Worker    def find_comments(self, lineno):
534*635a8641SAndroid Build Coastguard Worker        if not self.comment_tags or self.last_lineno > lineno:
535*635a8641SAndroid Build Coastguard Worker            return []
536*635a8641SAndroid Build Coastguard Worker        for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
537*635a8641SAndroid Build Coastguard Worker            if token_lineno > lineno:
538*635a8641SAndroid Build Coastguard Worker                return self.find_backwards(self.offset + idx)
539*635a8641SAndroid Build Coastguard Worker        return self.find_backwards(len(self.tokens))
540*635a8641SAndroid Build Coastguard Worker
541*635a8641SAndroid Build Coastguard Worker
542*635a8641SAndroid Build Coastguard Workerdef babel_extract(fileobj, keywords, comment_tags, options):
543*635a8641SAndroid Build Coastguard Worker    """Babel extraction method for Jinja templates.
544*635a8641SAndroid Build Coastguard Worker
545*635a8641SAndroid Build Coastguard Worker    .. versionchanged:: 2.3
546*635a8641SAndroid Build Coastguard Worker       Basic support for translation comments was added.  If `comment_tags`
547*635a8641SAndroid Build Coastguard Worker       is now set to a list of keywords for extraction, the extractor will
548*635a8641SAndroid Build Coastguard Worker       try to find the best preceeding comment that begins with one of the
549*635a8641SAndroid Build Coastguard Worker       keywords.  For best results, make sure to not have more than one
550*635a8641SAndroid Build Coastguard Worker       gettext call in one line of code and the matching comment in the
551*635a8641SAndroid Build Coastguard Worker       same line or the line before.
552*635a8641SAndroid Build Coastguard Worker
553*635a8641SAndroid Build Coastguard Worker    .. versionchanged:: 2.5.1
554*635a8641SAndroid Build Coastguard Worker       The `newstyle_gettext` flag can be set to `True` to enable newstyle
555*635a8641SAndroid Build Coastguard Worker       gettext calls.
556*635a8641SAndroid Build Coastguard Worker
557*635a8641SAndroid Build Coastguard Worker    .. versionchanged:: 2.7
558*635a8641SAndroid Build Coastguard Worker       A `silent` option can now be provided.  If set to `False` template
559*635a8641SAndroid Build Coastguard Worker       syntax errors are propagated instead of being ignored.
560*635a8641SAndroid Build Coastguard Worker
561*635a8641SAndroid Build Coastguard Worker    :param fileobj: the file-like object the messages should be extracted from
562*635a8641SAndroid Build Coastguard Worker    :param keywords: a list of keywords (i.e. function names) that should be
563*635a8641SAndroid Build Coastguard Worker                     recognized as translation functions
564*635a8641SAndroid Build Coastguard Worker    :param comment_tags: a list of translator tags to search for and include
565*635a8641SAndroid Build Coastguard Worker                         in the results.
566*635a8641SAndroid Build Coastguard Worker    :param options: a dictionary of additional options (optional)
567*635a8641SAndroid Build Coastguard Worker    :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
568*635a8641SAndroid Build Coastguard Worker             (comments will be empty currently)
569*635a8641SAndroid Build Coastguard Worker    """
570*635a8641SAndroid Build Coastguard Worker    extensions = set()
571*635a8641SAndroid Build Coastguard Worker    for extension in options.get('extensions', '').split(','):
572*635a8641SAndroid Build Coastguard Worker        extension = extension.strip()
573*635a8641SAndroid Build Coastguard Worker        if not extension:
574*635a8641SAndroid Build Coastguard Worker            continue
575*635a8641SAndroid Build Coastguard Worker        extensions.add(import_string(extension))
576*635a8641SAndroid Build Coastguard Worker    if InternationalizationExtension not in extensions:
577*635a8641SAndroid Build Coastguard Worker        extensions.add(InternationalizationExtension)
578*635a8641SAndroid Build Coastguard Worker
579*635a8641SAndroid Build Coastguard Worker    def getbool(options, key, default=False):
580*635a8641SAndroid Build Coastguard Worker        return options.get(key, str(default)).lower() in \
581*635a8641SAndroid Build Coastguard Worker            ('1', 'on', 'yes', 'true')
582*635a8641SAndroid Build Coastguard Worker
583*635a8641SAndroid Build Coastguard Worker    silent = getbool(options, 'silent', True)
584*635a8641SAndroid Build Coastguard Worker    environment = Environment(
585*635a8641SAndroid Build Coastguard Worker        options.get('block_start_string', BLOCK_START_STRING),
586*635a8641SAndroid Build Coastguard Worker        options.get('block_end_string', BLOCK_END_STRING),
587*635a8641SAndroid Build Coastguard Worker        options.get('variable_start_string', VARIABLE_START_STRING),
588*635a8641SAndroid Build Coastguard Worker        options.get('variable_end_string', VARIABLE_END_STRING),
589*635a8641SAndroid Build Coastguard Worker        options.get('comment_start_string', COMMENT_START_STRING),
590*635a8641SAndroid Build Coastguard Worker        options.get('comment_end_string', COMMENT_END_STRING),
591*635a8641SAndroid Build Coastguard Worker        options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
592*635a8641SAndroid Build Coastguard Worker        options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
593*635a8641SAndroid Build Coastguard Worker        getbool(options, 'trim_blocks', TRIM_BLOCKS),
594*635a8641SAndroid Build Coastguard Worker        getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS),
595*635a8641SAndroid Build Coastguard Worker        NEWLINE_SEQUENCE,
596*635a8641SAndroid Build Coastguard Worker        getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE),
597*635a8641SAndroid Build Coastguard Worker        frozenset(extensions),
598*635a8641SAndroid Build Coastguard Worker        cache_size=0,
599*635a8641SAndroid Build Coastguard Worker        auto_reload=False
600*635a8641SAndroid Build Coastguard Worker    )
601*635a8641SAndroid Build Coastguard Worker
602*635a8641SAndroid Build Coastguard Worker    if getbool(options, 'trimmed'):
603*635a8641SAndroid Build Coastguard Worker        environment.policies['ext.i18n.trimmed'] = True
604*635a8641SAndroid Build Coastguard Worker    if getbool(options, 'newstyle_gettext'):
605*635a8641SAndroid Build Coastguard Worker        environment.newstyle_gettext = True
606*635a8641SAndroid Build Coastguard Worker
607*635a8641SAndroid Build Coastguard Worker    source = fileobj.read().decode(options.get('encoding', 'utf-8'))
608*635a8641SAndroid Build Coastguard Worker    try:
609*635a8641SAndroid Build Coastguard Worker        node = environment.parse(source)
610*635a8641SAndroid Build Coastguard Worker        tokens = list(environment.lex(environment.preprocess(source)))
611*635a8641SAndroid Build Coastguard Worker    except TemplateSyntaxError as e:
612*635a8641SAndroid Build Coastguard Worker        if not silent:
613*635a8641SAndroid Build Coastguard Worker            raise
614*635a8641SAndroid Build Coastguard Worker        # skip templates with syntax errors
615*635a8641SAndroid Build Coastguard Worker        return
616*635a8641SAndroid Build Coastguard Worker
617*635a8641SAndroid Build Coastguard Worker    finder = _CommentFinder(tokens, comment_tags)
618*635a8641SAndroid Build Coastguard Worker    for lineno, func, message in extract_from_ast(node, keywords):
619*635a8641SAndroid Build Coastguard Worker        yield lineno, func, message, finder.find_comments(lineno)
620*635a8641SAndroid Build Coastguard Worker
621*635a8641SAndroid Build Coastguard Worker
622*635a8641SAndroid Build Coastguard Worker#: nicer import names
623*635a8641SAndroid Build Coastguard Workeri18n = InternationalizationExtension
624*635a8641SAndroid Build Coastguard Workerdo = ExprStmtExtension
625*635a8641SAndroid Build Coastguard Workerloopcontrols = LoopControlExtension
626*635a8641SAndroid Build Coastguard Workerwith_ = WithExtension
627*635a8641SAndroid Build Coastguard Workerautoescape = AutoEscapeExtension
628